From 280303d76c284d5e351d80d5234c9d062c3b75cf Mon Sep 17 00:00:00 2001 From: Stanislav Shwartsman Date: Sat, 25 Nov 2023 16:43:47 +0200 Subject: [PATCH] initial code for UINTR implementation (#138) First step into implementing UINTR - User Level Interrupts ISA extension To be continued --------- Co-authored-by: Stanislav Shwartsman --- bochs/.conf.everything | 1 + bochs/CHANGES | 2 + bochs/config.h.in | 5 + bochs/configure | 503 ++++++++------------------ bochs/configure.ac | 23 ++ bochs/cpu/Makefile.in | 10 +- bochs/cpu/apic.cc | 2 +- bochs/cpu/cpu.h | 55 ++- bochs/cpu/cpuid.cc | 6 + bochs/cpu/crregs.cc | 20 + bochs/cpu/decoder/features.h | 1 + bochs/cpu/decoder/fetchdecode_opmap.h | 10 + bochs/cpu/decoder/ia_opcodes.def | 8 + bochs/cpu/event.cc | 32 +- bochs/cpu/flag_ctrl_pro.cc | 18 +- bochs/cpu/init.cc | 16 + bochs/cpu/msr.cc | 120 ++++++ bochs/cpu/msr.h | 11 +- bochs/cpu/uintr.cc | 345 ++++++++++++++++++ bochs/cpu/vapic.cc | 19 +- bochs/cpu/vmcs.cc | 15 + bochs/cpu/vmx.cc | 40 +- bochs/cpu/vmx.h | 10 +- bochs/cpu/xsave.cc | 77 ++++ 24 files changed, 982 insertions(+), 367 deletions(-) create mode 100644 bochs/cpu/uintr.cc diff --git a/bochs/.conf.everything b/bochs/.conf.everything index 5500dd2296..7083b42446 100755 --- a/bochs/.conf.everything +++ b/bochs/.conf.everything @@ -20,6 +20,7 @@ --enable-avx \ --enable-cet \ --enable-evex \ + --enable-uintr \ --enable-perfmon \ --enable-memtype \ --enable-x86-debugger \ diff --git a/bochs/CHANGES b/bochs/CHANGES index 4366aebaf0..74324043b9 100644 --- a/bochs/CHANGES +++ b/bochs/CHANGES @@ -7,6 +7,7 @@ Brief summary : - Bugfixes for CPU emulation correctness (MONITOR/MWAIT, VMX/SVM, AVX-512, CET, SHA, GFNI fixes) ! Implemented VMX MBE (Mode Based Execution Control) emulation required for Windows 11 guest ! Implemented Linear Address Separation (LASS) extension + ! Implemented User-Level Interrupt (UINTR) extension ! Implemented recently published Intel instruction sets: - MOVDIRI, AVX512 BF16, AVX IFMA52, VNNI-INT8, VNNI-INT16, AVX-NE-CONVERT, CMPCCXADD, SM3, SM4, SHA512, WRMSRNS, SERIALIZE - Improved 64-bit guest support in Bochs internal debugger, added new internal debugger commands @@ -29,6 +30,7 @@ Detailed change log : - Critical CPU emulation bugfixes for SHA and GFNI instructions - Implemented VMX MBE (Mode Based Execution Control) emulation required for Windows 11 guest - Implemented Linear Address Separation (LASS) extension + - Implemented User-Level Interrupt (UINTR) extension - Implemented recently published Intel instruction sets: - MOVDIRI, AVX512 BF16, AVX IFMA52, VNNI-INT8, VNNI-INT16, AVX-NE-CONVERT, CMPCCXADD, SM3, SM4, SHA512, WRMSRNS, SERIALIZE diff --git a/bochs/config.h.in b/bochs/config.h.in index b7aa8a9be6..1f7af797e0 100644 --- a/bochs/config.h.in +++ b/bochs/config.h.in @@ -629,6 +629,7 @@ typedef Bit32u bx_phy_address; #define BX_SUPPORT_3DNOW 0 #define BX_SUPPORT_PKEYS 0 #define BX_SUPPORT_CET 0 +#define BX_SUPPORT_UINTR 0 #define BX_SUPPORT_MONITOR_MWAIT 0 #define BX_SUPPORT_PERFMON 0 #define BX_SUPPORT_MEMTYPE 0 @@ -637,6 +638,10 @@ typedef Bit32u bx_phy_address; #define BX_SUPPORT_AVX 0 #define BX_SUPPORT_EVEX 0 +#if BX_SUPPORT_UINTR && BX_SUPPORT_X86_64 == 0 + #error "UINTR require x86-64 support" +#endif + #if BX_SUPPORT_SVM && BX_SUPPORT_X86_64 == 0 #error "SVM require x86-64 support" #endif diff --git a/bochs/configure b/bochs/configure index 91a3b77b0b..7c0a03153e 100755 --- a/bochs/configure +++ b/bochs/configure @@ -1068,6 +1068,7 @@ enable_vmx enable_svm enable_protection_keys enable_cet +enable_uintr enable_3dnow enable_alignment_check enable_monitor_mwait @@ -1801,6 +1802,7 @@ Optional Features: --enable-protection-keys User-Mode Protection Keys support (no) --enable-cet Control Flow Enforcement Technology support (no) + --enable-uintr for User Level Interrupt support (no) --enable-3dnow 3DNow! support (no - incomplete) --enable-alignment-check alignment check (#AC) support (yes, if cpu level > @@ -2132,7 +2134,7 @@ else $as_nop #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (void); below. */ + which can conflict with char $2 (); below. */ #include #undef $2 @@ -2143,7 +2145,7 @@ else $as_nop #ifdef __cplusplus extern "C" #endif -char $2 (void); +char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ @@ -2279,7 +2281,7 @@ else $as_nop #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (void); below. */ + which can conflict with char $2 (); below. */ #include #undef $2 @@ -2290,7 +2292,7 @@ else $as_nop #ifdef __cplusplus extern "C" #endif -char $2 (void); +char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ @@ -3104,7 +3106,9 @@ struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (char **p, int i) +static char *e (p, i) + char **p; + int i; { return p[i]; } @@ -3155,7 +3159,6 @@ extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); -extern void free (void *); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare @@ -6144,7 +6147,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 6147 "configure"' > conftest.$ac_ext + echo '#line 6150 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -7641,11 +7644,11 @@ else $as_nop -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7644: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7647: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7648: \$? = $ac_status" >&5 + echo "$as_me:7651: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -7875,11 +7878,11 @@ else $as_nop -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7878: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7881: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7882: \$? = $ac_status" >&5 + echo "$as_me:7885: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -7943,11 +7946,11 @@ else $as_nop -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7946: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7949: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7950: \$? = $ac_status" >&5 + echo "$as_me:7953: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -9488,14 +9491,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (void); + builtin and then its argument prototype would still apply. */ +char dlopen (); int main (void) { @@ -9548,14 +9545,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char shl_load (void); + builtin and then its argument prototype would still apply. */ +char shl_load (); int main (void) { @@ -9598,14 +9589,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (void); + builtin and then its argument prototype would still apply. */ +char dlopen (); int main (void) { @@ -9643,14 +9628,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (void); + builtin and then its argument prototype would still apply. */ +char dlopen (); int main (void) { @@ -9688,14 +9667,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dld_link (void); + builtin and then its argument prototype would still apply. */ +char dld_link (); int main (void) { @@ -9768,7 +9741,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:11957: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:11988: \$? = $ac_status" >&5 + echo "$as_me:11961: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -12049,11 +12022,11 @@ else $as_nop -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:12052: $lt_compile\"" >&5) + (eval echo "\"\$as_me:12025: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:12056: \$? = $ac_status" >&5 + echo "$as_me:12029: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -13072,7 +13045,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:13963: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:13994: \$? = $ac_status" >&5 + echo "$as_me:13967: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -14055,11 +14028,11 @@ else $as_nop -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:14058: $lt_compile\"" >&5) + (eval echo "\"\$as_me:14031: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:14062: \$? = $ac_status" >&5 + echo "$as_me:14035: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -16023,11 +15996,11 @@ else $as_nop -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16026: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15999: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:16030: \$? = $ac_status" >&5 + echo "$as_me:16003: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -16257,11 +16230,11 @@ else $as_nop -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16260: $lt_compile\"" >&5) + (eval echo "\"\$as_me:16233: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:16264: \$? = $ac_status" >&5 + echo "$as_me:16237: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -16325,11 +16298,11 @@ else $as_nop -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16328: $lt_compile\"" >&5) + (eval echo "\"\$as_me:16301: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:16332: \$? = $ac_status" >&5 + echo "$as_me:16305: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -17870,14 +17843,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (void); + builtin and then its argument prototype would still apply. */ +char dlopen (); int main (void) { @@ -17930,14 +17897,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char shl_load (void); + builtin and then its argument prototype would still apply. */ +char shl_load (); int main (void) { @@ -17980,14 +17941,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (void); + builtin and then its argument prototype would still apply. */ +char dlopen (); int main (void) { @@ -18025,14 +17980,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (void); + builtin and then its argument prototype would still apply. */ +char dlopen (); int main (void) { @@ -18070,14 +18019,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dld_link (void); + builtin and then its argument prototype would still apply. */ +char dld_link (); int main (void) { @@ -18150,7 +18093,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char opendir (void); + builtin and then its argument prototype would still apply. */ +char opendir (); int main (void) { @@ -19526,14 +19463,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char opendir (void); + builtin and then its argument prototype would still apply. */ +char opendir (); int main (void) { @@ -19738,14 +19669,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char shl_load (void); + builtin and then its argument prototype would still apply. */ +char shl_load (); int main (void) { @@ -19786,14 +19711,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (void); + builtin and then its argument prototype would still apply. */ +char dlopen (); int main (void) { @@ -19854,14 +19773,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (void); + builtin and then its argument prototype would still apply. */ +char dlopen (); int main (void) { @@ -19902,14 +19815,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dld_link (void); + builtin and then its argument prototype would still apply. */ +char dld_link (); int main (void) { @@ -20051,7 +19958,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dnet_ntoa (void); + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); int main (void) { @@ -21046,14 +20941,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dnet_ntoa (void); + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); int main (void) { @@ -21113,14 +21002,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char gethostbyname (void); + builtin and then its argument prototype would still apply. */ +char gethostbyname (); int main (void) { @@ -21160,14 +21043,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char gethostbyname (void); + builtin and then its argument prototype would still apply. */ +char gethostbyname (); int main (void) { @@ -21223,14 +21100,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char connect (void); + builtin and then its argument prototype would still apply. */ +char connect (); int main (void) { @@ -21279,14 +21150,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char remove (void); + builtin and then its argument prototype would still apply. */ +char remove (); int main (void) { @@ -21335,14 +21200,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char shmat (void); + builtin and then its argument prototype would still apply. */ +char shmat (); int main (void) { @@ -21393,14 +21252,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char IceConnectionNumber (void); + builtin and then its argument prototype would still apply. */ +char IceConnectionNumber (); int main (void) { @@ -21589,23 +21442,22 @@ unsigned short int ascii_mm[] = int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } - int - main (int argc, char **argv) - { - /* Intimidate the compiler so that it does not - optimize the arrays away. */ - char *p = argv[0]; - ascii_mm[1] = *p++; ebcdic_mm[1] = *p++; - ascii_ii[1] = *p++; ebcdic_ii[1] = *p++; - return use_ascii (argc) == use_ebcdic (*p); - } + extern int foo; + +int +main (void) +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} _ACEOF -if ac_fn_c_try_link "$LINENO" +if ac_fn_c_try_compile "$LINENO" then : - if grep BIGenDianSyS conftest$ac_exeext >/dev/null; then + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi - if grep LiTTleEnDian conftest$ac_exeext >/dev/null ; then + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else @@ -21614,8 +21466,7 @@ then : fi fi fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -22930,14 +22781,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char lt_dlinit (void); + builtin and then its argument prototype would still apply. */ +char lt_dlinit (); int main (void) { @@ -23017,14 +22862,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (void); + builtin and then its argument prototype would still apply. */ +char dlopen (); int main (void) { @@ -24299,6 +24138,40 @@ if test "$support_cet" -gt 0 -a "$use_x86_64" = 0; then as_fn_error $? "Control Flow Enforcement Technology feature emulation require x86-64 enabled" "$LINENO" 5 fi +support_uintr=0 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for User Level Interrupt support" >&5 +printf %s "checking for User Level Interrupt support... " >&6; } +# Check whether --enable-uintr was given. +if test ${enable_uintr+y} +then : + enableval=$enable_uintr; if test "$enableval" = yes; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + printf "%s\n" "#define BX_SUPPORT_UINTR 1" >>confdefs.h + + support_uintr=1 + elif test "$enableval" = no; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + printf "%s\n" "#define BX_SUPPORT_UINTR 0" >>confdefs.h + + fi + +else $as_nop + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + printf "%s\n" "#define BX_SUPPORT_UINTR 0" >>confdefs.h + + + +fi + + +if test "$support_uintr" -gt 0 -a "$use_x86_64" = 0; then + as_fn_error $? "User Level Interrupt feature emulation require x86-64 enabled" "$LINENO" 5 +fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for 3DNow! support" >&5 printf %s "checking for 3DNow! support... " >&6; } # Check whether --enable-3dnow was given. @@ -26708,14 +26581,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char mvaddch (void); + builtin and then its argument prototype would still apply. */ +char mvaddch (); int main (void) { @@ -26754,14 +26621,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char mvaddch (void); + builtin and then its argument prototype would still apply. */ +char mvaddch (); int main (void) { @@ -26800,14 +26661,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char mvaddch (void); + builtin and then its argument prototype would still apply. */ +char mvaddch (); int main (void) { @@ -26846,14 +26701,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char mvaddch (void); + builtin and then its argument prototype would still apply. */ +char mvaddch (); int main (void) { @@ -26893,14 +26742,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char mvaddch (void); + builtin and then its argument prototype would still apply. */ +char mvaddch (); int main (void) { @@ -26939,14 +26782,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char mvaddch (void); + builtin and then its argument prototype would still apply. */ +char mvaddch (); int main (void) { @@ -26985,14 +26822,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char mvaddch (void); + builtin and then its argument prototype would still apply. */ +char mvaddch (); int main (void) { @@ -27031,14 +26862,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char mvaddch (void); + builtin and then its argument prototype would still apply. */ +char mvaddch (); int main (void) { @@ -27154,14 +26979,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char socket (void); + builtin and then its argument prototype would still apply. */ +char socket (); int main (void) { @@ -27242,14 +27061,8 @@ printf %s "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. - The 'extern "C"' is for builds by C++ compilers; - although this is not generally supported in C code supporting it here - has little cost and some practical benefit (sr 110532). */ -#ifdef __cplusplus -extern "C" -#endif -char pthread_join (void); + builtin and then its argument prototype would still apply. */ +char pthread_join (); int main (void) { diff --git a/bochs/configure.ac b/bochs/configure.ac index 3ac64f226f..1413c3ea37 100644 --- a/bochs/configure.ac +++ b/bochs/configure.ac @@ -1252,6 +1252,29 @@ if test "$support_cet" -gt 0 -a "$use_x86_64" = 0; then AC_MSG_ERROR([Control Flow Enforcement Technology feature emulation require x86-64 enabled]) fi +support_uintr=0 +AC_MSG_CHECKING(for User Level Interrupt support) +AC_ARG_ENABLE(uintr, + AS_HELP_STRING([--enable-uintr], [for User Level Interrupt support (no)]), + [if test "$enableval" = yes; then + AC_MSG_RESULT(yes) + AC_DEFINE(BX_SUPPORT_UINTR, 1) + support_uintr=1 + elif test "$enableval" = no; then + AC_MSG_RESULT(no) + AC_DEFINE(BX_SUPPORT_UINTR, 0) + fi + ], + [ + AC_MSG_RESULT(no) + AC_DEFINE(BX_SUPPORT_UINTR, 0) + ] + ) + +if test "$support_uintr" -gt 0 -a "$use_x86_64" = 0; then + AC_MSG_ERROR([User Level Interrupt feature emulation require x86-64 enabled]) +fi + AC_MSG_CHECKING(for 3DNow! support) AC_ARG_ENABLE(3dnow, AS_HELP_STRING([--enable-3dnow], [3DNow! support (no - incomplete)]), diff --git a/bochs/cpu/Makefile.in b/bochs/cpu/Makefile.in index c1c0008491..d851fe8050 100644 --- a/bochs/cpu/Makefile.in +++ b/bochs/cpu/Makefile.in @@ -141,7 +141,8 @@ OBJS64 = \ stack64.o \ bmi64.o \ cmpccxadd64.o \ - vapic.o + vapic.o \ + uintr.o \ BX_INCLUDES = ../bochs.h ../config.h @@ -798,6 +799,13 @@ tasking.o: tasking.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../logio.h \ fpu/status_w.h fpu/control_w.h crregs.h descriptor.h decoder/instr.h \ lazy_flags.h tlb.h icache.h apic.h xmm.h vmx.h svm.h cpuid.h stack.h \ access.h +uintr.o: uintr.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../logio.h \ + ../misc/bswap.h cpu.h ../bx_debug/debug.h ../config.h ../osdep.h \ + ../cpu/decoder/decoder.h ../cpu/decoder/features.h decoder/decoder.h \ + ../instrument/stubs/instrument.h i387.h fpu/softfloat.h fpu/tag_w.h \ + fpu/status_w.h fpu/control_w.h crregs.h descriptor.h decoder/instr.h \ + lazy_flags.h tlb.h icache.h apic.h xmm.h vmx.h svm.h cpuid.h stack.h \ + access.h scalar_arith.h vapic.o: vapic.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../logio.h \ ../misc/bswap.h cpu.h ../bx_debug/debug.h ../config.h ../osdep.h \ ../cpu/decoder/decoder.h ../cpu/decoder/features.h decoder/decoder.h \ diff --git a/bochs/cpu/apic.cc b/bochs/cpu/apic.cc index f57c342407..cc67d9c0bf 100644 --- a/bochs/cpu/apic.cc +++ b/bochs/cpu/apic.cc @@ -787,7 +787,7 @@ int bx_local_apic_c::highest_priority_int(Bit32u *array) // ignore of interrupt vectors < 16 happen naturally as there is no way to ISR to it // if (reg == 0) tmp &= 0xffff0000; if (tmp) { - int vector = (reg * 32) + (31 - lzcntd(tmp)); + int vector = (reg * 32) + most_significant_bitd(tmp); return vector; } } diff --git a/bochs/cpu/cpu.h b/bochs/cpu/cpu.h index bfebd2bc0e..59c93ea3ee 100644 --- a/bochs/cpu/cpu.h +++ b/bochs/cpu/cpu.h @@ -259,7 +259,8 @@ enum BX_Instr_Branch { BX_INSTR_IS_SYSCALL = 17, BX_INSTR_IS_SYSRET = 18, BX_INSTR_IS_SYSENTER = 19, - BX_INSTR_IS_SYSEXIT = 20 + BX_INSTR_IS_SYSEXIT = 20, + BX_INSTR_IS_UIRET = 21 }; // possible types passed to BX_INSTR_PREFETCH_HINT() @@ -760,7 +761,6 @@ typedef struct { }; } bx_gen_reg_t; #endif - #endif // #if BX_SUPPORT_X86_64 #if BX_SUPPORT_APIC @@ -953,6 +953,21 @@ class BOCHSAPI BX_CPU_C : public logfunctions { Bit32u wr_pkey[16]; #endif +#if BX_SUPPORT_UINTR && BX_SUPPORT_X86_64 + struct { + bx_address ui_handler; + Bit64u stack_adjust; + Bit32u uinv; // user interrupt notification vector, actually 8 bit + Bit32u uitt_size; // user interrupt target table size + bx_address uitt_addr; // user interrupt target table address + bx_address upid_addr; // user posted-interrupt descriptor address + Bit64u uirr; // user-interrupt request register + bool UIF; // if UIF=0 user interrupt cannot be delivered + + bool senduipi_enabled() const { return uitt_addr & 0x1; } + } uintr; +#endif + #if BX_SUPPORT_FPU i387_t the_i387; #endif @@ -1072,9 +1087,10 @@ class BOCHSAPI BX_CPU_C : public logfunctions { #define BX_EVENT_PENDING_VMX_VIRTUAL_INTR (1 << 9) #define BX_EVENT_PENDING_INTR (1 << 10) #define BX_EVENT_PENDING_LAPIC_INTR (1 << 11) -#define BX_EVENT_VMX_VTPR_UPDATE (1 << 12) -#define BX_EVENT_VMX_VEOI_UPDATE (1 << 13) -#define BX_EVENT_VMX_VIRTUAL_APIC_WRITE (1 << 14) +#define BX_EVENT_PENDING_UINTR (1 << 12) +#define BX_EVENT_VMX_VTPR_UPDATE (1 << 13) +#define BX_EVENT_VMX_VEOI_UPDATE (1 << 14) +#define BX_EVENT_VMX_VIRTUAL_APIC_WRITE (1 << 15) Bit32u pending_event; Bit32u event_mask; Bit32u async_event; // keep 32-bit because of BX_ASYNC_EVENT_STOP_TRACE @@ -4050,6 +4066,14 @@ class BOCHSAPI BX_CPU_C : public logfunctions { BX_SMF void WRPKRU(bxInstruction_c *) BX_CPP_AttrRegparmN(1); #endif +#if BX_SUPPORT_UINTR && BX_SUPPORT_X86_64 + BX_SMF void STUI(bxInstruction_c *) BX_CPP_AttrRegparmN(1); + BX_SMF void CLUI(bxInstruction_c *) BX_CPP_AttrRegparmN(1); + BX_SMF void TESTUI(bxInstruction_c *) BX_CPP_AttrRegparmN(1); + BX_SMF void UIRET(bxInstruction_c *) BX_CPP_AttrRegparmN(1); + BX_SMF void SENDUIPI_Gq(bxInstruction_c *) BX_CPP_AttrRegparmN(1); +#endif + BX_SMF void RDPID_Ed(bxInstruction_c *) BX_CPP_AttrRegparmN(1); BX_SMF void UndefinedOpcode(bxInstruction_c *) BX_CPP_AttrRegparmN(1); @@ -4426,6 +4450,12 @@ class BOCHSAPI BX_CPU_C : public logfunctions { BX_SMF void handleSseModeChange(void); BX_SMF void handleAvxModeChange(void); #endif +#if BX_SUPPORT_UINTR && BX_SUPPORT_X86_64 + BX_SMF void send_uipi(Bit32u notification_destination, Bit32u notification_vector); + BX_SMF void uintr_uirr_update(); + BX_SMF void uintr_control(); + BX_SMF bool uintr_masked(); +#endif #if BX_SUPPORT_AVX BX_SMF void avx_masked_load8(bxInstruction_c *i, bx_address eaddr, BxPackedAvxRegister *dst, Bit64u mask); @@ -4559,6 +4589,10 @@ class BOCHSAPI BX_CPU_C : public logfunctions { BX_SMF void deliver_NMI(void); BX_SMF void deliver_SMI(void); BX_SMF void deliver_SIPI(unsigned vector); +#if BX_SUPPORT_UINTR && BX_SUPPORT_X86_64 + BX_SMF void deliver_UINTR(); + BX_SMF void process_uintr_notification(); +#endif BX_SMF void debug(bx_address offset); BX_SMF void debug_disasm_instruction(bx_address offset); @@ -4771,6 +4805,13 @@ class BOCHSAPI BX_CPU_C : public logfunctions { BX_SMF void xrstor_cet_s_state(bxInstruction_c *i, bx_address offset); BX_SMF void xrstor_init_cet_s_state(void); #endif + +#if BX_SUPPORT_UINTR && BX_SUPPORT_X86_64 + BX_SMF bool xsave_uintr_state_xinuse(void); + BX_SMF void xsave_uintr_state(bxInstruction_c *i, bx_address offset); + BX_SMF void xrstor_uintr_state(bxInstruction_c *i, bx_address offset); + BX_SMF void xrstor_init_uintr_state(void); +#endif #endif #if BX_SUPPORT_CET @@ -5049,6 +5090,10 @@ BX_CPP_INLINE void BX_CPU_C::updateFetchModeMask(void) BX_CPU_THIS_PTR user_pl = // CPL == 3 (BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.rpl == 3); + +#if BX_SUPPORT_UINTR + uintr_control(); // CPL changes +#endif } #if BX_X86_DEBUGGER diff --git a/bochs/cpu/cpuid.cc b/bochs/cpu/cpuid.cc index 498e5e8e79..b6cbe6fc35 100644 --- a/bochs/cpu/cpuid.cc +++ b/bochs/cpu/cpuid.cc @@ -1062,7 +1062,13 @@ Bit32u bx_cpuid_t::get_std_cpuid_leaf_7_edx(Bit32u extra) const // [2:2] AVX512 4VNNIW instructions support - not supported // [3:3] AVX512 4FMAPS instructions support - not supported // [4:4] Support of Fast REP MOV instructions with short length - not supported, might be enabled through extra + // [5:5] UINTR: User interrupts support - not yet supported +#if BX_SUPPORT_UINTR + if (is_cpu_extension_supported(BX_ISA_UINTR)) + edx |= BX_CPUID_STD7_SUBLEAF0_EDX_UINTR; +#endif + // [7:6] reserved // [8:8] AVX512 VP2INTERSECT instructions support diff --git a/bochs/cpu/crregs.cc b/bochs/cpu/crregs.cc index 44070ef3bd..fb24db78e0 100644 --- a/bochs/cpu/crregs.cc +++ b/bochs/cpu/crregs.cc @@ -1266,6 +1266,11 @@ Bit32u BX_CPU_C::get_cr4_allow_mask(void) allowMask |= BX_CR4_PKS_MASK; #endif +#if BX_SUPPORT_UINTR + if (is_cpu_extension_supported(BX_ISA_UINTR)) + allowMask |= BX_CR4_UINTR_MASK; +#endif + if (is_cpu_extension_supported(BX_ISA_LASS)) allowMask |= BX_CR4_LASS_MASK; #endif @@ -1772,6 +1777,18 @@ void BX_CPU_C::xsave_xrestor_init(void) xsave_restore[xcr0_t::BX_XCR0_CET_S_BIT].xrstor_init_method = &BX_CPU_C::xrstor_init_cet_s_state; } #endif + +#if BX_SUPPORT_UINTR + if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_UINTR)) { + // XCR0[14]: UINTR State + xsave_restore[xcr0_t::BX_XCR0_UINTR_BIT].len = XSAVE_UINTR_STATE_LEN; + xsave_restore[xcr0_t::BX_XCR0_UINTR_BIT].offset = 0; // IA32_XSS only + xsave_restore[xcr0_t::BX_XCR0_UINTR_BIT].xstate_in_use_method = &BX_CPU_C::xsave_uintr_state_xinuse; + xsave_restore[xcr0_t::BX_XCR0_UINTR_BIT].xsave_method = &BX_CPU_C::xsave_uintr_state; + xsave_restore[xcr0_t::BX_XCR0_UINTR_BIT].xrstor_method = &BX_CPU_C::xrstor_uintr_state; + xsave_restore[xcr0_t::BX_XCR0_UINTR_BIT].xrstor_init_method = &BX_CPU_C::xrstor_init_uintr_state; + } +#endif } #if BX_CPU_LEVEL >= 5 @@ -1827,6 +1844,9 @@ Bit32u BX_CPU_C::get_ia32_xss_allow_mask(void) Bit32u ia32_xss_support_mask = 0; #if BX_SUPPORT_CET ia32_xss_support_mask |= BX_XCR0_CET_U_MASK | BX_XCR0_CET_S_MASK; +#endif +#if BX_SUPPORT_UINTR + ia32_xss_support_mask |= BX_XCR0_UINTR_MASK; #endif return ia32_xss_support_mask; } diff --git a/bochs/cpu/decoder/features.h b/bochs/cpu/decoder/features.h index 3b17d4bd9c..584daa5ae3 100644 --- a/bochs/cpu/decoder/features.h +++ b/bochs/cpu/decoder/features.h @@ -140,4 +140,5 @@ x86_feature(BX_ISA_WRMSRNS, "wrmsrns") /* Non-S x86_feature(BX_ISA_CMPCCXADD, "cmpccxadd") /* CMPccXADD instructions */ x86_feature(BX_ISA_SERIALIZE, "serialize") /* SERIALIZE instruction */ x86_feature(BX_ISA_LASS, "lass") /* Linear Address Space Separation support */ +x86_feature(BX_ISA_UINTR, "uintr") /* User Level Interrupts support */ x86_feature(BX_ISA_MOVDIRI, "movdiri") /* MOVDIRI instruction support */ diff --git a/bochs/cpu/decoder/fetchdecode_opmap.h b/bochs/cpu/decoder/fetchdecode_opmap.h index 9ca454d925..29b184e513 100644 --- a/bochs/cpu/decoder/fetchdecode_opmap.h +++ b/bochs/cpu/decoder/fetchdecode_opmap.h @@ -1553,6 +1553,13 @@ static const Bit64u BxOpcodeTable0F01[] = { form_opcode(ATTR_NNN5 | ATTR_RRR7 | ATTR_MODC0 | ATTR_SSE_NO_PREFIX, BX_IA_WRPKRU), #endif +#if BX_SUPPORT_UINTR && BX_SUPPORT_X86_64 + form_opcode(ATTR_IS64 | ATTR_NNN5 | ATTR_RRR4 | ATTR_MODC0 | ATTR_SSE_PREFIX_F3, BX_IA_UIRET), + form_opcode(ATTR_IS64 | ATTR_NNN5 | ATTR_RRR5 | ATTR_MODC0 | ATTR_SSE_PREFIX_F3, BX_IA_TESTUI), + form_opcode(ATTR_IS64 | ATTR_NNN5 | ATTR_RRR6 | ATTR_MODC0 | ATTR_SSE_PREFIX_F3, BX_IA_CLUI), + form_opcode(ATTR_IS64 | ATTR_NNN5 | ATTR_RRR7 | ATTR_MODC0 | ATTR_SSE_PREFIX_F3, BX_IA_STUI), +#endif + #if BX_SUPPORT_X86_64 form_opcode(ATTR_NNN7 | ATTR_RRR0 | ATTR_MODC0 | ATTR_IS64, BX_IA_SWAPGS), #endif @@ -2835,6 +2842,9 @@ static const Bit64u BxOpcodeTable0FC7[] = { #if BX_SUPPORT_X86_64 form_opcode(ATTR_OS64 | ATTR_MODC0 | ATTR_NNN6 | ATTR_NO_SSE_PREFIX_F2_F3, BX_IA_RDRAND_Eq), form_opcode(ATTR_OS64 | ATTR_MODC0 | ATTR_NNN7 | ATTR_NO_SSE_PREFIX_F2_F3, BX_IA_RDSEED_Eq), +#endif +#if BX_SUPPORT_X86_64 && BX_SUPPORT_UINTR + form_opcode(ATTR_IS64 | ATTR_MODC0 | ATTR_NNN6 | ATTR_SSE_PREFIX_F3, BX_IA_SENDUIPI_Gq), #endif form_opcode(ATTR_NNN7 | ATTR_MODC0 | ATTR_SSE_PREFIX_F3, BX_IA_RDPID_Ed), form_opcode_lockable(ATTR_OS16_32 | ATTR_NNN1 | ATTR_MOD_MEM, BX_IA_CMPXCHG8B), diff --git a/bochs/cpu/decoder/ia_opcodes.def b/bochs/cpu/decoder/ia_opcodes.def index b8bd5bda85..e161f75619 100644 --- a/bochs/cpu/decoder/ia_opcodes.def +++ b/bochs/cpu/decoder/ia_opcodes.def @@ -1783,6 +1783,14 @@ bx_define_opcode(BX_IA_RDPKRU, "rdpkru", "rdpkru", &BX_CPU_C::BxError, &BX_CPU_C bx_define_opcode(BX_IA_WRPKRU, "wrpkru", "wrpkru", &BX_CPU_C::BxError, &BX_CPU_C::WRPKRU, BX_ISA_PKU, OP_NONE, OP_NONE, OP_NONE, OP_NONE, BX_TRACE_END) #endif +#if BX_SUPPORT_UINTR && BX_SUPPORT_X86_64 +bx_define_opcode(BX_IA_CLUI, "clui", "clui", NULL, &BX_CPU_C::CLUI, BX_ISA_UINTR, OP_NONE, OP_NONE, OP_NONE, OP_NONE, 0) +bx_define_opcode(BX_IA_STUI, "stui", "stui", NULL, &BX_CPU_C::STUI, BX_ISA_UINTR, OP_NONE, OP_NONE, OP_NONE, OP_NONE, BX_TRACE_END) +bx_define_opcode(BX_IA_TESTUI, "testui", "testui", NULL, &BX_CPU_C::TESTUI, BX_ISA_UINTR, OP_NONE, OP_NONE, OP_NONE, OP_NONE, 0) +bx_define_opcode(BX_IA_UIRET, "uiret", "uiret", NULL, &BX_CPU_C::UIRET, BX_ISA_UINTR, OP_NONE, OP_NONE, OP_NONE, OP_NONE, BX_TRACE_END) +bx_define_opcode(BX_IA_SENDUIPI_Gq, "senduipi", "senduipi", NULL, &BX_CPU_C::SENDUIPI_Gq, BX_ISA_UINTR, OP_Gq, OP_NONE, OP_NONE, OP_NONE, BX_TRACE_END) +#endif + bx_define_opcode(BX_IA_RDPID_Ed, "rdpid", "rdpid", NULL, &BX_CPU_C::RDPID_Ed, BX_ISA_RDPID, OP_Ed, OP_NONE, OP_NONE, OP_NONE, 0) bx_define_opcode(BX_IA_WRMSRNS, "wrmsrns", "wrmsrns", NULL, &BX_CPU_C::WRMSR, BX_ISA_WRMSRNS, OP_NONE, OP_NONE, OP_NONE, OP_NONE, BX_TRACE_END) diff --git a/bochs/cpu/event.cc b/bochs/cpu/event.cc index e5203abb7d..18cc1c3026 100644 --- a/bochs/cpu/event.cc +++ b/bochs/cpu/event.cc @@ -41,7 +41,7 @@ bool BX_CPU_C::handleWaitForEvent(void) // an interrupt wakes up the CPU. while (1) { - if ((is_pending(BX_EVENT_PENDING_INTR | BX_EVENT_PENDING_LAPIC_INTR) && (BX_CPU_THIS_PTR get_IF() || BX_CPU_THIS_PTR activity_state == BX_ACTIVITY_STATE_MWAIT_IF)) || + if ((is_pending(BX_EVENT_PENDING_INTR | BX_EVENT_PENDING_LAPIC_INTR | BX_EVENT_PENDING_UINTR) && (BX_CPU_THIS_PTR get_IF() || BX_CPU_THIS_PTR activity_state == BX_ACTIVITY_STATE_MWAIT_IF)) || is_unmasked_event_pending(BX_EVENT_NMI | BX_EVENT_SMI | BX_EVENT_INIT | BX_EVENT_VMX_VTPR_UPDATE | BX_EVENT_VMX_VEOI_UPDATE | @@ -143,9 +143,21 @@ void BX_CPU_C::InterruptAcknowledge(void) VMexit_Event(BX_EXTERNAL_INTERRUPT, vector, 0, 0); #endif - BX_INSTR_HWINTERRUPT(BX_CPU_ID, vector, +#if BX_SUPPORT_UINTR + if (BX_CPU_THIS_PTR cr4.get_UINTR() && long64_mode() && vector == BX_CPU_THIS_PTR uintr.uinv) + { +#if BX_SUPPORT_APIC + BX_CPU_THIS_PTR lapic.receive_EOI(0); +#endif + process_uintr_notification(); + } + else +#endif + { + BX_INSTR_HWINTERRUPT(BX_CPU_ID, vector, BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value, RIP); - interrupt(vector, BX_EXTERNAL_INTERRUPT, 0, 0); + interrupt(vector, BX_EXTERNAL_INTERRUPT, 0, 0); + } BX_CPU_THIS_PTR prev_rip = RIP; // commit new RIP } @@ -292,6 +304,14 @@ bool BX_CPU_C::handleAsyncEvent(void) if (interrupts_inhibited(BX_INHIBIT_INTERRUPTS) || ! SVM_GIF) { // Processing external interrupts is inhibited on this // boundary because of certain instructions like STI. +#if BX_SUPPORT_UINTR + // execution of STI doesn't block User interrupts delivery, only MOV_SS does + if (! interrupts_inhibited(BX_INHIBIT_INTERRUPTS_BY_MOVSS)) { + if (is_unmasked_event_pending(BX_EVENT_PENDING_UINTR)) { + deliver_UINTR(); + } + } +#endif } #if BX_SUPPORT_VMX >= 2 else if (is_unmasked_event_pending(BX_EVENT_VMX_PREEMPTION_TIMER_EXPIRED)) { @@ -329,6 +349,12 @@ bool BX_CPU_C::handleAsyncEvent(void) { InterruptAcknowledge(); } +#if BX_SUPPORT_UINTR + else if (is_unmasked_event_pending(BX_EVENT_PENDING_UINTR)) + { + deliver_UINTR(); + } +#endif #if BX_SUPPORT_SVM else if (is_unmasked_event_pending(BX_EVENT_SVM_VIRQ_PENDING)) { diff --git a/bochs/cpu/flag_ctrl_pro.cc b/bochs/cpu/flag_ctrl_pro.cc index 61cdfe3300..54fb8a3c57 100644 --- a/bochs/cpu/flag_ctrl_pro.cc +++ b/bochs/cpu/flag_ctrl_pro.cc @@ -129,6 +129,10 @@ void BX_CPU_C::handleInterruptMaskChange(void) } #endif +#if BX_SUPPORT_UINTR + if (!uintr_masked()) unmask_event(BX_EVENT_PENDING_UINTR); +#endif + return; } @@ -136,10 +140,13 @@ void BX_CPU_C::handleInterruptMaskChange(void) #if BX_SUPPORT_VMX if (BX_CPU_THIS_PTR in_vmx_guest && PIN_VMEXIT(VMX_PIN_BASED_VMEXEC_CTRL_EXTERNAL_INTERRUPT_VMEXIT)) { - // if 'External-interrupt exiting' control is set, the value of EFLAGS.IF - // doesn't affect interrupt blocking - mask_event(BX_EVENT_VMX_INTERRUPT_WINDOW_EXITING | BX_EVENT_PENDING_VMX_VIRTUAL_INTR); - unmask_event(BX_EVENT_PENDING_INTR | BX_EVENT_PENDING_LAPIC_INTR); + // if 'External-interrupt exiting' control is set, the value of EFLAGS.IF + // doesn't affect interrupt blocking + mask_event(BX_EVENT_VMX_INTERRUPT_WINDOW_EXITING | BX_EVENT_PENDING_VMX_VIRTUAL_INTR); + unmask_event(BX_EVENT_PENDING_INTR | BX_EVENT_PENDING_LAPIC_INTR); +#if BX_SUPPORT_UINTR + if (!uintr_masked()) unmask_event(BX_EVENT_PENDING_UINTR); +#endif return; } #endif @@ -147,7 +154,7 @@ void BX_CPU_C::handleInterruptMaskChange(void) #if BX_SUPPORT_SVM if (BX_CPU_THIS_PTR in_svm_guest && SVM_V_INTR_MASKING) { if (! SVM_HOST_IF) - mask_event(BX_EVENT_PENDING_INTR | BX_EVENT_PENDING_LAPIC_INTR); + mask_event(BX_EVENT_PENDING_INTR | BX_EVENT_PENDING_LAPIC_INTR | BX_EVENT_PENDING_UINTR); mask_event(BX_EVENT_SVM_VIRQ_PENDING); } @@ -157,6 +164,7 @@ void BX_CPU_C::handleInterruptMaskChange(void) mask_event(BX_EVENT_VMX_INTERRUPT_WINDOW_EXITING | BX_EVENT_PENDING_INTR | BX_EVENT_PENDING_LAPIC_INTR | + BX_EVENT_PENDING_UINTR | BX_EVENT_PENDING_VMX_VIRTUAL_INTR | BX_EVENT_SVM_VIRQ_PENDING); } diff --git a/bochs/cpu/init.cc b/bochs/cpu/init.cc index 56aa7eb570..1bcba615d7 100644 --- a/bochs/cpu/init.cc +++ b/bochs/cpu/init.cc @@ -444,6 +444,18 @@ void BX_CPU_C::register_state(void) #endif #endif +#if BX_SUPPORT_UINTR + bx_list_c *UINTR = new bx_list_c(cpu, "UINTR"); + BXRS_PARAM_BOOL(UINTR, UIF, uintr.UIF); + BXRS_HEX_PARAM_FIELD(UINTR, uirr, uintr.uirr); + BXRS_HEX_PARAM_FIELD(UINTR, ui_handler, uintr.ui_handler); + BXRS_HEX_PARAM_FIELD(UINTR, stack_adjust, uintr.stack_adjust); + BXRS_HEX_PARAM_FIELD(UINTR, uinv, uintr.uinv); + BXRS_HEX_PARAM_FIELD(UINTR, uitt_size, uintr.uitt_size); + BXRS_HEX_PARAM_FIELD(UINTR, uitt_addr, uintr.uitt_addr); + BXRS_HEX_PARAM_FIELD(UINTR, upid_addr, uintr.upid_addr); +#endif + #if BX_SUPPORT_FPU bx_list_c *fpu = new bx_list_c(cpu, "FPU"); BXRS_HEX_PARAM_FIELD(fpu, cwd, the_i387.cwd); @@ -895,6 +907,10 @@ void BX_CPU_C::reset(unsigned source) #endif #endif // BX_CPU_LEVEL >= 6 +#if BX_SUPPORT_UINTR + memset(&BX_CPU_THIS_PTR uintr, 0, sizeof(BX_CPU_THIS_PTR uintr)); +#endif + #if BX_CPU_LEVEL >= 5 BX_CPU_THIS_PTR msr.ia32_spec_ctrl = 0; diff --git a/bochs/cpu/msr.cc b/bochs/cpu/msr.cc index 4ea9f607bb..9dfdec9572 100644 --- a/bochs/cpu/msr.cc +++ b/bochs/cpu/msr.cc @@ -209,6 +209,51 @@ bool BX_CPP_AttrRegparmN(2) BX_CPU_C::rdmsr(Bit32u index, Bit64u *msr) break; #endif +#if BX_SUPPORT_UINTR + case BX_MSR_IA32_UINTR_RR: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("RDMSR BX_MSR_IA32_UINTR_RR: UINTR not enabled in the cpu model")); + return handle_unknown_rdmsr(index, msr); + } + val64 = BX_CPU_THIS_PTR uintr.uirr; + break; + case BX_MSR_IA32_UINTR_HANDLER: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("RDMSR BX_MSR_IA32_UINTR_HANDLER: UINTR not enabled in the cpu model")); + return handle_unknown_rdmsr(index, msr); + } + val64 = BX_CPU_THIS_PTR uintr.ui_handler; + break; + case BX_MSR_IA32_UINTR_STACKADJUST: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("RDMSR BX_MSR_IA32_UINTR_STACKADJUST: UINTR not enabled in the cpu model")); + return handle_unknown_rdmsr(index, msr); + } + val64 = BX_CPU_THIS_PTR uintr.stack_adjust; + break; + case BX_MSR_IA32_UINTR_MISC: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("RDMSR BX_MSR_IA32_UINTR_MISC: UINTR not enabled in the cpu model")); + return handle_unknown_rdmsr(index, msr); + } + val64 = GET64_FROM_HI32_LO32(BX_CPU_THIS_PTR uintr.uinv, BX_CPU_THIS_PTR uintr.uitt_size); + break; + case BX_MSR_IA32_UINTR_PD: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("RDMSR BX_MSR_IA32_UINTR_PD: UINTR not enabled in the cpu model")); + return handle_unknown_rdmsr(index, msr); + } + val64 = BX_CPU_THIS_PTR uintr.upid_addr; + break; + case BX_MSR_IA32_UINTR_TT: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("RDMSR BX_MSR_IA32_UINTR_TT: UINTR not enabled in the cpu model")); + return handle_unknown_rdmsr(index, msr); + } + val64 = BX_CPU_THIS_PTR uintr.uitt_addr; + break; +#endif + #if BX_SUPPORT_PKEYS case BX_MSR_IA32_PKRS: if (! is_cpu_extension_supported(BX_ISA_PKS)) { @@ -849,6 +894,81 @@ bool BX_CPP_AttrRegparmN(2) BX_CPU_C::wrmsr(Bit32u index, Bit64u val_64) break; #endif +#if BX_SUPPORT_UINTR + case BX_MSR_IA32_UINTR_RR: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("WRMSR BX_MSR_IA32_UINTR_RR: UINTR not enabled in the cpu model")); + return handle_unknown_wrmsr(index, val_64); + } + BX_CPU_THIS_PTR uintr.uirr = val_64; + uintr_uirr_update(); // potentially signal or clear user-level-interrupt + break; + case BX_MSR_IA32_UINTR_HANDLER: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("WRMSR BX_MSR_IA32_UINTR_HANDLER: UINTR not enabled in the cpu model")); + return handle_unknown_wrmsr(index, val_64); + } + if (! IsCanonical(val_64)) { + BX_ERROR(("WRMSR: attempt to write non-canonical value to BX_MSR_IA32_UINTR_HANDLER !")); + return 0; + } + BX_CPU_THIS_PTR uintr.ui_handler = val_64; + break; + case BX_MSR_IA32_UINTR_STACKADJUST: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("WRMSR BX_MSR_IA32_UINTR_STACKADJUST: UINTR not enabled in the cpu model")); + return handle_unknown_wrmsr(index, val_64); + } + if (! IsCanonical(val_64)) { + BX_ERROR(("WRMSR: attempt to write non-canonical value to BX_MSR_IA32_UINTR_STACKADJUST !")); + return 0; + } + BX_CPU_THIS_PTR uintr.stack_adjust = val_64; + break; + case BX_MSR_IA32_UINTR_MISC: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("WRMSR BX_MSR_IA32_UINTR_MISC: UINTR not enabled in the cpu model")); + return handle_unknown_wrmsr(index, val_64); + } + if (val_64 & BX_CONST64(0xffffff0000000000)) { + BX_ERROR(("WRMSR: attempt to write to reserved bits of BX_MSR_IA32_UINTR_MISC !")); + return 0; + } + BX_CPU_THIS_PTR uintr.uitt_size = GET32L(val_64); + BX_CPU_THIS_PTR uintr.uinv = GET32H(val_64); + break; + case BX_MSR_IA32_UINTR_PD: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("WRMSR BX_MSR_IA32_UINTR_PD: UINTR not enabled in the cpu model")); + return handle_unknown_wrmsr(index, val_64); + } + if (! IsCanonical(val_64)) { + BX_ERROR(("WRMSR: attempt to write non-canonical value to BX_MSR_IA32_UINTR_PD !")); + return 0; + } + if (val_64 & 0x3f) { // bits [5:0] are reserved and MBZ + BX_ERROR(("WRMSR: attempt to write to reserved bits of BX_MSR_IA32_UINTR_MISC !")); + return 0; + } + BX_CPU_THIS_PTR uintr.upid_addr = val_64; + break; + case BX_MSR_IA32_UINTR_TT: + if (! is_cpu_extension_supported(BX_ISA_UINTR)) { + BX_ERROR(("WRMSR BX_MSR_IA32_UINTR_TT: UINTR not enabled in the cpu model")); + return handle_unknown_wrmsr(index, val_64); + } + if (! IsCanonical(val_64)) { + BX_ERROR(("WRMSR: attempt to write non-canonical value to BX_MSR_IA32_UINTR_TT !")); + return 0; + } + if (val_64 & 0x0E) { // bits [3:1] are reserved and MBZ + BX_ERROR(("WRMSR: attempt to write to reserved bits of BX_MSR_IA32_UINTR_TT !")); + return 0; + } + BX_CPU_THIS_PTR uintr.uitt_addr = val_64; + break; +#endif + #if BX_SUPPORT_PKEYS case BX_MSR_IA32_PKRS: if (! is_cpu_extension_supported(BX_ISA_PKS)) { diff --git a/bochs/cpu/msr.h b/bochs/cpu/msr.h index 1e3fa87e63..544e20e829 100644 --- a/bochs/cpu/msr.h +++ b/bochs/cpu/msr.h @@ -138,7 +138,16 @@ enum MSR_Register { BX_MSR_IA32_PKRS = 0x6E1, - BX_MSR_XSS = 0xda0, +#if BX_SUPPORT_UINTR + BX_MSR_IA32_UINTR_RR = 0x985, + BX_MSR_IA32_UINTR_HANDLER = 0x986, + BX_MSR_IA32_UINTR_STACKADJUST = 0x987, + BX_MSR_IA32_UINTR_MISC = 0x988, + BX_MSR_IA32_UINTR_PD = 0x989, // interface to UPID_ADDR + BX_MSR_IA32_UINTR_TT = 0x98A, // interface to UITT_ADDR +#endif + + BX_MSR_XSS = 0xDA0, /* AMD MSRs */ BX_MSR_EFER = 0xc0000080, diff --git a/bochs/cpu/uintr.cc b/bochs/cpu/uintr.cc new file mode 100644 index 0000000000..004169d273 --- /dev/null +++ b/bochs/cpu/uintr.cc @@ -0,0 +1,345 @@ +///////////////////////////////////////////////////////////////////////// +// $Id$ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2023 Stanislav Shwartsman +// Written by Stanislav Shwartsman [sshwarts at sourceforge net] +// +// 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 +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA +// +///////////////////////////////////////////////////////////////////////// + +#define NEED_CPU_REG_SHORTCUTS 1 +#include "bochs.h" +#include "cpu.h" +#define LOG_THIS BX_CPU_THIS_PTR + +#if BX_SUPPORT_UINTR && BX_SUPPORT_X86_64 + +#include "scalar_arith.h" // for lzcntq + +void BX_CPU_C::deliver_UINTR() +{ + BX_ASSERT(BX_CPU_THIS_PTR cr4.get_UINTR() && !uintr_masked()); + + if (!IsCanonical(BX_CPU_THIS_PTR uintr.ui_handler)) { + BX_ERROR(("UINTR.UI_HANDLER is not canonical: #GP(0)")); + exception(BX_GP_EXCEPTION, 0); + } + + RSP_SPECULATIVE; + + Bit64u tmp_RSP = RSP; + if (BX_CPU_THIS_PTR uintr.stack_adjust & 0x1) + RSP = BX_CPU_THIS_PTR uintr.stack_adjust; + else + RSP -= BX_CPU_THIS_PTR uintr.stack_adjust; + RSP &= BX_CONST64(0xfffffffffffffff0); + + push_64(tmp_RSP); + push_64(read_eflags()); + push_64(RIP); + + BX_ASSERT(BX_CPU_THIS_PTR uintr.uirr != 0); + unsigned vector = most_significant_bitq(BX_CPU_THIS_PTR uintr.uirr); // find #most significant bit in UIRR + push_64(vector); + +#if BX_SUPPORT_CET + if (ShadowStackEnabled(3)) + shadow_stack_push_64(RIP); + track_indirect(3); +#endif + + RSP_COMMIT; + + BX_CPU_THIS_PTR uintr.uirr &= ~(BX_CONST64(1) << vector); + uintr_uirr_update(); // potentially clear user-level-interrupt + + BX_CPU_THIS_PTR uintr.UIF = 0; + BX_CPU_THIS_PTR clear_TF(); + BX_CPU_THIS_PTR clear_RF(); + RIP = BX_CPU_THIS_PTR uintr.ui_handler; + + uintr_control(); // UIF changed +} + +void BX_CPP_AttrRegparmN(1) BX_CPU_C::UIRET(bxInstruction_c *i) +{ + if (! BX_CPU_THIS_PTR cr4.get_UINTR()) { + BX_ERROR(("%s: UINTR in not enabled in CR4", i->getIaOpcodeNameShort())); + exception(BX_UD_EXCEPTION, 0); + } + + BX_INSTR_FAR_BRANCH_ORIGIN(); + + Bit64u new_rip = stack_read_qword(RSP + 8); + Bit32u new_eflags = (Bit32u) stack_read_qword(RSP + 16); + Bit64u new_rsp = stack_read_qword(RSP + 24); + + if (!IsCanonical(new_rip)) { + BX_ERROR(("UIRET #GP(0): return RIP is not canonical")); + exception(BX_GP_EXCEPTION, 0); + } + +#if BX_SUPPORT_CET + if (ShadowStackEnabled(CPL)) { + Bit64u shadow_RIP = shadow_stack_pop_64(); + if (new_rip != shadow_RIP) { + BX_ERROR(("shadow_stack_restore: LIP mismatch")); + exception(BX_CP_EXCEPTION, BX_CP_FAR_RET_IRET); + } + } +#endif + + RIP = new_rip; + // update in RFLAGS only CF, PF, AF, ZF, SF, TF, DF, OF, NT, RF, AC, and ID + writeEFlags(new_eflags, 0x254DD5); + RSP = new_rsp; + +#if BX_SUPPORT_MONITOR_MWAIT + BX_CPU_THIS_PTR monitor.reset_monitor(); +#endif + + BX_CPU_THIS_PTR uintr.UIF = 1; + uintr_control(); // potentially enable user interrupt delivery + + BX_INSTR_FAR_BRANCH(BX_CPU_ID, BX_INSTR_IS_UIRET, + FAR_BRANCH_PREV_CS, FAR_BRANCH_PREV_RIP, + BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value, RIP); + + BX_NEXT_TRACE(i); +} + +void BX_CPP_AttrRegparmN(1) BX_CPU_C::STUI(bxInstruction_c *i) +{ + if (! BX_CPU_THIS_PTR cr4.get_UINTR()) { + BX_ERROR(("%s: UINTR in not enabled in CR4", i->getIaOpcodeNameShort())); + exception(BX_UD_EXCEPTION, 0); + } + BX_CPU_THIS_PTR uintr.UIF = 1; + uintr_control(); // potentially enable user interrupt delivery + + BX_NEXT_TRACE(i); +} + +void BX_CPP_AttrRegparmN(1) BX_CPU_C::CLUI(bxInstruction_c *i) +{ + if (! BX_CPU_THIS_PTR cr4.get_UINTR()) { + BX_ERROR(("%s: UINTR in not enabled in CR4", i->getIaOpcodeNameShort())); + exception(BX_UD_EXCEPTION, 0); + } + BX_CPU_THIS_PTR uintr.UIF = 0; + uintr_control(); // disable user interrupt delivery + + BX_NEXT_INSTR(i); +} + +void BX_CPP_AttrRegparmN(1) BX_CPU_C::TESTUI(bxInstruction_c *i) +{ + if (! BX_CPU_THIS_PTR cr4.get_UINTR()) { + BX_ERROR(("%s: UINTR in not enabled in CR4", i->getIaOpcodeNameShort())); + exception(BX_UD_EXCEPTION, 0); + } + clearEFlagsOSZAPC(); + BX_CPU_THIS_PTR set_CF(BX_CPU_THIS_PTR uintr.UIF); + + BX_NEXT_INSTR(i); +} + +void BX_CPP_AttrRegparmN(1) BX_CPU_C::SENDUIPI_Gq(bxInstruction_c *i) +{ + if (! BX_CPU_THIS_PTR cr4.get_UINTR()) { + BX_ERROR(("%s: UINTR in not enabled in CR4", i->getIaOpcodeNameShort())); + exception(BX_UD_EXCEPTION, 0); + } + + if (! BX_CPU_THIS_PTR uintr.senduipi_enabled()) { + BX_ERROR(("SENDUIPI in disabled by IA32_UINTR_TT[0]")); + exception(BX_UD_EXCEPTION, 0); + } + + Bit64u index = BX_READ_64BIT_REG(i->src()); + if (index > BX_CPU_THIS_PTR uintr.uitt_size) { + BX_ERROR(("SENDUIPI: value of the source operand exceeds UITT.SIZE")); + exception(BX_GP_EXCEPTION, 0); + } + + // ----------- UITT format -------- + // | 00 | Valid + // | 07:01 | Reserved, MBZ + // | 15:08 | UVector, user-level interrupt vector, must be < 64 + // | 63:16 | Reserved, MBZ + // | 127:64 | UPID_ADDR, linear address of UPID, must be 64-byte aligned + // -------------------------------- + + Bit64u tmpUITT_entry_lo = system_read_qword(BX_CPU_THIS_PTR uintr.uitt_addr + index * 16); + Bit64u tmpUITT_upidaddr = system_read_qword(BX_CPU_THIS_PTR uintr.uitt_addr + index * 16 + 8); + if ((tmpUITT_entry_lo & 0x1) == 0) { + BX_ERROR(("SENDUIPI #GP(0): invalid UITT entry")); + exception(BX_GP_EXCEPTION, 0); + } + if (tmpUITT_entry_lo & BX_CONST64(0xFFFFFFFFFFFF00FE)) { + BX_ERROR(("SENDUIPI #GP(0): invalid UITT entry, reserved bits set")); + exception(BX_GP_EXCEPTION, 0); + } + unsigned uvector = (tmpUITT_entry_lo >> 8) & 0xFF; + if (uvector >= 64) { + BX_ERROR(("SENDUIPI #GP(0): UVector=%d >= 64", uvector)); + exception(BX_GP_EXCEPTION, 0); + } + + // ----------- UPID format -------- + // | 00 | Outstanding notification (UPID.ON) + // | 01 | Suppress notification (UPID.SN) + // | 15:02 | Reserved, MBZ + // | 23:16 | Notification vector, 8-bit + // | 31:24 | Reserved, MBZ + // | 63:32 | Notification destination, 32-bit APIC/X2APIC ID + // | 127:64 | Posted interrupt requests (PIR) + // | | One bit for each user-interrupt vector + // -------------------------------- + + bool send_notify = false; + Bit32u notification_vector = 0; + Bit32u notification_destination = 0; + +// should be done atomically using RMW + Bit64u tmpUPID_lo = system_read_qword(tmpUITT_upidaddr); + Bit64u tmpUPID_hi = system_read_qword(tmpUITT_upidaddr + 8); + if (tmpUPID_lo & BX_CONST64(0xFF00FFFC)) { + BX_ERROR(("SENDUIPI #GP(0): invalid UPID, reserved bits set")); + exception(BX_GP_EXCEPTION, 0); + } + + tmpUPID_hi |= (BX_CONST64(1) << uvector); // PIR[uvector] = 1 + if ((tmpUPID_lo & 0x3) == 0) { + tmpUPID_lo |= 1; + send_notify = true; + notification_vector = GET32L(tmpUPID_lo) >> 16; + notification_destination = GET32H(tmpUPID_lo); + } + + system_write_qword(tmpUITT_upidaddr, tmpUPID_lo); + system_write_qword(tmpUITT_upidaddr + 8, tmpUPID_hi); +// should be done atomically using RMW + + if (send_notify) { + send_uipi(notification_destination, notification_vector); + } + + BX_NEXT_TRACE(i); +} + +#include "apic.h" + +void BX_CPU_C::send_uipi(Bit32u notification_destination, Bit32u notification_vector) +{ +#if BX_SUPPORT_VMX + VMCS_CACHE *vm = &BX_CPU_THIS_PTR vmcs; + if (vm->vmexec_ctrls2 & VMX_VM_EXEC_CTRL1_TPR_SHADOW) { + if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL2_VIRTUALIZE_APIC_ACCESSES)) { + // virtualize sending of an XAPIC-mode IPI by: + // - writing to VICR_HI[31:24] = 8 bit destination APIC_ID from NDST[15:8] + // VICR_HI[23:00] = 0 + VMX_Write_Virtual_APIC(BX_LAPIC_ICR_HI, (notification_destination & 0xff00) << 16); + + // - writing to VICR_LO[31:08] = 0 (indicating a physically addressed fixed-mode IPI) + // VICR_HI[07:00] = notification_vector + VMX_Write_Virtual_APIC(BX_LAPIC_ICR_LO, notification_vector); + } + else { + // virtualize sending of an X2APIC-mode IPI by: + // - writing 64-bit value into VICR + // VICR[7:0] = notification_vector + // VICR[31:8] = 0 (indicating a physically addressed fixed-mode IPI) + // VICR[64:32] = 32-bit notification destination + VMX_Write_Virtual_X2APIC(BX_LAPIC_ICR_LO, GET64_FROM_HI32_LO32(notification_destination, notification_vector)); + } + + VMexit(VMX_VMEXIT_APIC_WRITE, BX_LAPIC_ICR_LO); // trap-like vmexit + return; + } +#endif + + // sending of IPI is done by writing to the local APIC's ICR register: + // ICR[7:0] = notification_vector + // ICR[64:32] = the APIC_ID from NDST[15:8] (legacy apic mode) or NDST[31:0] (x2apic mode) + // ICR[31:8] = 0 (indicating a physically addressed fixed-mode IPI) +#if BX_SUPPORT_APIC + BX_CPU_THIS_PTR lapic.send_ipi(x2apic_mode() ? notification_destination : (notification_destination >> 8), notification_vector); +#endif +} + +void BX_CPU_C::process_uintr_notification() +{ +#if BX_SUPPORT_VMX + // the User-Level Interrupt notification process looks like external interrupt with the vector UINV + // in particular for IDT-vectoring info + if (BX_CPU_THIS_PTR in_vmx_guest) { + VMCS_CACHE *vm = &BX_CPU_THIS_PTR vmcs; + vm->idt_vector_error_code = 0; + vm->idt_vector_info = (BX_CPU_THIS_PTR uintr.uinv) | (BX_EXTERNAL_INTERRUPT << 8); + } +#endif + + // Step 1: Clear UPID.ON atomically with locked AND operation + +// should be done atomically using RMW + Bit8u tmpUPID_lo8 = system_read_byte(BX_CPU_THIS_PTR uintr.upid_addr); + tmpUPID_lo8 &= 0xFE; + system_write_byte(BX_CPU_THIS_PTR uintr.upid_addr, tmpUPID_lo8); +// should be done atomically using RMW + + // Step 2: Read UPID.PIR into temporary PIR register and write '0 to the PIR using locked XCHG operation + +// should be done atomically using RMW + Bit64u PIR = system_read_qword(BX_CPU_THIS_PTR uintr.upid_addr + 8); + system_write_qword(BX_CPU_THIS_PTR uintr.upid_addr + 8, 0); +// should be done atomically using RMW + + // Step 3: for any bit set in the temporary PIR register set corresponding UIRR bit + // recognize pending user-level interrupt + BX_CPU_THIS_PTR uintr.uirr |= PIR; + uintr_uirr_update(); // potentially signal user-level-interrupt +} + +bool BX_CPU_C::uintr_masked() +{ + // The user-level interrupt can be delivered only if: + // long64_mode() + // UIF = 1 + // CPL = 3 + return !long64_mode() || !BX_CPU_THIS_PTR uintr.UIF || CPL != 3; +} + +// mask or unmask BX_EVENT_PENDING_UINTR according to conditions telling if it can be potentially delivered +void BX_CPU_C::uintr_uirr_update() +{ + // There is a pending user-level interrupt if UINTR.UIRR != 0 + if (BX_CPU_THIS_PTR cr4.get_UINTR() && BX_CPU_THIS_PTR uintr.uirr) + signal_event(BX_EVENT_PENDING_UINTR); + else + clear_event(BX_EVENT_PENDING_UINTR); +} + +void BX_CPU_C::uintr_control() +{ + if (uintr_masked()) + mask_event(BX_EVENT_PENDING_UINTR); + else + unmask_event(BX_EVENT_PENDING_UINTR); +} + +#endif diff --git a/bochs/cpu/vapic.cc b/bochs/cpu/vapic.cc index 9b98612227..580bdc3545 100644 --- a/bochs/cpu/vapic.cc +++ b/bochs/cpu/vapic.cc @@ -270,7 +270,7 @@ BX_CPP_INLINE Bit8u BX_CPU_C::vapic_clear_and_find_highest_priority_int(unsigned for (n = 7; n >= 0; n--) { if (! arr[n]) continue; - return (n * 32) + (31 - lzcntd(arr[n])); + return (n * 32) + most_significant_bitd(arr[n]); } return 0; @@ -390,9 +390,22 @@ void BX_CPU_C::VMX_Deliver_Virtual_Interrupt(void) BX_CPU_THIS_PTR EXT = 1; /* external event */ - BX_INSTR_HWINTERRUPT(BX_CPU_ID, vector, +#if BX_SUPPORT_UINTR + if (BX_CPU_THIS_PTR cr4.get_UINTR() && long64_mode() && vector == BX_CPU_THIS_PTR uintr.uinv) + { + unsigned vector = vm->svi; + vm->svi = vapic_clear_and_find_highest_priority_int(BX_LAPIC_ISR1, vector); + VMX_PPR_Virtualization(); + + process_uintr_notification(); + } + else +#endif + { + BX_INSTR_HWINTERRUPT(BX_CPU_ID, vector, BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value, RIP); - interrupt(vector, BX_EXTERNAL_INTERRUPT, 0, 0); + interrupt(vector, BX_EXTERNAL_INTERRUPT, 0, 0); + } BX_CPU_THIS_PTR prev_rip = RIP; // commit new RIP BX_CPU_THIS_PTR EXT = 0; diff --git a/bochs/cpu/vmcs.cc b/bochs/cpu/vmcs.cc index a72893e4a8..6bf088afa1 100644 --- a/bochs/cpu/vmcs.cc +++ b/bochs/cpu/vmcs.cc @@ -199,6 +199,11 @@ bool BX_CPU_C::vmcs_field_supported(Bit32u encoding) return BX_SUPPORT_VMX_EXTENSION(BX_VMX_PML); #endif +#if BX_SUPPORT_UINTR + case VMCS_16BIT_GUEST_UINV: + return is_cpu_extension_supported(BX_ISA_UINTR); +#endif + /* VMCS 16-bit host-state fields */ /* binary 0000_11xx_xxxx_xxx0 */ case VMCS_16BIT_HOST_ES_SELECTOR: @@ -855,6 +860,7 @@ void BX_CPU_C::init_vmexit_ctrls(void) // [20] Save guest MSR_EFER on VMEXIT // [21] Load host MSR_EFER on VMEXIT // [22] Save VMX preemption timer counter on VMEXIT + // [27] Clear UINV state on VMEXIT // [28] Save host CET state on VMEXIT // [29] Save host MSR_IA32_PKRS on VMEXIT // [30] Save guest MSR_PERF_GLOBAL_CTRL on VMEXIT @@ -880,6 +886,10 @@ void BX_CPU_C::init_vmexit_ctrls(void) if (BX_SUPPORT_VMX_EXTENSION(BX_VMX_PREEMPTION_TIMER)) cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER; #endif +#if BX_SUPPORT_UINTR + if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_UINTR)) + cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_CLEAR_UINV; +#endif #if BX_SUPPORT_CET if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_CET)) cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_LOAD_HOST_CET_STATE; @@ -909,6 +919,7 @@ void BX_CPU_C::init_vmentry_ctrls(void) // [14] Load guest MSR_PAT // [15] Load guest MSR_EFER // [17] Load guest CET state + // [19] Load guest UINV state // [22] Load guest MSR_IA32_PKRS value cap->vmx_vmentry_ctrl_supported_bits = VMX_VMENTRY_CTRL1_LOAD_DBG_CTRLS | @@ -931,6 +942,10 @@ void BX_CPU_C::init_vmentry_ctrls(void) if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_CET)) cap->vmx_vmentry_ctrl_supported_bits |= VMX_VMENTRY_CTRL1_LOAD_GUEST_CET_STATE; #endif +#if BX_SUPPORT_UINTR + if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_UINTR)) + cap->vmx_vmentry_ctrl_supported_bits |= VMX_VMENTRY_CTRL1_LOAD_UINV; +#endif #if BX_SUPPORT_PKEYS if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_PKS)) cap->vmx_vmentry_ctrl_supported_bits |= VMX_VMENTRY_CTRL1_LOAD_GUEST_PKRS; diff --git a/bochs/cpu/vmx.cc b/bochs/cpu/vmx.cc index 6024e0d8a3..5af62b1154 100644 --- a/bochs/cpu/vmx.cc +++ b/bochs/cpu/vmx.cc @@ -974,7 +974,7 @@ VMX_error_code BX_CPU_C::VMenterLoadCheckVmControls(void) unsigned push_error = (vm->vmentry_interr_info >> 11) & 1; unsigned error_code = push_error ? vm->vmentry_excep_err_code : 0; - unsigned push_error_reference = 0; + unsigned push_error_reference = false; if (event_type == BX_HARDWARE_EXCEPTION && vector < BX_CPU_HANDLED_EXCEPTIONS) push_error_reference = exceptions_info[vector].push_error; @@ -1944,6 +1944,16 @@ Bit32u BX_CPU_C::VMenterLoadCheckGuestState(Bit64u *qualification) return VMX_VMEXIT_VMENTRY_FAILURE_GUEST_STATE; } +#if BX_SUPPORT_UINTR + if (vmentry_ctrls & VMX_VMENTRY_CTRL1_LOAD_UINV) { + guest.uintr_uinv = VMread16(VMCS_16BIT_GUEST_UINV); + if (guest.uintr_uinv >= 256) { + BX_ERROR(("VMENTER FAIL: VMCS guest INTR.UINV=%d > 256", guest.uintr_uinv)); + return VMX_VMEXIT_VMENTRY_FAILURE_GUEST_STATE; + } + } +#endif + if ((guest.rflags & EFlagsIFMask) == 0) { if (guest.interruptibility_state & BX_VMX_INTERRUPTS_BLOCKED_BY_STI) { BX_ERROR(("VMENTER FAIL: VMCS guest interrupts can't be blocked by STI when EFLAGS.IF = 0")); @@ -2125,10 +2135,19 @@ Bit32u BX_CPU_C::VMenterLoadCheckGuestState(Bit64u *qualification) BX_CPU_THIS_PTR msr.sysenter_eip_msr = guest.sysenter_eip_msr; BX_CPU_THIS_PTR msr.sysenter_cs_msr = guest.sysenter_cs_msr; +#if BX_SUPPORT_UINTR + if (vmentry_ctrls & VMX_VMENTRY_CTRL1_LOAD_UINV) { + BX_CPU_THIS_PTR uintr.uinv = guest.uintr_uinv; + } +#endif + #if BX_SUPPORT_VMX >= 2 if (vmentry_ctrls & VMX_VMENTRY_CTRL1_LOAD_PAT_MSR) { BX_CPU_THIS_PTR msr.pat = guest.pat_msr; } +#endif + +#if BX_SUPPORT_VMX >= 2 vm->ple.last_pause_time = vm->ple.first_pause_time = 0; #endif @@ -2264,7 +2283,12 @@ void BX_CPU_C::VMenterInjectEvents(void) vm->idt_vector_info = vm->vmentry_interr_info & ~0x80000000; vm->idt_vector_error_code = error_code; - interrupt(vector, type, push_error, error_code); +#if BX_SUPPORT_UINTR + if (BX_CPU_THIS_PTR cr4.get_UINTR() && long64_mode() && vector == BX_CPU_THIS_PTR uintr.uinv) + process_uintr_notification(); + else +#endif + interrupt(vector, type, push_error, error_code); BX_CPU_THIS_PTR last_exception_type = 0; // error resolved } @@ -2387,6 +2411,12 @@ void BX_CPU_C::VMexitSaveGuestState(Bit32u reason) } #endif +#if BX_SUPPORT_UINTR + if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_UINTR)) { + VMwrite16(VMCS_16BIT_GUEST_UINV, BX_CPU_THIS_PTR uintr.uinv); + } +#endif + #if BX_SUPPORT_PKEYS if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_PKS)) { VMwrite64(VMCS_64BIT_GUEST_IA32_PKRS, BX_CPU_THIS_PTR pkrs); @@ -2710,6 +2740,12 @@ void BX_CPU_C::VMexitLoadHostState(void) } #endif +#if BX_SUPPORT_UINTR + if (vmexit_ctrls & VMX_VMEXIT_CTRL1_CLEAR_UINV) { + BX_CPU_THIS_PTR uintr.uinv = 0; + } +#endif + #if BX_SUPPORT_PKEYS if (vmexit_ctrls & VMX_VMEXIT_CTRL1_LOAD_HOST_PKRS) { set_PKeys(BX_CPU_THIS_PTR pkru, host_state->pkrs); diff --git a/bochs/cpu/vmx.h b/bochs/cpu/vmx.h index 54c15d15f3..229cc1f4c7 100644 --- a/bochs/cpu/vmx.h +++ b/bochs/cpu/vmx.h @@ -204,6 +204,7 @@ const Bit64u VMX_VMFUNC_EPTP_SWITCHING_MASK = (BX_CONST64(1) << VMX_VMFUNC_EPTP_ #define VMCS_16BIT_CONTROL_VPID 0x00000000 /* VPID */ #define VMCS_16BIT_CONTROL_POSTED_INTERRUPT_VECTOR 0x00000002 /* Posted Interrupts - not implememted yet */ #define VMCS_16BIT_CONTROL_EPTP_INDEX 0x00000004 /* #VE Exception */ +#define VMCS_16BIT_CONTROL_LAST_PID_POINTER_INDEX 0x00000008 /* IPI Virtualization */ /* VMCS 16-bit guest-state fields */ /* binary 0000_10xx_xxxx_xxx0 */ @@ -217,6 +218,7 @@ const Bit64u VMX_VMFUNC_EPTP_SWITCHING_MASK = (BX_CONST64(1) << VMX_VMFUNC_EPTP_ #define VMCS_16BIT_GUEST_TR_SELECTOR 0x0000080E #define VMCS_16BIT_GUEST_INTERRUPT_STATUS 0x00000810 /* Virtual Interrupt Delivery */ #define VMCS_16BIT_GUEST_PML_INDEX 0x00000812 /* Page Modification Logging */ +#define VMCS_16BIT_GUEST_UINV 0x00000814 /* UINTR */ /* VMCS 16-bit host-state fields */ /* binary 0000_11xx_xxxx_xxx0 */ @@ -296,7 +298,7 @@ const Bit64u VMX_VMFUNC_EPTP_SWITCHING_MASK = (BX_CONST64(1) << VMX_VMFUNC_EPTP_ #define VMCS_64BIT_CONTROL_PCONFIG_EXITING_BITMAP_HI 0x0000203F #define VMCS_64BIT_CONTROL_HLAT_POINTER 0x00002040 /* HLAT (not implemented) */ #define VMCS_64BIT_CONTROL_HLAT_POINTER_HI 0x00002041 -#define VMCS_64BIT_CONTROL_PID_POINTER_TABLE_ADDRESS 0x00002042 /* Posted interrupts (not implemented) */ +#define VMCS_64BIT_CONTROL_PID_POINTER_TABLE_ADDRESS 0x00002042 /* IPI Virtualization (not implemented) */ #define VMCS_64BIT_CONTROL_PID_POINTER_TABLE_ADDRESS_HI 0x00002043 #define VMCS_64BIT_CONTROL_SECONDARY_VMEXIT_CONTROLS 0x00002044 #define VMCS_64BIT_CONTROL_SECONDARY_VMEXIT_CONTROLS_HI 0x00002045 @@ -641,6 +643,10 @@ typedef struct bx_VMCS_GUEST_STATE bx_address interrupt_ssp_table_address; #endif +#if BX_SUPPORT_UINTR + Bit16u uintr_uinv; +#endif + #if BX_SUPPORT_PKEYS Bit32u pkrs; #endif @@ -886,6 +892,7 @@ typedef struct bx_VMCS #define VMX_VMEXIT_CTRL1_SUPPRESS_HOST_VMX_TRACE (1 << 24) /* Processor Trace (not implemented) */ #define VMX_VMEXIT_CTRL1_CLEAR_IA32_RTIT_CTRL (1 << 25) /* Clear IA32_RTIT_CTRL MSR on vmexit (not implemented) */ #define VMX_VMEXIT_CTRL1_CLEAR_IA32_LBR_CTRL (1 << 26) /* Clear IA32_LBR_CTRL MSR on vmexit (not implemented) */ +#define VMX_VMEXIT_CTRL1_CLEAR_UINV (1 << 27) /* UINTR */ #define VMX_VMEXIT_CTRL1_LOAD_HOST_CET_STATE (1 << 28) /* CET */ #define VMX_VMEXIT_CTRL1_LOAD_HOST_PKRS (1 << 29) /* Supervisor-Mode Protection Keys */ #define VMX_VMEXIT_CTRL1_SAVE_PERF_GLOBAL_CTRL (1 << 30) /* Save IA32_PERF_GLOBAL_CTRL on vmexit (not implemented) */ @@ -914,6 +921,7 @@ typedef struct bx_VMCS #define VMX_VMENTRY_CTRL1_LOAD_BNDCFGS (1 << 16) /* MPX (not implemented) */ #define VMX_VMENTRY_CTRL1_SUPPRESS_VMX_PACKETS (1 << 17) /* Processor Trace (not implemented) */ #define VMX_VMENTRY_CTRL1_LOAD_GUEST_RTIT_CTRL (1 << 18) // not implemented +#define VMX_VMENTRY_CTRL1_LOAD_UINV (1 << 19) /* UINTR */ #define VMX_VMENTRY_CTRL1_LOAD_GUEST_CET_STATE (1 << 20) /* CET */ #define VMX_VMENTRY_CTRL1_LOAD_GUEST_LBR_CTRL (1 << 21) // not implemented #define VMX_VMENTRY_CTRL1_LOAD_GUEST_PKRS (1 << 22) /* Supervisor-Mode Protection Keys */ diff --git a/bochs/cpu/xsave.cc b/bochs/cpu/xsave.cc index 370cf4ecb5..dc3851731f 100644 --- a/bochs/cpu/xsave.cc +++ b/bochs/cpu/xsave.cc @@ -966,6 +966,83 @@ bool BX_CPU_C::xsave_cet_s_state_xinuse(void) } #endif +#if BX_SUPPORT_UINTR +void BX_CPU_C::xsave_uintr_state(bxInstruction_c *i, bx_address offset) +{ + bx_address asize_mask = i->asize_mask(); + + write_virtual_qword(i->seg(), offset, BX_CPU_THIS_PTR uintr.ui_handler); + write_virtual_qword(i->seg(), (offset + 8) & asize_mask, BX_CPU_THIS_PTR uintr.stack_adjust); + + Bit64u misc = GET64_FROM_HI32_LO32(BX_CPU_THIS_PTR uintr.uinv, BX_CPU_THIS_PTR uintr.uitt_size); + if (BX_CPU_THIS_PTR uintr.UIF) + misc |= BX_CONST64(0x8000000000000000); + write_virtual_qword(i->seg(), (offset + 16) & asize_mask, misc); + + write_virtual_qword(i->seg(), (offset + 24) & asize_mask, BX_CPU_THIS_PTR uintr.upid_addr); + write_virtual_qword(i->seg(), (offset + 32) & asize_mask, BX_CPU_THIS_PTR uintr.uirr); + write_virtual_qword(i->seg(), (offset + 40) & asize_mask, BX_CPU_THIS_PTR uintr.uitt_addr); + + BX_CPU_THIS_PTR uintr.uinv = 0; +} + +void BX_CPU_C::xrstor_uintr_state(bxInstruction_c *i, bx_address offset) +{ + if (BX_CPU_THIS_PTR uintr.uinv) { + BX_ERROR(("Attempting to restore UINTR state when UINTR.UINV is set: #GP(0)")); + exception(BX_GP_EXCEPTION, 0); + } + + BX_CPU_THIS_PTR uintr.uinv = 0; // will be restored and overwritten, should stay '0 in case of any fault + + bx_address asize_mask = i->asize_mask(); + + Bit64u ui_handler = read_virtual_qword(i->seg(), offset); + Bit64u stack_adjust = read_virtual_qword(i->seg(), (offset + 8) & asize_mask); + Bit64u misc = read_virtual_qword(i->seg(), (offset + 16) & asize_mask); + bool UIF = (misc >> 63) != 0; + misc &= ~BX_CONST64(0x8000000000000000); // clear UIF + Bit64u upid_addr = read_virtual_qword(i->seg(), (offset + 24) & asize_mask); + Bit64u uirr = read_virtual_qword(i->seg(), (offset + 32) & asize_mask); + Bit64u uitt_addr = read_virtual_qword(i->seg(), (offset + 40) & asize_mask); + + // XRSTOR on UINTR state does all reserved bits and canonicality check like WRMSR would do + wrmsr(BX_MSR_IA32_UINTR_HANDLER, ui_handler); + wrmsr(BX_MSR_IA32_UINTR_STACKADJUST, stack_adjust); + wrmsr(BX_MSR_IA32_UINTR_PD, upid_addr); + wrmsr(BX_MSR_IA32_UINTR_TT, uitt_addr); + wrmsr(BX_MSR_IA32_UINTR_MISC, misc); // make sure all faults happen before + wrmsr(BX_MSR_IA32_UINTR_RR, uirr); + + BX_CPU_THIS_PTR uintr.UIF = UIF; + uintr_control(); // potentially enable user interrupt delivery +} + +void BX_CPU_C::xrstor_init_uintr_state(void) +{ + BX_CPU_THIS_PTR uintr.ui_handler = 0; + BX_CPU_THIS_PTR uintr.stack_adjust = 0; + BX_CPU_THIS_PTR uintr.uitt_size = 0; + BX_CPU_THIS_PTR uintr.uinv = 0; + BX_CPU_THIS_PTR uintr.UIF = 0; + BX_CPU_THIS_PTR uintr.upid_addr = 0; + BX_CPU_THIS_PTR uintr.uitt_addr = 0; + BX_CPU_THIS_PTR uintr.uirr = 0; +} + +bool BX_CPU_C::xsave_uintr_state_xinuse(void) +{ + return BX_CPU_THIS_PTR uintr.ui_handler != 0 || + BX_CPU_THIS_PTR uintr.stack_adjust != 0 || + BX_CPU_THIS_PTR uintr.uitt_size != 0 || + BX_CPU_THIS_PTR uintr.uinv != 0 || + BX_CPU_THIS_PTR uintr.UIF != 0 || + BX_CPU_THIS_PTR uintr.upid_addr != 0 || + BX_CPU_THIS_PTR uintr.uitt_addr != 0 || + BX_CPU_THIS_PTR uintr.uirr != 0; +} +#endif + Bit32u BX_CPU_C::get_xinuse_vector(Bit32u requested_feature_bitmap) { Bit32u xinuse = 0;