Merge tag 'x86-cpu-2026-06-14' of gitolite.kernel.org:pub/scm/linux/kernel/git/tip/tip

Pull x86 cpuid updates from Ingo Molnar:

 - CPUID API updates (Ahmed S. Darwish):
    - Introduce a centralized CPUID parser
    - Introduce a centralized CPUID data model
    - Introduce <asm/cpuid/leaf_types.h>
    - Rename cpuid_leaf()/cpuid_subleaf() APIs
    - treewide: Explicitly include the x86 CPUID headers

 - Update to x86-cpuid-db v3.1 (Maciej Wieczor-Retman)

 - Continued removal of pre-i586 support and related simplifications
   (Ingo Molnar)

 - Add Intel CPU model number for rugged Panther Lake (Tony Luck)

 - Misc fixes, updates and cleanups by Arnd Bergmann, Chao Gao, Lukas
   Bulwahn, Sohil Mehta, Maciej Wieczor-Retman.

* tag 'x86-cpu-2026-06-14' of gitolite.kernel.org:pub/scm/linux/kernel/git/tip/tip: (25 commits)
  x86/cpu: Make CONFIG_X86_CX8 unconditional
  x86/cpu: Remove unused !CONFIG_X86_TSC code
  x86/cpuid: Update bitfields to x86-cpuid-db v3.1
  tools/x86/kcpuid: Update bitfields to x86-cpuid-db v3.1
  x86/cpu: Make CONFIG_X86_TSC unconditional
  MAINTAINERS: Drop obsolete FPU EMULATOR section
  x86/cpu: Fix a F00F bug warning and clean up surrounding code
  x86/cpu: Add Intel CPU model number for rugged Panther Lake
  x86/cpuid: Introduce a centralized CPUID parser
  x86/cpu: Introduce a centralized CPUID data model
  x86/cpuid: Introduce <asm/cpuid/leaf_types.h>
  x86/cpuid: Rename cpuid_leaf()/cpuid_subleaf() APIs
  x86/cpu: Do not include the CPUID API header in asm/processor.h
  Documentation: core-api/cpu_hotplug: Remove stale cpu0_hotplug docs
  x86/cpu, cpufreq: Remove AMD ELAN support
  x86/fpu: Remove the math-emu/ FPU emulation library
  x86/fpu: Remove the 'no387' boot option
  x86/fpu: Remove MATH_EMULATION and related glue code
  treewide: Explicitly include the x86 CPUID headers
  x86/cpu: Remove the CONFIG_X86_INVD_BUG quirk
  ...
This commit is contained in:
Linus Torvalds
2026-06-15 15:25:17 +05:30
157 changed files with 3157 additions and 13470 deletions
+1
View File
@@ -2804,6 +2804,7 @@ D: Some of PAS 16 mixer & PCM support, inet6-apps
N: William (Bill) Metzenthen
E: billm@suburbia.net
E: billm@melbpc.org.au
D: Author of the FPU emulator.
D: Minor kernel hacker for other lost causes (Hercules mono, etc).
S: 22 Parker Street
@@ -4416,10 +4416,6 @@ Kernel parameters
These settings can be accessed at runtime via
the nmi_watchdog and hardlockup_panic sysctls.
no387 [BUGS=X86-32] Tells the kernel to use the 387 maths
emulation library even if a 387 maths coprocessor
is present.
no4lvl [RISCV,EARLY] Disable 4-level and 5-level paging modes.
Forces kernel to use 3-level paging instead.
-5
View File
@@ -45,11 +45,6 @@ Command Line Switches
This option is limited to the X86 and S390 architecture.
``cpu0_hotplug``
Allow to shutdown CPU0.
This option is limited to the X86 architecture.
CPU maps
========
+3 -9
View File
@@ -1122,10 +1122,9 @@ S: Orphan
F: drivers/usb/gadget/udc/amd5536udc.*
AMD GEODE PROCESSOR/CHIPSET SUPPORT
M: Andres Salomon <dilinger@queued.net>
L: linux-geode@lists.infradead.org (moderated for non-subscribers)
S: Supported
W: http://www.amd.com/us-en/ConnectivitySolutions/TechnicalResources/0,,50_2334_2452_11363,00.html
S: Orphaned
F: arch/x86/platform/geode/
F: arch/x86/include/asm/geode.h
F: drivers/char/hw_random/geode-rng.c
F: drivers/crypto/geode*
@@ -10117,12 +10116,6 @@ F: Documentation/fpga/
F: drivers/fpga/
F: include/linux/fpga/
FPU EMULATOR
M: Bill Metzenthen <billm@melbpc.org.au>
S: Maintained
W: https://floatingpoint.billm.au/
F: arch/x86/math-emu/
FRAMEBUFFER CONSOLE
M: Helge Deller <deller@gmx.de>
M: Thomas Zimmermann <tzimmermann@suse.de>
@@ -28899,6 +28892,7 @@ R: Ahmed S. Darwish <darwi@linutronix.de>
L: x86-cpuid@lists.linux.dev
S: Maintained
W: https://x86-cpuid.org
F: arch/x86/include/asm/cpuid/leaf_types.h
F: tools/arch/x86/kcpuid/
X86 ENTRY CODE
+10 -51
View File
@@ -132,7 +132,7 @@ config X86
select ARCH_SUPPORTS_LTO_CLANG_THIN
select ARCH_SUPPORTS_RT
select ARCH_USE_BUILTIN_BSWAP
select ARCH_USE_CMPXCHG_LOCKREF if X86_CX8
select ARCH_USE_CMPXCHG_LOCKREF
select ARCH_USE_MEMTEST
select ARCH_USE_QUEUED_RWLOCKS
select ARCH_USE_QUEUED_SPINLOCKS
@@ -682,7 +682,6 @@ config X86_INTEL_QUARK
depends on X86_32
depends on X86_EXTENDED_PLATFORM
depends on X86_PLATFORM_DEVICES
depends on X86_TSC
depends on PCI
depends on PCI_GOANY
depends on X86_IO_APIC
@@ -694,17 +693,6 @@ config X86_INTEL_QUARK
Say Y here if you have a Quark based system such as the Arduino
compatible Intel Galileo.
config X86_RDC321X
bool "RDC R-321x SoC"
depends on X86_32
depends on X86_EXTENDED_PLATFORM
select M486
select X86_REBOOTFIXUPS
help
This option is needed for RDC R-321x system-on-chip, also known
as R-8610-(G).
If you don't have one of these chips, you should say N here.
config X86_INTEL_LPSS
bool "Intel Low Power Subsystem Support"
depends on X86 && ACPI && PCI
@@ -1643,33 +1631,6 @@ config X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
Set whether the default state of memory_corruption_check is
on or off.
config MATH_EMULATION
bool
depends on MODIFY_LDT_SYSCALL
prompt "Math emulation" if X86_32 && (M486SX || MELAN)
help
Linux can emulate a math coprocessor (used for floating point
operations) if you don't have one. 486DX and Pentium processors have
a math coprocessor built in, 486SX and 386 do not, unless you added
a 487DX or 387, respectively. (The messages during boot time can
give you some hints here ["man dmesg"].) Everyone needs either a
coprocessor or this emulation.
If you don't have a math coprocessor, you need to say Y here; if you
say Y here even though you have a coprocessor, the coprocessor will
be used nevertheless. (This behavior can be changed with the kernel
command line option "no387", which comes handy if your coprocessor
is broken. Try "man bootparam" or see the documentation of your boot
loader (lilo or loadlin) about how to pass options to the kernel at
boot time.) This means that it is a good idea to say Y here if you
intend to use this kernel on different machines.
More information about the internals of the Linux math coprocessor
emulation can be found in <file:arch/x86/math-emu/README>.
If you are not sure, say Y; apart from resulting in a 66 KB bigger
kernel, it won't hurt.
config MTRR
def_bool y
prompt "MTRR (Memory Type Range Register) support" if EXPERT
@@ -2775,18 +2736,16 @@ menuconfig APM
1) make sure that you have enough swap space and that it is
enabled.
2) pass the "idle=poll" option to the kernel
3) switch on floating point emulation in the kernel and pass
the "no387" option to the kernel
4) pass the "floppy=nodma" option to the kernel
5) pass the "mem=4M" option to the kernel (thereby disabling
3) pass the "floppy=nodma" option to the kernel
4) pass the "mem=4M" option to the kernel (thereby disabling
all but the first 4 MB of RAM)
6) make sure that the CPU is not over clocked.
7) read the sig11 FAQ at <http://www.bitwizard.nl/sig11/>
8) disable the cache from your BIOS settings
9) install a fan for the video card or exchange video RAM
10) install a better fan for the CPU
11) exchange RAM chips
12) exchange the motherboard.
5) make sure that the CPU is not over clocked.
6) read the sig11 FAQ at <http://www.bitwizard.nl/sig11/>
7) disable the cache from your BIOS settings
8) install a fan for the video card or exchange video RAM
9) install a better fan for the CPU
10) exchange RAM chips
11) exchange the motherboard.
To compile this driver as a module, choose M here: the
module will be called apm.
+4 -52
View File
@@ -32,8 +32,6 @@ choice
- "Athlon" for the AMD K7 family (Athlon/Duron/Thunderbird).
- "Crusoe" for the Transmeta Crusoe series.
- "Efficeon" for the Transmeta Efficeon series.
- "Winchip-C6" for original IDT Winchip.
- "Winchip-2" for IDT Winchips with 3dNow! capabilities.
- "AMD Elan" for the 32-bit AMD Elan embedded CPU.
- "GeodeGX1" for Geode GX1 (Cyrix MediaGX).
- "Geode GX/LX" For AMD Geode GX and LX processors.
@@ -45,14 +43,6 @@ choice
See each option's help text for additional details. If you don't know
what to do, choose "Pentium-Pro".
config M586
bool "586/K5/5x86/6x86/6x86MX"
depends on X86_32
help
Select this for an 586 or 686 series processor such as the AMD K5,
the Cyrix 5x86, 6x86 and 6x86MX. This choice does not
assume the RDTSC (Read Time Stamp Counter) instruction.
config M586TSC
bool "Pentium-Classic"
depends on X86_32
@@ -155,24 +145,6 @@ config MEFFICEON
help
Select this for a Transmeta Efficeon processor.
config MWINCHIPC6
bool "Winchip-C6"
depends on X86_32
help
Select this for an IDT Winchip C6 chip. Linux and GCC
treat this chip as a 586TSC with some extended instructions
and alignment requirements.
config MWINCHIP3D
bool "Winchip-2/Winchip-2A/Winchip-3"
depends on X86_32
help
Select this for an IDT Winchip-2, 2A or 3. Linux and GCC
treat this chip as a 586TSC with some extended instructions
and alignment requirements. Also enable out of order memory
stores for this CPU, which can increase performance of some
operations.
config MGEODEGX1
bool "GeodeGX1"
depends on X86_32
@@ -270,19 +242,15 @@ config X86_L1_CACHE_SHIFT
default "7" if MPENTIUM4
default "6" if MK7 || MPENTIUMM || MATOM || MVIAC7 || X86_GENERIC || X86_64
default "4" if MGEODEGX1
default "5" if MWINCHIP3D || MWINCHIPC6 || MCRUSOE || MEFFICEON || MCYRIXIII || MK6 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODE_LX
default "5" if MCRUSOE || MEFFICEON || MCYRIXIII || MK6 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MVIAC3_2 || MGEODE_LX
config X86_F00F_BUG
def_bool y
depends on M586MMX || M586TSC || M586
config X86_INVD_BUG
def_bool y
depends on M486SX || M486
config X86_ALIGNMENT_16
def_bool y
depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || MK6 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODEGX1
depends on MCYRIXIII || MK6 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODEGX1
config X86_INTEL_USERCOPY
def_bool y
@@ -290,11 +258,10 @@ config X86_INTEL_USERCOPY
config X86_USE_PPRO_CHECKSUM
def_bool y
depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7 || MEFFICEON || MGEODE_LX || MATOM
depends on MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7 || MEFFICEON || MGEODE_LX || MATOM
config X86_TSC
def_bool y
depends on (MWINCHIP3D || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MVIAC3_2 || MVIAC7 || MGEODEGX1 || MGEODE_LX || MATOM) || X86_64
config X86_HAVE_PAE
def_bool y
@@ -302,7 +269,6 @@ config X86_HAVE_PAE
config X86_CX8
def_bool y
depends on X86_HAVE_PAE || M586TSC || M586MMX || MK6 || MK7 || MGEODEGX1 || MGEODE_LX
# this should be set for all -march=.. options where the compiler
# generates cmov.
@@ -318,7 +284,7 @@ config X86_MINIMUM_CPU_FAMILY
config X86_DEBUGCTLMSR
def_bool y
depends on !(MK6 || MWINCHIPC6 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586) && !UML
depends on !(MK6 || MCYRIXIII || M586MMX || M586TSC || M586) && !UML
config IA32_FEAT_CTL
def_bool y
@@ -419,20 +385,6 @@ config CPU_SUP_TRANSMETA_32
If unsure, say N.
config CPU_SUP_UMC_32
default y
bool "Support UMC processors" if PROCESSOR_SELECT
depends on M486SX || M486 || (EXPERT && !64BIT)
help
This enables detection, tunings and quirks for UMC processors
You need this enabled if you want your kernel to run on a
UMC CPU. Disabling this option on other types of CPUs
makes the kernel a tiny bit smaller. Disabling it on a UMC
CPU might render the kernel unbootable.
If unsure, say N.
config CPU_SUP_ZHAOXIN
default y
bool "Support Zhaoxin processors" if PROCESSOR_SELECT
-2
View File
@@ -42,7 +42,6 @@ config X86_REQUIRED_FEATURE_NOPL
config X86_REQUIRED_FEATURE_CX8
def_bool y
depends on X86_CX8
# this should be set for all -march=.. options where the compiler
# generates cmov.
@@ -70,7 +69,6 @@ config X86_REQUIRED_FEATURE_UP
config X86_REQUIRED_FEATURE_FPU
def_bool y
depends on !MATH_EMULATION
config X86_REQUIRED_FEATURE_PAE
def_bool y
-1
View File
@@ -275,7 +275,6 @@ archprepare: $(cpufeaturemasks.hdr)
libs-y += arch/x86/lib/
# drivers-y are linked after core-y
drivers-$(CONFIG_MATH_EMULATION) += arch/x86/math-emu/
drivers-$(CONFIG_PCI) += arch/x86/pci/
# suspend and hibernation support
-6
View File
@@ -10,7 +10,6 @@ else
align := -falign-functions=0 -falign-jumps=0 -falign-loops=0
endif
cflags-$(CONFIG_M586) += -march=i586
cflags-$(CONFIG_M586TSC) += -march=i586
cflags-$(CONFIG_M586MMX) += -march=pentium-mmx
cflags-$(CONFIG_M686) += -march=i686
@@ -24,16 +23,11 @@ cflags-$(CONFIG_MK6) += -march=k6
cflags-$(CONFIG_MK7) += -march=athlon
cflags-$(CONFIG_MCRUSOE) += -march=i686 $(align)
cflags-$(CONFIG_MEFFICEON) += -march=i686 $(call tune,pentium3) $(align)
cflags-$(CONFIG_MWINCHIPC6) += $(call cc-option,-march=winchip-c6,-march=i586)
cflags-$(CONFIG_MWINCHIP3D) += $(call cc-option,-march=winchip2,-march=i586)
cflags-$(CONFIG_MCYRIXIII) += $(call cc-option,-march=c3,-march=i486) $(align)
cflags-$(CONFIG_MVIAC3_2) += $(call cc-option,-march=c3-2,-march=i686)
cflags-$(CONFIG_MVIAC7) += -march=i686
cflags-$(CONFIG_MATOM) += -march=atom
# AMD Elan support
cflags-$(CONFIG_MELAN) += -march=i486
# Geode GX1 support
cflags-$(CONFIG_MGEODEGX1) += -march=pentium-mmx
cflags-$(CONFIG_MGEODE_LX) += $(call cc-option,-march=geode,-march=pentium-mmx)
+1
View File
@@ -2,6 +2,7 @@
#include "misc.h"
#include <asm/bootparam.h>
#include <asm/bootparam_utils.h>
#include <asm/cpuid/api.h>
#include <asm/e820/types.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
+1
View File
@@ -43,6 +43,7 @@
#include <asm/init.h>
#include <asm/setup.h>
#include <asm/sections.h>
#include <asm/cpuid/api.h>
#include <asm/coco.h>
#include <asm/sev.h>
+1
View File
@@ -14,6 +14,7 @@
#include <asm/ia32.h>
#include <asm/insn.h>
#include <asm/insn-eval.h>
#include <asm/cpuid/api.h>
#include <asm/paravirt_types.h>
#include <asm/pgtable.h>
#include <asm/set_memory.h>
+2
View File
@@ -8,8 +8,10 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <asm/apicdef.h>
#include <asm/apic.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include <asm/nmi.h>
+1
View File
@@ -15,6 +15,7 @@
#include <linux/sched/clock.h>
#include <asm/apic.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include "../perf_event.h"
+2
View File
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/perf_event.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include <asm/perf_event.h>
+3
View File
@@ -10,8 +10,11 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/perf_event.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include "../perf_event.h"
/* Event code: LSB 8 bits, passed in attr->config any other bit is reserved. */
+1
View File
@@ -16,6 +16,7 @@
#include <linux/smp.h>
#include <asm/perf_event.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#define NUM_COUNTERS_NB 4
+1
View File
@@ -17,6 +17,7 @@
#include <linux/kvm_host.h>
#include <asm/cpufeature.h>
#include <asm/cpuid/api.h>
#include <asm/debugreg.h>
#include <asm/hardirq.h>
#include <asm/intel-family.h>
+1
View File
@@ -4,6 +4,7 @@
#include <linux/types.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/perf_event.h>
#include <asm/msr.h>
+1
View File
@@ -13,6 +13,7 @@
#include <linux/nmi.h>
#include <asm/cpufeature.h>
#include <asm/cpuid/api.h>
#include <asm/hardirq.h>
#include <asm/apic.h>
#include <asm/msr.h>
+2
View File
@@ -2,6 +2,8 @@
#ifndef _ASM_X86_ACRN_H
#define _ASM_X86_ACRN_H
#include <asm/cpuid/api.h>
/*
* This CPUID returns feature bitmaps in EAX.
* Guest VM uses this to detect the appropriate feature bit.
-1
View File
@@ -125,7 +125,6 @@ t_no:
#define cpu_has_bug(c, bit) cpu_has(c, (bit))
#define set_cpu_bug(c, bit) set_cpu_cap(c, (bit))
#define clear_cpu_bug(c, bit) clear_cpu_cap(c, (bit))
#define static_cpu_has_bug(bit) static_cpu_has((bit))
#define boot_cpu_has_bug(bit) cpu_has_bug(&boot_cpu_data, (bit))
+250 -3
View File
@@ -7,6 +7,7 @@
#include <linux/build_bug.h>
#include <linux/types.h>
#include <asm/processor.h>
#include <asm/string.h>
/*
@@ -131,12 +132,12 @@ static inline void __cpuid_read(u32 leaf, u32 subleaf, u32 *regs)
__cpuid(regs + CPUID_EAX, regs + CPUID_EBX, regs + CPUID_ECX, regs + CPUID_EDX);
}
#define cpuid_subleaf(leaf, subleaf, regs) { \
#define cpuid_read_subleaf(leaf, subleaf, regs) { \
static_assert(sizeof(*(regs)) == 16); \
__cpuid_read(leaf, subleaf, (u32 *)(regs)); \
}
#define cpuid_leaf(leaf, regs) { \
#define cpuid_read(leaf, regs) { \
static_assert(sizeof(*(regs)) == 16); \
__cpuid_read(leaf, 0, (u32 *)(regs)); \
}
@@ -228,7 +229,7 @@ static inline u32 cpuid_base_hypervisor(const char *sig, u32 leaves)
*/
static inline void cpuid_leaf_0x2(union leaf_0x2_regs *regs)
{
cpuid_leaf(0x2, regs);
cpuid_read(0x2, regs);
/*
* All Intel CPUs must report an iteration count of 1. In case
@@ -289,4 +290,250 @@ static inline bool cpuid_amd_hygon_has_l3_cache(void)
return cpuid_edx(0x80000006);
}
/*
* 'struct cpuid_leaves' accessors (without sanity checks):
*
* For internal use by the CPUID parser.
*/
/* Return constified pointers for all call-site APIs */
#define __const_ptr(_ptr) \
((const __typeof__(*(_ptr)) *)(_ptr))
#define __cpuid_leaves_subleaf(_leaves, _leaf, _subleaf) \
__const_ptr(&((_leaves)->leaf_ ## _leaf ## _ ## _subleaf)[0])
#define __cpuid_leaves_subleaf_n(_leaves, _leaf, _index) \
__const_ptr(&((_leaves)->leaf_ ## _leaf ## _ ## n)[_index])
#define __cpuid_leaves_subleaf_info(_leaves, _leaf, _subleaf) \
__const_ptr(&((_leaves)->leaf_ ## _leaf ## _ ## _subleaf ## _ ## info))
/*
* 'struct cpuid_table' accessors (with sanity checks):
*
* For internal use by the CPUID parser.
*/
#define __cpuid_table_nr_filled_subleaves(_table, _leaf, _subleaf) \
__cpuid_leaves_subleaf_info(&((_table)->leaves), _leaf, _subleaf)->nr_entries
#define __cpuid_table_subleaf_range_size(_table, _leaf) \
ARRAY_SIZE((_table)->leaves.leaf_ ## _leaf ## _n)
#define __cpuid_table_invalid_subleaf(_table, _leaf, _subleaf) \
(((_subleaf) < (__cpuid_leaf_first_subleaf(_leaf))) || \
((_subleaf) > (__cpuid_leaf_first_subleaf(_leaf) + \
__cpuid_table_subleaf_range_size(_table, _leaf) - 1)))
/* Return NULL if the parser did not fill that leaf. Check cpuid_subleaf(). */
#define __cpuid_table_subleaf(_table, _leaf, _subleaf) \
({ \
unsigned int ____f = __cpuid_table_nr_filled_subleaves(_table, _leaf, _subleaf); \
\
(____f != 1) ? NULL : __cpuid_leaves_subleaf(&((_table)->leaves), _leaf, _subleaf); \
})
/*
* Return NULL if the CPUID parser did not fill this leaf, or if the given
* dynamic subleaf value is out of range. Check cpuid_subleaf_n().
*/
#define __cpuid_table_subleaf_n(_table, _leaf, _subleaf) \
({ \
unsigned int ____i = (_subleaf) - __cpuid_leaf_first_subleaf(_leaf); \
unsigned int ____f = __cpuid_table_nr_filled_subleaves(_table, _leaf, n); \
\
/* CPUID parser might not have filled the entire subleaf range */ \
((____i >= ____f) || __cpuid_table_invalid_subleaf(_table, _leaf, _subleaf)) ? \
NULL : __cpuid_leaves_subleaf_n(&((_table)->leaves), _leaf, ____i); \
})
/*
* Compile-time checks for leaves with a subleaf range:
*/
#define __cpuid_assert_subleaf_range(_cpuinfo, _leaf) \
static_assert(__cpuid_table_subleaf_range_size(&(_cpuinfo)->cpuid, _leaf) > 1)
#define __cpuid_assert_subleaf_within_range(_cpuinfo, _leaf, _subleaf) \
BUILD_BUG_ON(__builtin_constant_p(_subleaf) && \
__cpuid_table_invalid_subleaf(&(_cpuinfo)->cpuid, _leaf, _subleaf))
/*
* CPUID Parser Call-site APIs
*
* Call sites should use below APIs instead of invoking direct CPUID queries.
*
* Benefits include:
*
* - Return CPUID output as typed C structures that are auto-generated from a
* centralized database (see <asm/cpuid/leaf_types.h). Such data types have a
* full C99 bitfield layout per CPUID leaf/subleaf combination. Call sites
* can thus avoid doing ugly and cryptic bitwise operations on raw CPUID data.
*
* - Return cached, per-CPU, CPUID output. Below APIs do not invoke any CPUID
* queries, thus avoiding their side effects like serialization and VM exits.
* Call-site-specific hard coded constants and macros for caching CPUID query
* outputs can also be avoided.
*
* - Return sanitized CPUID data. Below APIs return NULL if the given CPUID
* leaf/subleaf input is not supported by hardware, or if the hardware CPUID
* output was deemed invalid by the CPUID parser. This centralizes all CPUID
* data sanitization in one place (the kernel's CPUID parser.)
*
* - A centralized global view of system CPUID data. Below APIs will reflect
* any kernel-enforced feature masking or overrides, unlike ad hoc parsing of
* raw CPUID output by drivers and individual call sites.
*/
/*
* Call-site APIs for CPUID leaves with a single subleaf:
*/
/**
* cpuid_subleaf() - Access parsed CPUID
* @_cpuinfo: CPU capability structure reference ('struct cpuinfo_x86')
* @_leaf: CPUID leaf, in compile-time 0xN format; e.g. 0x7, 0xf
* @_subleaf: CPUID subleaf, in compile-time decimal format; e.g. 0, 1, 3
*
* Returns a pointer to parsed CPUID output, from the CPUID table inside
* @_cpuinfo, as a <cpuid/leaf_types.h> data type: 'struct leaf_0xM_N', where
* 0xM is the token provided at @_leaf, and N is the token provided at
* @_subleaf; e.g. struct leaf_0x7_0.
*
* Returns NULL if the requested CPUID @_leaf/@_subleaf query output is not
* present at the parsed CPUID table inside @_cpuinfo. This can happen if:
*
* - The CPUID table inside @_cpuinfo has not yet been populated.
* - The CPUID table inside @_cpuinfo was populated, but the CPU does not
* implement the requested CPUID @_leaf/@_subleaf combination.
* - The CPUID table inside @_cpuinfo was populated, but the kernel's CPUID
* parser has predetermined that the requested CPUID @_leaf/@_subleaf
* hardware output is invalid or unsupported.
*
* Example usage::
*
* const struct leaf_0x7_0 *l7_0 = cpuid_subleaf(c, 0x7, 0);
* if (!l7_0) {
* // Handle error
* }
*
* const struct leaf_0x7_1 *l7_1 = cpuid_subleaf(c, 0x7, 1);
* if (!l7_1) {
* // Handle error
* }
*/
#define cpuid_subleaf(_cpuinfo, _leaf, _subleaf) \
__cpuid_table_subleaf(&(_cpuinfo)->cpuid, _leaf, _subleaf) \
/**
* cpuid_leaf() - Access parsed CPUID data
* @_cpuinfo: CPU capability structure reference ('struct cpuinfo_x86')
* @_leaf: CPUID leaf, in compile-time 0xN format; e.g. 0x0, 0x2, 0x80000000
*
* Similar to cpuid_subleaf(), but with a CPUID subleaf = 0.
*
* Example usage::
*
* const struct leaf_0x0_0 *l0 = cpuid_leaf(c, 0x0);
* if (!l0) {
* // Handle error
* }
*
* const struct leaf_0x80000000_0 *el0 = cpuid_leaf(c, 0x80000000);
* if (!el0) {
* // Handle error
* }
*/
#define cpuid_leaf(_cpuinfo, _leaf) \
cpuid_subleaf(_cpuinfo, _leaf, 0)
/**
* cpuid_leaf_raw() - Access parsed CPUID data in raw format
* @_cpuinfo: CPU capability structure reference ('struct cpuinfo_x86')
* @_leaf: CPUID leaf, in compile-time 0xN format
*
* Similar to cpuid_leaf(), but returns a raw 'struct cpuid_regs' pointer to
* the parsed CPUID data instead of a "typed" <asm/cpuid/leaf_types.h> pointer.
*/
#define cpuid_leaf_raw(_cpuinfo, _leaf) \
((const struct cpuid_regs *)(cpuid_leaf(_cpuinfo, _leaf)))
/*
* Call-site APIs for CPUID leaves with a subleaf range:
*/
/**
* cpuid_subleaf_n() - Access parsed CPUID data for leaf with a subleaf range
* @_cpuinfo: CPU capability structure reference ('struct cpuinfo_x86')
* @_leaf: CPUID leaf, in compile-time 0xN format; e.g. 0x4, 0x8000001d
* @_subleaf: Subleaf number, which can be passed dynamically. It must be smaller
* than cpuid_subleaf_count(@_cpuinfo, @_leaf).
*
* Build-time errors will be emitted in the following cases:
*
* - @_leaf has no subleaf range. Leaves with a subleaf range have an '_n' type
* suffix and are listed at <asm/cpuid/types.h> using the CPUID_LEAF_N() macro.
*
* - @_subleaf is known at compile-time but is out of range.
*
* Example usage::
*
* const struct leaf_0x4_n *l4;
*
* for (int i = 0; i < cpuid_subleaf_count(c, 0x4); i++) {
* l4 = cpuid_subleaf_n(c, 0x4, i);
* if (!l4) {
* // Handle error
* }
* ...
* }
*
* Beside the standard error situations detailed at cpuid_subleaf(), this
* macro will also return NULL if @_subleaf is out of the leaf's subleaf range.
*/
#define cpuid_subleaf_n(_cpuinfo, _leaf, _subleaf) \
({ \
__cpuid_assert_subleaf_range(_cpuinfo, _leaf); \
__cpuid_assert_subleaf_within_range(_cpuinfo, _leaf, _subleaf); \
__cpuid_table_subleaf_n(&(_cpuinfo)->cpuid, _leaf, _subleaf); \
})
/**
* cpuid_subleaf_n_raw() - Access parsed CPUID data for leaf with subleaf range
* @_cpuinfo: CPU capability structure reference ('struct cpuinfo_x86')
* @_leaf: CPUID leaf, in compile-time 0xN format; e.g. 0x4, 0x8000001d
* @_subleaf: Subleaf number, which can be passed dynamically. It must be smaller
* than cpuid_subleaf_count(@_cpuinfo, @_leaf).
*
* Similar to cpuid_subleaf_n(), but returns a raw 'struct cpuid_regs' pointer to
* the parsed CPUID data instead of a "typed" <asm/cpuid/leaf_types.h> pointer.
*/
#define cpuid_subleaf_n_raw(_cpuinfo, _leaf, _subleaf) \
((const struct cpuid_regs *)cpuid_subleaf_n(_cpuinfo, _leaf, _subleaf))
/**
* cpuid_subleaf_count() - Number of filled subleaves for @_leaf
* @_cpuinfo: CPU capability structure reference ('struct cpuinfo_x86')
* @_leaf: CPUID leaf, in compile-time 0xN format; e.g. 0x4, 0x8000001d
*
* Return the number of subleaves filled by the CPUID parser for @_leaf.
*
* @_leaf must have subleaf range. Leaves with a subleaf range have an '_n' type
* suffix and are listed at <asm/cpuid/types.h> using the CPUID_LEAF_N() macro.
*/
#define cpuid_subleaf_count(_cpuinfo, _leaf) \
({ \
__cpuid_assert_subleaf_range(_cpuinfo, _leaf); \
__cpuid_table_nr_filled_subleaves(&(_cpuinfo)->cpuid, _leaf, n); \
})
/*
* CPUID parser exported APIs:
*/
void cpuid_scan_cpu(struct cpuinfo_x86 *c);
void cpuid_refresh_leaf(struct cpuinfo_x86 *c, u32 leaf);
void cpuid_refresh_range(struct cpuinfo_x86 *c, u32 start, u32 end);
#endif /* _ASM_X86_CPUID_API_H */
File diff suppressed because it is too large Load Diff
+98
View File
@@ -5,6 +5,8 @@
#include <linux/build_bug.h>
#include <linux/types.h>
#include <asm/cpuid/leaf_types.h>
/*
* Types for raw CPUID access:
*/
@@ -30,6 +32,12 @@ enum cpuid_regs_idx {
#define CPUID_LEAF_FREQ 0x16
#define CPUID_LEAF_TILE 0x1d
#define CPUID_RANGE(idx) ((idx) & 0xffff0000)
#define CPUID_RANGE_MAX(idx) (CPUID_RANGE(idx) + 0xffff)
#define CPUID_BASE_START 0x00000000
#define CPUID_BASE_END CPUID_RANGE_MAX(CPUID_BASE_START)
/*
* Types for CPUID(0x2) parsing:
*/
@@ -124,4 +132,94 @@ extern const struct leaf_0x2_table cpuid_0x2_table[256];
*/
#define TLB_0x63_2M_4M_ENTRIES 32
/*
* Types for centralized CPUID tables:
*
* For internal use by the CPUID parser.
*/
/**
* struct leaf_parse_info - CPUID query parse info
* @nr_entries: Number of valid entries filled by the CPUID parser
*/
struct leaf_parse_info {
unsigned int nr_entries;
};
/**
* __CPUID_LEAF() - Define a CPUID output and parse info entry
* @_name: Struct type name of the CPUID leaf/subleaf (e.g. 'leaf_0x7_0'). Such
* types are defined at <cpuid/leaf_types.h> and follow the leaf_0xM_N
* format, where 0xM is the leaf and N is the subleaf.
* @_count: Number of storage entries to allocate for this leaf/subleaf.
*
* For a given leaf/subleaf, define an array of CPUID storage entries and an associated
* query info structure.
*
* Use an array of storage entries to accommodate CPUID leaves with multiple subleaves
* having the same output format. This is common for hierarchical enumeration; e.g.,
* CPUID(0x4), CPUID(0x12), and CPUID(0x8000001d).
*/
#define __CPUID_LEAF(_name, _count) \
struct _name _name[_count]; \
struct leaf_parse_info _name##_info
/**
* CPUID_LEAF() - Define a 'struct cpuid_leaves' storage entry
* @_leaf: Leaf number, in compile-time 0xN format
* @_subleaf: Subleaf number, in compile-time decimal format
*
* Convenience wrapper around __CPUID_LEAF().
*/
#define CPUID_LEAF(_leaf, _subleaf) \
__CPUID_LEAF(leaf_ ## _leaf ## _ ## _subleaf, 1)
#define __cpuid_leaf_first_subleaf(_l) \
LEAF_ ## _l ## _ ## SUBLEAF_N_FIRST
#define __cpuid_leaf_last_subleaf(_l) \
LEAF_ ## _l ## _ ## SUBLEAF_N_LAST
#define __cpuid_leaf_subleaf_count_min(_l) 2
#define __cpuid_leaf_subleaf_count_max(_l) \
(__cpuid_leaf_last_subleaf(_l) - __cpuid_leaf_first_subleaf(_l) + 1)
/**
* CPUID_LEAF_N() - Define a 'struct cpuid_leaves' storage entry
* @_leaf: Leaf number, in compile-time 0xN format
* @_count: Number of storage entries to allocate for that leaf. It must not exceed
* the limits defined at <cpuid/leaf_types.h>.
*
* Convenience wrapper around __CPUID_LEAF().
*/
#define CPUID_LEAF_N(_leaf, _count) \
static_assert(_count >= __cpuid_leaf_subleaf_count_min(_leaf)); \
static_assert(_count <= __cpuid_leaf_subleaf_count_max(_leaf)); \
__CPUID_LEAF(leaf_ ## _leaf ## _ ## n, _count)
/*
* struct cpuid_leaves - Parsed CPUID data
*/
struct cpuid_leaves {
/* Leaf Subleaf number (or max number of subleaves) */
CPUID_LEAF ( 0x0, 0 );
CPUID_LEAF ( 0x1, 0 );
};
/*
* Types for centralized CPUID tables:
*
* For external use.
*/
/**
* struct cpuid_table - Per-CPU CPUID data repository
* @leaves: Parsed CPUID queries output and their metadata
*
* This is to be embedded inside 'struct cpuinfo_x86' to provide parsed and
* sanitized CPUID data per CPU.
*/
struct cpuid_table {
struct cpuid_leaves leaves;
};
#endif /* _ASM_X86_CPUID_TYPES_H */
-6
View File
@@ -119,12 +119,6 @@ extern void fpu__init_system(void);
extern void fpu__init_check_bugs(void);
extern void fpu__resume_cpu(void);
#ifdef CONFIG_MATH_EMULATION
extern void fpstate_init_soft(struct swregs_state *soft);
#else
static inline void fpstate_init_soft(struct swregs_state *soft) {}
#endif
/* State tracking */
DECLARE_PER_CPU(bool, kernel_fpu_allowed);
DECLARE_PER_CPU(struct fpu *, fpu_fpregs_owner_ctx);
+2
View File
@@ -29,6 +29,7 @@
* _N,_P - other mobile parts
* _H - premium mobile parts
* _S - other client parts
* _R - ruggedized for harsh environment
*
* Historical OPTDIFFs:
*
@@ -151,6 +152,7 @@
#define INTEL_LUNARLAKE_M IFM(6, 0xBD) /* Lion Cove / Skymont */
#define INTEL_PANTHERLAKE_L IFM(6, 0xCC) /* Cougar Cove / Darkmont */
#define INTEL_PANTHERLAKE_R IFM(6, 0xE5) /* Cougar Cove / Darkmont */
#define INTEL_WILDCATLAKE_L IFM(6, 0xD5)
+1
View File
@@ -3,6 +3,7 @@
#define _ASM_X86_MICROCODE_H
#include <asm/msr.h>
#include <asm/cpuid/api.h>
struct cpu_signature {
unsigned int sig;
+2 -1
View File
@@ -16,7 +16,7 @@ struct vm86;
#include <uapi/asm/sigcontext.h>
#include <asm/current.h>
#include <asm/cpufeatures.h>
#include <asm/cpuid/api.h>
#include <asm/cpuid/types.h>
#include <asm/page.h>
#include <asm/pgtable_types.h>
#include <asm/percpu.h>
@@ -170,6 +170,7 @@ struct cpuinfo_x86 {
char x86_vendor_id[16];
char x86_model_id[64];
struct cpuinfo_topology topo;
struct cpuid_table cpuid;
/* in KB - valid for CPUS which support this call: */
unsigned int x86_cache_size;
int x86_cache_alignment; /* In bytes */
+1 -2
View File
@@ -7,8 +7,7 @@
static inline unsigned long random_get_entropy(void)
{
if (!IS_ENABLED(CONFIG_X86_TSC) &&
!cpu_feature_enabled(X86_FEATURE_TSC))
if (!cpu_feature_enabled(X86_FEATURE_TSC))
return random_get_entropy_fallback();
return rdtsc();
}
-8
View File
@@ -5,17 +5,9 @@
#include <linux/compiler.h>
#include <linux/types.h>
#ifdef CONFIG_X86_TSC
extern u64 notrace trace_clock_x86_tsc(void);
# define ARCH_TRACE_CLOCKS \
{ trace_clock_x86_tsc, "x86-tsc", .in_ns = 0 },
#else /* !CONFIG_X86_TSC */
#define ARCH_TRACE_CLOCKS
#endif
#endif /* _ASM_X86_TRACE_CLOCK_H */
+1 -12
View File
@@ -76,8 +76,7 @@ extern void disable_TSC(void);
static inline cycles_t get_cycles(void)
{
if (!IS_ENABLED(CONFIG_X86_TSC) &&
!cpu_feature_enabled(X86_FEATURE_TSC))
if (!cpu_feature_enabled(X86_FEATURE_TSC))
return 0;
return rdtsc();
}
@@ -94,25 +93,15 @@ extern unsigned long native_calibrate_tsc(void);
extern unsigned long long native_sched_clock_from_tsc(u64 tsc);
extern int tsc_clocksource_reliable;
#ifdef CONFIG_X86_TSC
extern bool tsc_async_resets;
#else
# define tsc_async_resets false
#endif
/*
* Boot-time check whether the TSCs are synchronized across
* all CPUs/cores:
*/
#ifdef CONFIG_X86_TSC
extern bool tsc_store_and_check_tsc_adjust(bool bootcpu);
extern void tsc_verify_tsc_adjust(bool resume);
extern void check_tsc_sync_target(void);
#else
static inline bool tsc_store_and_check_tsc_adjust(bool bootcpu) { return false; }
static inline void tsc_verify_tsc_adjust(bool resume) { }
static inline void check_tsc_sync_target(void) { }
#endif
extern int notsc_setup(char *);
extern void tsc_save_sched_clock_state(void);
-6
View File
@@ -5,8 +5,6 @@
#ifdef CONFIG_X86_64
/* X86_64 does not define MODULE_PROC_FAMILY */
#elif defined CONFIG_M586
#define MODULE_PROC_FAMILY "586 "
#elif defined CONFIG_M586TSC
#define MODULE_PROC_FAMILY "586TSC "
#elif defined CONFIG_M586MMX
@@ -31,10 +29,6 @@
#define MODULE_PROC_FAMILY "CRUSOE "
#elif defined CONFIG_MEFFICEON
#define MODULE_PROC_FAMILY "EFFICEON "
#elif defined CONFIG_MWINCHIPC6
#define MODULE_PROC_FAMILY "WINCHIPC6 "
#elif defined CONFIG_MWINCHIP3D
#define MODULE_PROC_FAMILY "WINCHIP3D "
#elif defined CONFIG_MCYRIXIII
#define MODULE_PROC_FAMILY "CYRIXIII "
#elif defined CONFIG_MVIAC3_2
+1
View File
@@ -37,6 +37,7 @@ extern struct shared_info *HYPERVISOR_shared_info;
extern struct start_info *xen_start_info;
#include <asm/bug.h>
#include <asm/cpuid/api.h>
#include <asm/processor.h>
#define XEN_SIGNATURE "XenVMMXenVMM"
+2 -2
View File
@@ -108,7 +108,7 @@ apm-y := apm_32.o
obj-$(CONFIG_APM) += apm.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SMP) += smpboot.o
obj-$(CONFIG_X86_TSC) += tsc_sync.o
obj-y += tsc_sync.o
obj-$(CONFIG_SMP) += setup_percpu.o
obj-$(CONFIG_X86_MPPARSE) += mpparse.o
obj-y += apic/
@@ -117,7 +117,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_TRACER) += ftrace_$(BITS).o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
obj-$(CONFIG_X86_TSC) += trace_clock.o
obj-y += trace_clock.o
obj-$(CONFIG_TRACING) += trace.o
obj-$(CONFIG_RETHOOK) += rethook.o
obj-$(CONFIG_VMCORE_INFO) += vmcore_info_$(BITS).o
+1
View File
@@ -64,6 +64,7 @@
#include <asm/tsc.h>
#include <asm/hypervisor.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/intel-family.h>
#include <asm/irq_regs.h>
#include <asm/cpu.h>
+1
View File
@@ -19,6 +19,7 @@ KCSAN_SANITIZE_common.o := n
obj-y := cacheinfo.o scattered.o
obj-y += topology_common.o topology_ext.o topology_amd.o
obj-y += cpuid_parser.o
obj-y += common.o
obj-y += rdrand.o
obj-y += match.o
+1
View File
@@ -16,6 +16,7 @@
#include <asm/cacheinfo.h>
#include <asm/cpu.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/spec-ctrl.h>
#include <asm/smp.h>
#include <asm/numa.h>
+1
View File
@@ -5,6 +5,7 @@
#include <asm/cpu.h>
#include <asm/cpufeature.h>
#include <asm/cpuid/api.h>
#include <asm/e820/api.h>
#include <asm/mtrr.h>
#include <asm/msr.h>
+4 -8
View File
@@ -1737,13 +1737,6 @@ static void __init cpu_parse_early_param(void)
int arglen;
#ifdef CONFIG_X86_32
if (cmdline_find_option_bool(boot_command_line, "no387"))
#ifdef CONFIG_MATH_EMULATION
setup_clear_cpu_cap(X86_FEATURE_FPU);
#else
pr_err("Option 'no387' required CONFIG_MATH_EMULATION enabled.\n");
#endif
if (cmdline_find_option_bool(boot_command_line, "nofxsr"))
setup_clear_cpu_cap(X86_FEATURE_FXSR);
#endif
@@ -1791,6 +1784,7 @@ static void __init cpu_parse_early_param(void)
static void __init early_identify_cpu(struct cpuinfo_x86 *c)
{
memset(&c->x86_capability, 0, sizeof(c->x86_capability));
memset(&c->cpuid, 0, sizeof(c->cpuid));
c->extended_cpuid_level = 0;
if (!cpuid_feature())
@@ -1798,6 +1792,7 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
/* cyrix could have cpuid enabled via c_identify()*/
if (cpuid_feature()) {
cpuid_scan_cpu(c);
cpu_detect(c);
get_cpu_vendor(c);
intel_unlock_cpuid_leafs(c);
@@ -1970,8 +1965,8 @@ static void generic_identify(struct cpuinfo_x86 *c)
if (!cpuid_feature())
return;
cpuid_scan_cpu(c);
cpu_detect(c);
get_cpu_vendor(c);
intel_unlock_cpuid_leafs(c);
get_cpu_cap(c);
@@ -2023,6 +2018,7 @@ static void identify_cpu(struct cpuinfo_x86 *c)
#endif
c->x86_cache_alignment = c->x86_clflush_size;
memset(&c->x86_capability, 0, sizeof(c->x86_capability));
memset(&c->cpuid, 0, sizeof(c->cpuid));
#ifdef CONFIG_X86_VMX_FEATURE_NAMES
memset(&c->vmx_capability, 0, sizeof(c->vmx_capability));
#endif
+182
View File
@@ -0,0 +1,182 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CPUID parser; for populating the system's CPUID tables.
*/
#include <linux/kernel.h>
#include <asm/cpuid/api.h>
#include <asm/processor.h>
#include "cpuid_parser.h"
/* Clear a single CPUID table entry */
static void cpuid_clear(const struct cpuid_parse_entry *e, const struct cpuid_read_output *output)
{
struct cpuid_regs *regs = output->regs;
for (int i = 0; i < e->maxcnt; i++, regs++)
memset(regs, 0, sizeof(*regs));
memset(output->info, 0, sizeof(*output->info));
}
/*
* Leaf read functions:
*/
/*
* Default CPUID read function
* Satisfies the requirements stated at 'struct cpuid_parse_entry'->read().
*/
static void
cpuid_read_generic(const struct cpuid_parse_entry *e, const struct cpuid_read_output *output)
{
struct cpuid_regs *regs = output->regs;
for (int i = 0; i < e->maxcnt; i++, regs++, output->info->nr_entries++)
cpuid_read_subleaf(e->leaf, e->subleaf + i, regs);
}
/*
* CPUID parser table:
*/
static const struct cpuid_parse_entry cpuid_parse_entries[] = {
CPUID_PARSE_ENTRIES
};
/*
* Leaf-independent parser code:
*/
static unsigned int cpuid_range_max_leaf(const struct cpuid_table *t, unsigned int range)
{
const struct leaf_0x0_0 *l0 = __cpuid_table_subleaf(t, 0x0, 0);
switch (range) {
case CPUID_BASE_START: return l0 ? l0->max_std_leaf : 0;
default: return 0;
}
}
static void
__cpuid_reset_table(struct cpuid_table *t, const struct cpuid_parse_entry entries[],
unsigned int nr_entries, unsigned int start, unsigned int end, bool fill)
{
const struct cpuid_parse_entry *entry = entries;
unsigned int range = CPUID_RANGE(start);
for (unsigned int i = 0; i < nr_entries; i++, entry++) {
struct cpuid_read_output output = {
.regs = cpuid_table_regs_p(t, entry->regs_offs),
.info = cpuid_table_info_p(t, entry->info_offs),
};
if (entry->leaf < start || entry->leaf > end)
continue;
cpuid_clear(entry, &output);
/*
* Read the range's anchor leaf unconditionally so that the cached
* maximum valid leaf value is available for the remaining entries.
*/
if (fill && (entry->leaf == range || entry->leaf <= cpuid_range_max_leaf(t, range)))
entry->read(entry, &output);
}
}
/*
* Zero all cached CPUID entries within [@start-@end] range. This is needed when
* certain operations like MSR writes induce changes to the CPU's CPUID layout.
*/
static void
__cpuid_zero_table(struct cpuid_table *t, const struct cpuid_parse_entry entries[],
unsigned int nr_entries, unsigned int start, unsigned int end)
{
__cpuid_reset_table(t, entries, nr_entries, start, end, false);
}
static void
__cpuid_fill_table(struct cpuid_table *t, const struct cpuid_parse_entry entries[],
unsigned int nr_entries, unsigned int start, unsigned int end)
{
__cpuid_reset_table(t, entries, nr_entries, start, end, true);
}
static void
cpuid_fill_table(struct cpuid_table *t, const struct cpuid_parse_entry entries[], unsigned int nr_entries)
{
static const struct {
unsigned int start;
unsigned int end;
} ranges[] = {
{ CPUID_BASE_START, CPUID_BASE_END },
};
for (unsigned int i = 0; i < ARRAY_SIZE(ranges); i++)
__cpuid_fill_table(t, entries, nr_entries, ranges[i].start, ranges[i].end);
}
static void __cpuid_scan_cpu_full(struct cpuinfo_x86 *c)
{
unsigned int nr_entries = ARRAY_SIZE(cpuid_parse_entries);
struct cpuid_table *table = &c->cpuid;
cpuid_fill_table(table, cpuid_parse_entries, nr_entries);
}
static void
__cpuid_scan_cpu_partial(struct cpuinfo_x86 *c, unsigned int start_leaf, unsigned int end_leaf)
{
unsigned int nr_entries = ARRAY_SIZE(cpuid_parse_entries);
struct cpuid_table *table = &c->cpuid;
__cpuid_zero_table(table, cpuid_parse_entries, nr_entries, start_leaf, end_leaf);
__cpuid_fill_table(table, cpuid_parse_entries, nr_entries, start_leaf, end_leaf);
}
/*
* Call-site APIs:
*/
/**
* cpuid_scan_cpu() - Populate current CPU's CPUID table
* @c: CPU capability structure associated with the current CPU
*
* Populate the CPUID table embedded within @c with parsed CPUID data. All CPUID
* instructions are invoked locally, so this must be called on the CPU associated
* with @c.
*/
void cpuid_scan_cpu(struct cpuinfo_x86 *c)
{
__cpuid_scan_cpu_full(c);
}
/**
* cpuid_refresh_range() - Rescan a CPUID table's leaf range
* @c: CPU capability structure associated with the current CPU
* @start: Start of leaf range to be re-scanned
* @end: End of leaf range
*/
void cpuid_refresh_range(struct cpuinfo_x86 *c, u32 start, u32 end)
{
if (WARN_ON_ONCE(start > end))
return;
if (WARN_ON_ONCE(CPUID_RANGE(start) != CPUID_RANGE(end)))
return;
__cpuid_scan_cpu_partial(c, start, end);
}
/**
* cpuid_refresh_leaf() - Rescan a CPUID table's leaf
* @c: CPU capability structure associated with the current CPU
* @leaf: Leaf to be re-scanned
*/
void cpuid_refresh_leaf(struct cpuinfo_x86 *c, u32 leaf)
{
cpuid_refresh_range(c, leaf, leaf);
}
+120
View File
@@ -0,0 +1,120 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ARCH_X86_CPUID_PARSER_H
#define _ARCH_X86_CPUID_PARSER_H
#include <asm/cpuid/types.h>
/*
* Since accessing the CPUID leaves at 'struct cpuid_leaves' require compile time
* tokenization, split the CPUID parser into two stages: compile time macros for
* tokenizing the leaf/subleaf output offsets within the table, and generic runtime
* code to write to the relevant CPUID leaves using such offsets.
*
* The output of the compile time macros is cached by a compile time "parse entry"
* table (see 'struct cpuid_parse_entry'). The runtime parser code will utilize
* such offsets by passing them to the cpuid_table_*_p() functions.
*/
/*
* Compile time CPUID table offset calculations:
*
* @_leaf: CPUID leaf, in 0xN format
* @_subleaf: CPUID subleaf, in decimal format
*/
#define __cpuid_leaves_regs_offset(_leaf, _subleaf) \
offsetof(struct cpuid_leaves, leaf_ ## _leaf ## _ ## _subleaf)
#define __cpuid_leaves_info_offset(_leaf, _subleaf) \
offsetof(struct cpuid_leaves, leaf_ ## _leaf ## _ ## _subleaf ## _ ## info)
#define __cpuid_leaves_regs_maxcnt(_leaf, _subleaf) \
ARRAY_SIZE(((struct cpuid_leaves *)NULL)->leaf_ ## _leaf ## _ ## _subleaf)
/*
* Translation of compile time offsets to generic runtime pointers:
*/
static inline struct cpuid_regs *
cpuid_table_regs_p(const struct cpuid_table *t, unsigned long regs_offset)
{
return (struct cpuid_regs *)((unsigned long)(&t->leaves) + regs_offset);
}
static inline struct leaf_parse_info *
cpuid_table_info_p(const struct cpuid_table *t, unsigned long info_offset)
{
return (struct leaf_parse_info *)((unsigned long)(&t->leaves) + info_offset);
}
/**
* struct cpuid_read_output - Output of a CPUID read operation
* @regs: Pointer to an array of CPUID outputs, where each array element covers the
* full EAX->EDX output range.
* @info: Pointer to query info; for saving the number of filled elements at @regs.
*
* A CPUID parser read function like cpuid_read_generic() or cpuid_read_0xN() uses this
* structure to save the CPUID query outputs. Actual storage for @regs and @info is
* provided by the read function caller, and is typically within the CPU's CPUID table.
*
* See struct cpuid_parse_entry.read().
*/
struct cpuid_read_output {
struct cpuid_regs *regs;
struct leaf_parse_info *info;
};
/**
* struct cpuid_parse_entry - CPUID parse table entry
* @leaf: Leaf number to be parsed
* @subleaf: Subleaf number to be parsed
* @regs_offs: Offset within 'struct cpuid_leaves' for saving the CPUID query output; to be
* passed to cpuid_table_regs_p().
* @info_offs: Offset within 'struct cpuid_leaves' for saving the CPUID query parse info; to be
* passed to cpuid_table_info_p().
* @maxcnt: Maximum number of output storage entries available for the CPUID query.
* @read: Read function for this entry. It must save the parsed CPUID output to the passed
* 'struct cpuid_read_output'->regs array of size >= @maxcnt. It must set
* 'struct cpuid_read_output'->info.nr_entries to the number of CPUID output entries
* parsed and filled. A generic implementation is provided at cpuid_read_generic().
*/
struct cpuid_parse_entry {
unsigned int leaf;
unsigned int subleaf;
unsigned int regs_offs;
unsigned int info_offs;
unsigned int maxcnt;
void (*read)(const struct cpuid_parse_entry *e, const struct cpuid_read_output *o);
};
#define __CPUID_PARSE_ENTRY(_leaf, _subleaf, _suffix, _reader_fn) \
{ \
.leaf = _leaf, \
.subleaf = _subleaf, \
.regs_offs = __cpuid_leaves_regs_offset(_leaf, _suffix), \
.info_offs = __cpuid_leaves_info_offset(_leaf, _suffix), \
.maxcnt = __cpuid_leaves_regs_maxcnt(_leaf, _suffix), \
.read = cpuid_read_ ## _reader_fn, \
}
/*
* CPUID_PARSE_ENTRY_N() is for parsing CPUID leaves with a subleaf range.
* Check <asm/cpuid/types.h> __CPUID_LEAF() vs. CPUID_LEAF_N().
*/
#define CPUID_PARSE_ENTRY(_leaf, _subleaf, _reader_fn) \
__CPUID_PARSE_ENTRY(_leaf, _subleaf, _subleaf, _reader_fn)
#define CPUID_PARSE_ENTRY_N(_leaf, _reader_fn) \
__CPUID_PARSE_ENTRY(_leaf, __cpuid_leaf_first_subleaf(_leaf), n, _reader_fn)
/*
* CPUID parser table:
*/
#define CPUID_PARSE_ENTRIES \
/* Leaf Subleaf Reader function */ \
CPUID_PARSE_ENTRY ( 0x0, 0, generic ), \
CPUID_PARSE_ENTRY ( 0x1, 0, generic ), \
#endif /* _ARCH_X86_CPUID_PARSER_H */
+1
View File
@@ -10,6 +10,7 @@
#include <asm/apic.h>
#include <asm/cpu.h>
#include <asm/cpuid/api.h>
#include <asm/smp.h>
#include <asm/numa.h>
#include <asm/cacheinfo.h>
+3 -12
View File
@@ -388,24 +388,15 @@ __setup("forcepae", forcepae_setup);
static void intel_workarounds(struct cpuinfo_x86 *c)
{
#ifdef CONFIG_X86_F00F_BUG
/*
* All models of Pentium and Pentium with MMX technology CPUs
* have the F0 0F bug, which lets nonprivileged users lock up the
* system. Announce that the fault handler will be checking for it.
* system. The fault handler always checks for it.
* The Quark is also family 5, but does not have the same bug.
*/
clear_cpu_bug(c, X86_BUG_F00F);
if (c->x86_vfm >= INTEL_FAM5_START && c->x86_vfm < INTEL_QUARK_X1000) {
static int f00f_workaround_enabled;
if (IS_ENABLED(CONFIG_X86_F00F_BUG) &&
(c->x86_vfm >= INTEL_FAM5_START && c->x86_vfm < INTEL_QUARK_X1000))
set_cpu_bug(c, X86_BUG_F00F);
if (!f00f_workaround_enabled) {
pr_notice("Intel Pentium with F0 0F bug - workaround enabled.\n");
f00f_workaround_enabled = 1;
}
}
#endif
/*
* SEP CPUID bug: Pentium Pro reports SEP but doesn't have it until
+1
View File
@@ -49,6 +49,7 @@
#include <asm/fred.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/processor.h>
#include <asm/traps.h>
#include <asm/tlbflush.h>
+1
View File
@@ -26,6 +26,7 @@
#include <asm/amd/nb.h>
#include <asm/apic.h>
#include <asm/cpuid/api.h>
#include <asm/irq_vectors.h>
#include <asm/mce.h>
#include <asm/msr.h>
+1
View File
@@ -34,6 +34,7 @@
#include <asm/microcode.h>
#include <asm/processor.h>
#include <asm/cpuid/api.h>
#include <asm/cmdline.h>
#include <asm/setup.h>
#include <asm/cpu.h>
+1
View File
@@ -34,6 +34,7 @@
#include <asm/apic.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/perf_event.h>
#include <asm/processor.h>
#include <asm/cmdline.h>
+1
View File
@@ -25,6 +25,7 @@
#include <linux/mm.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/processor.h>
#include <asm/tlbflush.h>
#include <asm/setup.h>
+1
View File
@@ -19,6 +19,7 @@
#include <linux/random.h>
#include <asm/processor.h>
#include <asm/hypervisor.h>
#include <asm/cpuid/api.h>
#include <hyperv/hvhdk.h>
#include <asm/mshyperv.h>
#include <asm/desc.h>
+1
View File
@@ -22,6 +22,7 @@
#include <linux/cpuhotplug.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include <asm/resctrl.h>
#include "internal.h"
+1
View File
@@ -21,6 +21,7 @@
#include <linux/resctrl.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include "internal.h"
+1
View File
@@ -6,6 +6,7 @@
#include <asm/memtype.h>
#include <asm/apic.h>
#include <asm/cpuid/api.h>
#include <asm/processor.h>
#include "cpu.h"
+3
View File
@@ -6,7 +6,10 @@
#include <linux/mman.h>
#include <linux/security.h>
#include <linux/suspend.h>
#include <asm/cpuid/api.h>
#include <asm/traps.h>
#include "driver.h"
#include "encl.h"
+3
View File
@@ -15,9 +15,12 @@
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/vmalloc.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include <asm/sgx.h>
#include <asm/archrandom.h>
#include "driver.h"
#include "encl.h"
#include "encls.h"
+2 -1
View File
@@ -2,6 +2,7 @@
#include <linux/cpu.h>
#include <asm/apic.h>
#include <asm/cpuid/api.h>
#include <asm/memtype.h>
#include <asm/msr.h>
#include <asm/processor.h>
@@ -79,7 +80,7 @@ static bool parse_8000_001e(struct topo_scan *tscan)
if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
return false;
cpuid_leaf(0x8000001e, &leaf);
cpuid_read(0x8000001e, &leaf);
/*
* If leaf 0xb/0x26 is available, then the APIC ID and the domain
+1
View File
@@ -6,6 +6,7 @@
#include <asm/intel-family.h>
#include <asm/apic.h>
#include <asm/processor.h>
#include <asm/cpuid/api.h>
#include <asm/smp.h>
#include "cpu.h"
+2 -1
View File
@@ -2,6 +2,7 @@
#include <linux/cpu.h>
#include <asm/apic.h>
#include <asm/cpuid/api.h>
#include <asm/memtype.h>
#include <asm/processor.h>
@@ -70,7 +71,7 @@ static inline bool topo_subleaf(struct topo_scan *tscan, u32 leaf, u32 subleaf,
default: return false;
}
cpuid_subleaf(leaf, subleaf, &sl);
cpuid_read_subleaf(leaf, subleaf, &sl);
if (!sl.num_processors || sl.type == INVALID_TYPE)
return false;
+3
View File
@@ -3,8 +3,11 @@
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <linux/mm.h>
#include <asm/cpufeature.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include "cpu.h"
static void early_init_transmeta(struct cpuinfo_x86 *c)
-26
View File
@@ -1,26 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <asm/processor.h>
#include "cpu.h"
/*
* UMC chips appear to be only either 386 or 486,
* so no special init takes place.
*/
static const struct cpu_dev umc_cpu_dev = {
.c_vendor = "UMC",
.c_ident = { "UMC UMC UMC" },
.legacy_models = {
{ .family = 4, .model_names =
{
[1] = "U5D",
[2] = "U5S",
}
},
},
.c_x86_vendor = X86_VENDOR_UMC,
};
cpu_dev_register(umc_cpu_dev);
+1
View File
@@ -33,6 +33,7 @@
#include <asm/div64.h>
#include <asm/x86_init.h>
#include <asm/hypervisor.h>
#include <asm/cpuid/api.h>
#include <asm/timer.h>
#include <asm/apic.h>
#include <asm/vmware.h>
+1
View File
@@ -4,6 +4,7 @@
#include <asm/cpu.h>
#include <asm/cpufeature.h>
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include "cpu.h"
+3 -3
View File
@@ -37,6 +37,7 @@
#include <linux/gfp.h>
#include <linux/completion.h>
#include <asm/cpuid/api.h>
#include <asm/processor.h>
#include <asm/msr.h>
@@ -58,8 +59,7 @@ static void cpuid_smp_cpuid(void *cmd_block)
complete(&cmd->done);
}
static ssize_t cpuid_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
static ssize_t cpuid_read_f(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
char __user *tmp = buf;
struct cpuid_regs_done cmd;
@@ -119,7 +119,7 @@ static int cpuid_open(struct inode *inode, struct file *file)
static const struct file_operations cpuid_fops = {
.owner = THIS_MODULE,
.llseek = no_seek_end_llseek,
.read = cpuid_read,
.read = cpuid_read_f,
.open = cpuid_open,
};
-5
View File
@@ -558,11 +558,6 @@ static inline void fpstate_init_fstate(struct fpstate *fpstate)
*/
void fpstate_init_user(struct fpstate *fpstate)
{
if (!cpu_feature_enabled(X86_FEATURE_FPU)) {
fpstate_init_soft(&fpstate->regs.soft);
return;
}
xstate_init_xcomp_bv(&fpstate->regs.xsave, fpstate->xfeatures);
if (cpu_feature_enabled(X86_FEATURE_FXSR))
+1 -8
View File
@@ -36,12 +36,7 @@ static void fpu__init_cpu_generic(void)
write_cr0(cr0);
/* Flush out any pending x87 state: */
#ifdef CONFIG_MATH_EMULATION
if (!boot_cpu_has(X86_FEATURE_FPU))
;
else
#endif
asm volatile ("fninit");
asm volatile ("fninit");
}
/*
@@ -86,13 +81,11 @@ static void __init fpu__init_system_early_generic(void)
setup_clear_cpu_cap(X86_FEATURE_FPU);
}
#ifndef CONFIG_MATH_EMULATION
if (!test_cpu_cap(&boot_cpu_data, X86_FEATURE_FPU)) {
pr_emerg("x86/fpu: Giving up, no FPU found and no math emulation present\n");
for (;;)
asm volatile("hlt");
}
#endif
}
/*
+1 -1
View File
@@ -31,7 +31,7 @@ struct clock_event_device *global_clock_event;
*/
static bool __init use_pit(void)
{
if (!IS_ENABLED(CONFIG_X86_TSC) || !boot_cpu_has(X86_FEATURE_TSC))
if (!boot_cpu_has(X86_FEATURE_TSC))
return true;
/* This also returns true when APIC is disabled */
+1
View File
@@ -17,6 +17,7 @@
#include <asm/io_apic.h>
#include <asm/acpi.h>
#include <asm/cpu.h>
#include <asm/cpuid/api.h>
#include <asm/hypervisor.h>
#include <asm/i8259.h>
#include <asm/irqdomain.h>
+1
View File
@@ -41,6 +41,7 @@
#include <asm/hypervisor.h>
#include <asm/mtrr.h>
#include <asm/tlb.h>
#include <asm/cpuid/api.h>
#include <asm/cpuidle_haltpoll.h>
#include <asm/msr.h>
#include <asm/ptrace.h>
+1
View File
@@ -24,6 +24,7 @@
#include <asm/time.h>
#include <asm/pgalloc.h>
#include <asm/irq.h>
#include <asm/cpuid/api.h>
#include <asm/delay.h>
#include <asm/fixmap.h>
#include <asm/apic.h>
-21
View File
@@ -1475,13 +1475,6 @@ DEFINE_IDTENTRY(exc_coprocessor_error)
DEFINE_IDTENTRY(exc_simd_coprocessor_error)
{
if (IS_ENABLED(CONFIG_X86_INVD_BUG)) {
/* AMD 486 bug: INVD in CPL 0 raises #XF instead of #GP */
if (!static_cpu_has(X86_FEATURE_XMM)) {
__exc_general_protection(regs, 0);
return;
}
}
math_error(regs, X86_TRAP_XF);
}
@@ -1550,20 +1543,6 @@ DEFINE_IDTENTRY(exc_device_not_available)
if (handle_xfd_event(regs))
return;
#ifdef CONFIG_MATH_EMULATION
if (!boot_cpu_has(X86_FEATURE_FPU) && (cr0 & X86_CR0_EM)) {
struct math_emu_info info = { };
cond_local_irq_enable(regs);
info.regs = regs;
math_emulate(&info);
cond_local_irq_disable(regs);
return;
}
#endif
/* This should not happen. */
if (WARN(cr0 & X86_CR0_TS, "CR0.TS was set")) {
/* Try to fix it up and carry on. */
-13
View File
@@ -298,30 +298,17 @@ notrace u64 sched_clock(void)
preempt_enable_notrace();
return now;
}
int check_tsc_unstable(void)
{
return tsc_unstable;
}
EXPORT_SYMBOL_GPL(check_tsc_unstable);
#ifdef CONFIG_X86_TSC
int __init notsc_setup(char *str)
{
mark_tsc_unstable("boot parameter notsc");
return 1;
}
#else
/*
* disable flag for tsc. Takes effect by clearing the TSC cpu flag
* in cpu/common.c
*/
int __init notsc_setup(char *str)
{
setup_clear_cpu_cap(X86_FEATURE_TSC);
return 1;
}
#endif
__setup("notsc", notsc_setup);
enum {
+1
View File
@@ -52,6 +52,7 @@
#include <asm/page.h>
#include <asm/memtype.h>
#include <asm/cmpxchg.h>
#include <asm/cpuid/api.h>
#include <asm/io.h>
#include <asm/set_memory.h>
#include <asm/spec-ctrl.h>
+1
View File
@@ -15,6 +15,7 @@
#include "x86.h"
#include "spte.h"
#include <asm/cpuid/api.h>
#include <asm/e820/api.h>
#include <asm/memtype.h>
#include <asm/vmx.h>
+2
View File
@@ -3,8 +3,10 @@
#define ARCH_X86_KVM_REVERSE_CPUID_H
#include <uapi/asm/kvm.h>
#include <asm/cpufeature.h>
#include <asm/cpufeatures.h>
#include <asm/cpuid/types.h>
/*
* Define a KVM-only feature flag.
+1
View File
@@ -23,6 +23,7 @@
#include <asm/pkru.h>
#include <asm/trapnr.h>
#include <asm/cpuid/api.h>
#include <asm/fpu/xcr.h>
#include <asm/fpu/xstate.h>
#include <asm/debugreg.h>
+1
View File
@@ -41,6 +41,7 @@
#include <asm/irq_remapping.h>
#include <asm/spec-ctrl.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/traps.h>
#include <asm/reboot.h>
#include <asm/fpu/api.h>
+1
View File
@@ -15,6 +15,7 @@
#include <linux/perf_event.h>
#include <asm/msr.h>
#include <asm/perf_event.h>
#include <asm/cpuid/api.h>
#include "x86.h"
#include "cpuid.h"
#include "lapic.h"
+1
View File
@@ -2,6 +2,7 @@
/* Copyright(c) 2021 Intel Corporation. */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/cpuid/api.h>
#include <asm/msr.h>
#include <asm/sgx.h>
+1
View File
@@ -33,6 +33,7 @@
#include <asm/asm.h>
#include <asm/cpu.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
#include <asm/debugreg.h>
#include <asm/desc.h>
#include <asm/fpu/api.h>
-30
View File
@@ -1,30 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for wm-FPU-emu
#
#DEBUG = -DDEBUGGING
DEBUG =
PARANOID = -DPARANOID
ccflags-y += $(PARANOID) $(DEBUG) -fno-builtin $(MATH_EMULATION)
asflags-y += $(PARANOID)
# From 'C' language sources:
C_OBJS =fpu_entry.o errors.o \
fpu_arith.o fpu_aux.o fpu_etc.o fpu_tags.o fpu_trig.o \
load_store.o get_address.o \
poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \
reg_add_sub.o reg_compare.o reg_constant.o reg_convert.o \
reg_ld_str.o reg_divide.o reg_mul.o
# From 80x86 assembler sources:
A_OBJS =reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \
div_small.o reg_norm.o reg_round.o \
wm_shrx.o wm_sqrt.o \
div_Xsig.o polynom_Xsig.o round_Xsig.o \
shr_Xsig.o mul_Xsig.o
obj-y =$(C_OBJS) $(A_OBJS)
proto:
cproto -e -DMAKING_PROTO *.c >fpu_proto.h
-427
View File
@@ -1,427 +0,0 @@
+---------------------------------------------------------------------------+
| wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. |
| |
| Copyright (C) 1992,1993,1994,1995,1996,1997,1999 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@melbpc.org.au |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License version 2 as |
| published by the Free Software Foundation. |
| |
| This program 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 General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| |
+---------------------------------------------------------------------------+
wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387
which was my 80387 emulator for early versions of djgpp (gcc under
msdos); wm-emu387 was in turn based upon emu387 which was written by
DJ Delorie for djgpp. The interface to the Linux kernel is based upon
the original Linux math emulator by Linus Torvalds.
My target FPU for wm-FPU-emu is that described in the Intel486
Programmer's Reference Manual (1992 edition). Unfortunately, numerous
facets of the functioning of the FPU are not well covered in the
Reference Manual. The information in the manual has been supplemented
with measurements on real 80486's. Unfortunately, it is simply not
possible to be sure that all of the peculiarities of the 80486 have
been discovered, so there is always likely to be obscure differences
in the detailed behaviour of the emulator and a real 80486.
wm-FPU-emu does not implement all of the behaviour of the 80486 FPU,
but is very close. See "Limitations" later in this file for a list of
some differences.
Please report bugs, etc to me at:
billm@melbpc.org.au
or b.metzenthen@medoto.unimelb.edu.au
For more information on the emulator and on floating point topics, see
my web pages, currently at http://www.suburbia.net/~billm/
--Bill Metzenthen
December 1999
----------------------- Internals of wm-FPU-emu -----------------------
Numeric algorithms:
(1) Add, subtract, and multiply. Nothing remarkable in these.
(2) Divide has been tuned to get reasonable performance. The algorithm
is not the obvious one which most people seem to use, but is designed
to take advantage of the characteristics of the 80386. I expect that
it has been invented many times before I discovered it, but I have not
seen it. It is based upon one of those ideas which one carries around
for years without ever bothering to check it out.
(3) The sqrt function has been tuned to get good performance. It is based
upon Newton's classic method. Performance was improved by capitalizing
upon the properties of Newton's method, and the code is once again
structured taking account of the 80386 characteristics.
(4) The trig, log, and exp functions are based in each case upon quasi-
"optimal" polynomial approximations. My definition of "optimal" was
based upon getting good accuracy with reasonable speed.
(5) The argument reducing code for the trig function effectively uses
a value of pi which is accurate to more than 128 bits. As a consequence,
the reduced argument is accurate to more than 64 bits for arguments up
to a few pi, and accurate to more than 64 bits for most arguments,
even for arguments approaching 2^63. This is far superior to an
80486, which uses a value of pi which is accurate to 66 bits.
The code of the emulator is complicated slightly by the need to
account for a limited form of re-entrancy. Normally, the emulator will
emulate each FPU instruction to completion without interruption.
However, it may happen that when the emulator is accessing the user
memory space, swapping may be needed. In this case the emulator may be
temporarily suspended while disk i/o takes place. During this time
another process may use the emulator, thereby perhaps changing static
variables. The code which accesses user memory is confined to five
files:
fpu_entry.c
reg_ld_str.c
load_store.c
get_address.c
errors.c
As from version 1.12 of the emulator, no static variables are used
(apart from those in the kernel's per-process tables). The emulator is
therefore now fully re-entrant, rather than having just the restricted
form of re-entrancy which is required by the Linux kernel.
----------------------- Limitations of wm-FPU-emu -----------------------
There are a number of differences between the current wm-FPU-emu
(version 2.01) and the 80486 FPU (apart from bugs). The differences
are fewer than those which applied to the 1.xx series of the emulator.
Some of the more important differences are listed below:
The Roundup flag does not have much meaning for the transcendental
functions and its 80486 value with these functions is likely to differ
from its emulator value.
In a few rare cases the Underflow flag obtained with the emulator will
be different from that obtained with an 80486. This occurs when the
following conditions apply simultaneously:
(a) the operands have a higher precision than the current setting of the
precision control (PC) flags.
(b) the underflow exception is masked.
(c) the magnitude of the exact result (before rounding) is less than 2^-16382.
(d) the magnitude of the final result (after rounding) is exactly 2^-16382.
(e) the magnitude of the exact result would be exactly 2^-16382 if the
operands were rounded to the current precision before the arithmetic
operation was performed.
If all of these apply, the emulator will set the Underflow flag but a real
80486 will not.
NOTE: Certain formats of Extended Real are UNSUPPORTED. They are
unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities,
and Unnormals. None of these will be generated by an 80486 or by the
emulator. Do not use them. The emulator treats them differently in
detail from the way an 80486 does.
Self modifying code can cause the emulator to fail. An example of such
code is:
movl %esp,[%ebx]
fld1
The FPU instruction may be (usually will be) loaded into the pre-fetch
queue of the CPU before the mov instruction is executed. If the
destination of the 'movl' overlaps the FPU instruction then the bytes
in the prefetch queue and memory will be inconsistent when the FPU
instruction is executed. The emulator will be invoked but will not be
able to find the instruction which caused the device-not-present
exception. For this case, the emulator cannot emulate the behaviour of
an 80486DX.
Handling of the address size override prefix byte (0x67) has not been
extensively tested yet. A major problem exists because using it in
vm86 mode can cause a general protection fault. Address offsets
greater than 0xffff appear to be illegal in vm86 mode but are quite
acceptable (and work) in real mode. A small test program developed to
check the addressing, and which runs successfully in real mode,
crashes dosemu under Linux and also brings Windows down with a general
protection fault message when run under the MS-DOS prompt of Windows
3.1. (The program simply reads data from a valid address).
The emulator supports 16-bit protected mode, with one difference from
an 80486DX. A 80486DX will allow some floating point instructions to
write a few bytes below the lowest address of the stack. The emulator
will not allow this in 16-bit protected mode: no instructions are
allowed to write outside the bounds set by the protection.
----------------------- Performance of wm-FPU-emu -----------------------
Speed.
-----
The speed of floating point computation with the emulator will depend
upon instruction mix. Relative performance is best for the instructions
which require most computation. The simple instructions are adversely
affected by the FPU instruction trap overhead.
Timing: Some simple timing tests have been made on the emulator functions.
The times include load/store instructions. All times are in microseconds
measured on a 33MHz 386 with 64k cache. The Turbo C tests were under
ms-dos, the next two columns are for emulators running with the djgpp
ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97,
using libm4.0 (hard).
function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu
+ 60.5 154.8 76.5 139.4
- 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7
* 71.0 190.8 79.6 146.6
/ 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1
sin() 310.8 4692.0 319.0 398.5
cos() 284.4 4855.2 308.0 388.7
tan() 495.0 8807.1 394.9 504.7
atan() 328.9 4866.4 601.1 419.5-491.9
sqrt() 128.7 crashed 145.2 227.0
log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1
exp() 479.1 6619.2 469.1 850.8
The performance under Linux is improved by the use of look-ahead code.
The following results show the improvement which is obtained under
Linux due to the look-ahead code. Also given are the times for the
original Linux emulator with the 4.1 'soft' lib.
[ Linus' note: I changed look-ahead to be the default under linux, as
there was no reason not to use it after I had edited it to be
disabled during tracing ]
wm-FPU-emu w original w
look-ahead 'soft' lib
+ 106.4 190.2
- 108.6-111.6 192.4-216.2
* 113.4 193.1
/ 108.8-124.4 700.1-706.2
sin() 390.5 2642.0
cos() 381.5 2767.4
tan() 496.5 3153.3
atan() 367.2-435.5 2439.4-3396.8
sqrt() 195.1 4732.5
log() 358.0-387.5 3359.2-3390.3
exp() 619.3 4046.4
These figures are now somewhat out-of-date. The emulator has become
progressively slower for most functions as more of the 80486 features
have been implemented.
----------------------- Accuracy of wm-FPU-emu -----------------------
The accuracy of the emulator is in almost all cases equal to or better
than that of an Intel 80486 FPU.
The results of the basic arithmetic functions (+,-,*,/), and fsqrt
match those of an 80486 FPU. They are the best possible; the error for
these never exceeds 1/2 an lsb. The fprem and fprem1 instructions
return exact results; they have no error.
The following table compares the emulator accuracy for the sqrt(),
trig and log functions against the Turbo C "emulator". For this table,
each function was tested at about 400 points. Ideal worst-case results
would be 64 bits. The reduced Turbo C accuracy of cos() and tan() for
arguments greater than pi/4 can be thought of as being related to the
precision of the argument x; e.g. an argument of pi/2-(1e-10) which is
accurate to 64 bits can result in a relative accuracy in cos() of
about 64 + log2(cos(x)) = 31 bits.
Function Tested x range Worst result Turbo C
(relative bits)
sqrt(x) 1 .. 2 64.1 63.2
atan(x) 1e-10 .. 200 64.2 62.8
cos(x) 0 .. pi/2-(1e-10) 64.4 (x <= pi/4) 62.4
64.1 (x = pi/2-(1e-10)) 31.9
sin(x) 1e-10 .. pi/2 64.0 62.8
tan(x) 1e-10 .. pi/2-(1e-10) 64.0 (x <= pi/4) 62.1
64.1 (x = pi/2-(1e-10)) 31.9
exp(x) 0 .. 1 63.1 ** 62.9
log(x) 1+1e-6 .. 2 63.8 ** 62.1
** The accuracy for exp() and log() is low because the FPU (emulator)
does not compute them directly; two operations are required.
The emulator passes the "paranoia" tests (compiled with gcc 2.3.3 or
later) for 'float' variables (24 bit precision numbers) when precision
control is set to 24, 53 or 64 bits, and for 'double' variables (53
bit precision numbers) when precision control is set to 53 bits (a
properly performing FPU cannot pass the 'paranoia' tests for 'double'
variables when precision control is set to 64 bits).
The code for reducing the argument for the trig functions (fsin, fcos,
fptan and fsincos) has been improved and now effectively uses a value
for pi which is accurate to more than 128 bits precision. As a
consequence, the accuracy of these functions for large arguments has
been dramatically improved (and is now very much better than an 80486
FPU). There is also now no degradation of accuracy for fcos and fptan
for operands close to pi/2. Measured results are (note that the
definition of accuracy has changed slightly from that used for the
above table):
Function Tested x range Worst result
(absolute bits)
cos(x) 0 .. 9.22e+18 62.0
sin(x) 1e-16 .. 9.22e+18 62.1
tan(x) 1e-16 .. 9.22e+18 61.8
It is possible with some effort to find very large arguments which
give much degraded precision. For example, the integer number
8227740058411162616.0
is within about 10e-7 of a multiple of pi. To find the tan (for
example) of this number to 64 bits precision it would be necessary to
have a value of pi which had about 150 bits precision. The FPU
emulator computes the result to about 42.6 bits precision (the correct
result is about -9.739715e-8). On the other hand, an 80486 FPU returns
0.01059, which in relative terms is hopelessly inaccurate.
For arguments close to critical angles (which occur at multiples of
pi/2) the emulator is more accurate than an 80486 FPU. For very large
arguments, the emulator is far more accurate.
Prior to version 1.20 of the emulator, the accuracy of the results for
the transcendental functions (in their principal range) was not as
good as the results from an 80486 FPU. From version 1.20, the accuracy
has been considerably improved and these functions now give measured
worst-case results which are better than the worst-case results given
by an 80486 FPU.
The following table gives the measured results for the emulator. The
number of randomly selected arguments in each case is about half a
million. The group of three columns gives the frequency of the given
accuracy in number of times per million, thus the second of these
columns shows that an accuracy of between 63.80 and 63.89 bits was
found at a rate of 133 times per one million measurements for fsin.
The results show that the fsin, fcos and fptan instructions return
results which are in error (i.e. less accurate than the best possible
result (which is 64 bits)) for about one per cent of all arguments
between -pi/2 and +pi/2. The other instructions have a lower
frequency of results which are in error. The last two columns give
the worst accuracy which was found (in bits) and the approximate value
of the argument which produced it.
frequency (per M)
------------------- ---------------
instr arg range # tests 63.7 63.8 63.9 worst at arg
bits bits bits bits
----- ------------ ------- ---- ---- ----- ----- --------
fsin (0,pi/2) 547756 0 133 10673 63.89 0.451317
fcos (0,pi/2) 547563 0 126 10532 63.85 0.700801
fptan (0,pi/2) 536274 11 267 10059 63.74 0.784876
fpatan 4 quadrants 517087 0 8 1855 63.88 0.435121 (4q)
fyl2x (0,20) 541861 0 0 1323 63.94 1.40923 (x)
fyl2xp1 (-.293,.414) 520256 0 0 5678 63.93 0.408542 (x)
f2xm1 (-1,1) 538847 4 481 6488 63.79 0.167709
Tests performed on an 80486 FPU showed results of lower accuracy. The
following table gives the results which were obtained with an AMD
486DX2/66 (other tests indicate that an Intel 486DX produces
identical results). The tests were basically the same as those used
to measure the emulator (the values, being random, were in general not
the same). The total number of tests for each instruction are given
at the end of the table, in case each about 100k tests were performed.
Another line of figures at the end of the table shows that most of the
instructions return results which are in error for more than 10
percent of the arguments tested.
The numbers in the body of the table give the approx number of times a
result of the given accuracy in bits (given in the left-most column)
was obtained per one million arguments. For three of the instructions,
two columns of results are given: * The second column for f2xm1 gives
the number cases where the results of the first column were for a
positive argument, this shows that this instruction gives better
results for positive arguments than it does for negative. * In the
cases of fcos and fptan, the first column gives the results when all
cases where arguments greater than 1.5 were removed from the results
given in the second column. Unlike the emulator, an 80486 FPU returns
results of relatively poor accuracy for these instructions when the
argument approaches pi/2. The table does not show those cases when the
accuracy of the results were less than 62 bits, which occurs quite
often for fsin and fptan when the argument approaches pi/2. This poor
accuracy is discussed above in relation to the Turbo C "emulator", and
the accuracy of the value of pi.
bits f2xm1 f2xm1 fpatan fcos fcos fyl2x fyl2xp1 fsin fptan fptan
62.0 0 0 0 0 437 0 0 0 0 925
62.1 0 0 10 0 894 0 0 0 0 1023
62.2 14 0 0 0 1033 0 0 0 0 945
62.3 57 0 0 0 1202 0 0 0 0 1023
62.4 385 0 0 10 1292 0 23 0 0 1178
62.5 1140 0 0 119 1649 0 39 0 0 1149
62.6 2037 0 0 189 1620 0 16 0 0 1169
62.7 5086 14 0 646 2315 10 101 35 39 1402
62.8 8818 86 0 984 3050 59 287 131 224 2036
62.9 11340 1355 0 2126 4153 79 605 357 321 1948
63.0 15557 4750 0 3319 5376 246 1281 862 808 2688
63.1 20016 8288 0 4620 6628 511 2569 1723 1510 3302
63.2 24945 11127 10 6588 8098 1120 4470 2968 2990 4724
63.3 25686 12382 69 8774 10682 1906 6775 4482 5474 7236
63.4 29219 14722 79 11109 12311 3094 9414 7259 8912 10587
63.5 30458 14936 393 13802 15014 5874 12666 9609 13762 15262
63.6 32439 16448 1277 17945 19028 10226 15537 14657 19158 20346
63.7 35031 16805 4067 23003 23947 18910 20116 21333 25001 26209
63.8 33251 15820 7673 24781 25675 24617 25354 24440 29433 30329
63.9 33293 16833 18529 28318 29233 31267 31470 27748 29676 30601
Per cent with error:
30.9 3.2 18.5 9.8 13.1 11.6 17.4
Total arguments tested:
70194 70099 101784 100641 100641 101799 128853 114893 102675 102675
------------------------- Contributors -------------------------------
A number of people have contributed to the development of the
emulator, often by just reporting bugs, sometimes with suggested
fixes, and a few kind people have provided me with access in one way
or another to an 80486 machine. Contributors include (to those people
who I may have forgotten, please forgive me):
Linus Torvalds
Tommy.Thorn@daimi.aau.dk
Andrew.Tridgell@anu.edu.au
Nick Holloway, alfie@dcs.warwick.ac.uk
Hermano Moura, moura@dcs.gla.ac.uk
Jon Jagger, J.Jagger@scp.ac.uk
Lennart Benschop
Brian Gallew, geek+@CMU.EDU
Thomas Staniszewski, ts3v+@andrew.cmu.edu
Martin Howell, mph@plasma.apana.org.au
M Saggaf, alsaggaf@athena.mit.edu
Peter Barker, PETER@socpsy.sci.fau.edu
tom@vlsivie.tuwien.ac.at
Dan Russel, russed@rpi.edu
Daniel Carosone, danielce@ee.mu.oz.au
cae@jpmorgan.com
Hamish Coleman, t933093@minyos.xx.rmit.oz.au
Bruce Evans, bde@kralizec.zeta.org.au
Timo Korvola, Timo.Korvola@hut.fi
Rick Lyons, rick@razorback.brisnet.org.au
Rick, jrs@world.std.com
...and numerous others who responded to my request for help with
a real 80486.
-46
View File
@@ -1,46 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*---------------------------------------------------------------------------+
| control_w.h |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _CONTROLW_H_
#define _CONTROLW_H_
#ifdef __ASSEMBLER__
#define _Const_(x) $##x
#else
#define _Const_(x) x
#endif
#define CW_RC _Const_(0x0C00) /* rounding control */
#define CW_PC _Const_(0x0300) /* precision control */
#define CW_Precision Const_(0x0020) /* loss of precision mask */
#define CW_Underflow Const_(0x0010) /* underflow mask */
#define CW_Overflow Const_(0x0008) /* overflow mask */
#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */
#define CW_Denormal Const_(0x0002) /* denormalized operand mask */
#define CW_Invalid Const_(0x0001) /* invalid operation mask */
#define CW_Exceptions _Const_(0x003f) /* all masks */
#define RC_RND _Const_(0x0000)
#define RC_DOWN _Const_(0x0400)
#define RC_UP _Const_(0x0800)
#define RC_CHOP _Const_(0x0C00)
/* p 15-5: Precision control bits affect only the following:
ADD, SUB(R), MUL, DIV(R), and SQRT */
#define PR_24_BITS _Const_(0x000)
#define PR_53_BITS _Const_(0x200)
#define PR_64_BITS _Const_(0x300)
#define PR_RESERVED_BITS _Const_(0x100)
/* FULL_PRECISION simulates all exceptions masked */
#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f)
#endif /* _CONTROLW_H_ */
-367
View File
@@ -1,367 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
.file "div_Xsig.S"
/*---------------------------------------------------------------------------+
| div_Xsig.S |
| |
| Division subroutine for 96 bit quantities |
| |
| Copyright (C) 1994,1995 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@jacobi.maths.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Divide the 96 bit quantity pointed to by a, by that pointed to by b, and |
| put the 96 bit result at the location d. |
| |
| The result may not be accurate to 96 bits. It is intended for use where |
| a result better than 64 bits is required. The result should usually be |
| good to at least 94 bits. |
| The returned result is actually divided by one half. This is done to |
| prevent overflow. |
| |
| .aaaaaaaaaaaaaa / .bbbbbbbbbbbbb -> .dddddddddddd |
| |
| void div_Xsig(Xsig *a, Xsig *b, Xsig *dest) |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "fpu_emu.h"
#define XsigLL(x) (x)
#define XsigL(x) 4(x)
#define XsigH(x) 8(x)
#ifndef NON_REENTRANT_FPU
/*
Local storage on the stack:
Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
*/
#define FPU_accum_3 -4(%ebp)
#define FPU_accum_2 -8(%ebp)
#define FPU_accum_1 -12(%ebp)
#define FPU_accum_0 -16(%ebp)
#define FPU_result_3 -20(%ebp)
#define FPU_result_2 -24(%ebp)
#define FPU_result_1 -28(%ebp)
#else
.data
/*
Local storage in a static area:
Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
*/
.align 4,0
FPU_accum_3:
.long 0
FPU_accum_2:
.long 0
FPU_accum_1:
.long 0
FPU_accum_0:
.long 0
FPU_result_3:
.long 0
FPU_result_2:
.long 0
FPU_result_1:
.long 0
#endif /* NON_REENTRANT_FPU */
.text
SYM_FUNC_START(div_Xsig)
pushl %ebp
movl %esp,%ebp
#ifndef NON_REENTRANT_FPU
subl $28,%esp
#endif /* NON_REENTRANT_FPU */
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi /* pointer to num */
movl PARAM2,%ebx /* pointer to denom */
#ifdef PARANOID
testl $0x80000000, XsigH(%ebx) /* Divisor */
je L_bugged
#endif /* PARANOID */
/*---------------------------------------------------------------------------+
| Divide: Return arg1/arg2 to arg3. |
| |
| The maximum returned value is (ignoring exponents) |
| .ffffffff ffffffff |
| ------------------ = 1.ffffffff fffffffe |
| .80000000 00000000 |
| and the minimum is |
| .80000000 00000000 |
| ------------------ = .80000000 00000001 (rounded) |
| .ffffffff ffffffff |
| |
+---------------------------------------------------------------------------*/
/* Save extended dividend in local register */
/* Divide by 2 to prevent overflow */
clc
movl XsigH(%esi),%eax
rcrl %eax
movl %eax,FPU_accum_3
movl XsigL(%esi),%eax
rcrl %eax
movl %eax,FPU_accum_2
movl XsigLL(%esi),%eax
rcrl %eax
movl %eax,FPU_accum_1
movl $0,%eax
rcrl %eax
movl %eax,FPU_accum_0
movl FPU_accum_2,%eax /* Get the current num */
movl FPU_accum_3,%edx
/*----------------------------------------------------------------------*/
/* Initialization done.
Do the first 32 bits. */
/* We will divide by a number which is too large */
movl XsigH(%ebx),%ecx
addl $1,%ecx
jnc LFirst_div_not_1
/* here we need to divide by 100000000h,
i.e., no division at all.. */
mov %edx,%eax
jmp LFirst_div_done
LFirst_div_not_1:
divl %ecx /* Divide the numerator by the augmented
denom ms dw */
LFirst_div_done:
movl %eax,FPU_result_3 /* Put the result in the answer */
mull XsigH(%ebx) /* mul by the ms dw of the denom */
subl %eax,FPU_accum_2 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_3
movl FPU_result_3,%eax /* Get the result back */
mull XsigL(%ebx) /* now mul the ls dw of the denom */
subl %eax,FPU_accum_1 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_2
sbbl $0,FPU_accum_3
je LDo_2nd_32_bits /* Must check for non-zero result here */
#ifdef PARANOID
jb L_bugged_1
#endif /* PARANOID */
/* need to subtract another once of the denom */
incl FPU_result_3 /* Correct the answer */
movl XsigL(%ebx),%eax
movl XsigH(%ebx),%edx
subl %eax,FPU_accum_1 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_2
#ifdef PARANOID
sbbl $0,FPU_accum_3
jne L_bugged_1 /* Must check for non-zero result here */
#endif /* PARANOID */
/*----------------------------------------------------------------------*/
/* Half of the main problem is done, there is just a reduced numerator
to handle now.
Work with the second 32 bits, FPU_accum_0 not used from now on */
LDo_2nd_32_bits:
movl FPU_accum_2,%edx /* get the reduced num */
movl FPU_accum_1,%eax
/* need to check for possible subsequent overflow */
cmpl XsigH(%ebx),%edx
jb LDo_2nd_div
ja LPrevent_2nd_overflow
cmpl XsigL(%ebx),%eax
jb LDo_2nd_div
LPrevent_2nd_overflow:
/* The numerator is greater or equal, would cause overflow */
/* prevent overflow */
subl XsigL(%ebx),%eax
sbbl XsigH(%ebx),%edx
movl %edx,FPU_accum_2
movl %eax,FPU_accum_1
incl FPU_result_3 /* Reflect the subtraction in the answer */
#ifdef PARANOID
je L_bugged_2 /* Can't bump the result to 1.0 */
#endif /* PARANOID */
LDo_2nd_div:
cmpl $0,%ecx /* augmented denom msw */
jnz LSecond_div_not_1
/* %ecx == 0, we are dividing by 1.0 */
mov %edx,%eax
jmp LSecond_div_done
LSecond_div_not_1:
divl %ecx /* Divide the numerator by the denom ms dw */
LSecond_div_done:
movl %eax,FPU_result_2 /* Put the result in the answer */
mull XsigH(%ebx) /* mul by the ms dw of the denom */
subl %eax,FPU_accum_1 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_2
#ifdef PARANOID
jc L_bugged_2
#endif /* PARANOID */
movl FPU_result_2,%eax /* Get the result back */
mull XsigL(%ebx) /* now mul the ls dw of the denom */
subl %eax,FPU_accum_0 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */
sbbl $0,FPU_accum_2
#ifdef PARANOID
jc L_bugged_2
#endif /* PARANOID */
jz LDo_3rd_32_bits
#ifdef PARANOID
cmpl $1,FPU_accum_2
jne L_bugged_2
#endif /* PARANOID */
/* need to subtract another once of the denom */
movl XsigL(%ebx),%eax
movl XsigH(%ebx),%edx
subl %eax,FPU_accum_0 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_1
sbbl $0,FPU_accum_2
#ifdef PARANOID
jc L_bugged_2
jne L_bugged_2
#endif /* PARANOID */
addl $1,FPU_result_2 /* Correct the answer */
adcl $0,FPU_result_3
#ifdef PARANOID
jc L_bugged_2 /* Must check for non-zero result here */
#endif /* PARANOID */
/*----------------------------------------------------------------------*/
/* The division is essentially finished here, we just need to perform
tidying operations.
Deal with the 3rd 32 bits */
LDo_3rd_32_bits:
/* We use an approximation for the third 32 bits.
To take account of the 3rd 32 bits of the divisor
(call them del), we subtract del * (a/b) */
movl FPU_result_3,%eax /* a/b */
mull XsigLL(%ebx) /* del */
subl %edx,FPU_accum_1
/* A borrow indicates that the result is negative */
jnb LTest_over
movl XsigH(%ebx),%edx
addl %edx,FPU_accum_1
subl $1,FPU_result_2 /* Adjust the answer */
sbbl $0,FPU_result_3
/* The above addition might not have been enough, check again. */
movl FPU_accum_1,%edx /* get the reduced num */
cmpl XsigH(%ebx),%edx /* denom */
jb LDo_3rd_div
movl XsigH(%ebx),%edx
addl %edx,FPU_accum_1
subl $1,FPU_result_2 /* Adjust the answer */
sbbl $0,FPU_result_3
jmp LDo_3rd_div
LTest_over:
movl FPU_accum_1,%edx /* get the reduced num */
/* need to check for possible subsequent overflow */
cmpl XsigH(%ebx),%edx /* denom */
jb LDo_3rd_div
/* prevent overflow */
subl XsigH(%ebx),%edx
movl %edx,FPU_accum_1
addl $1,FPU_result_2 /* Reflect the subtraction in the answer */
adcl $0,FPU_result_3
LDo_3rd_div:
movl FPU_accum_0,%eax
movl FPU_accum_1,%edx
divl XsigH(%ebx)
movl %eax,FPU_result_1 /* Rough estimate of third word */
movl PARAM3,%esi /* pointer to answer */
movl FPU_result_1,%eax
movl %eax,XsigLL(%esi)
movl FPU_result_2,%eax
movl %eax,XsigL(%esi)
movl FPU_result_3,%eax
movl %eax,XsigH(%esi)
L_exit:
popl %ebx
popl %edi
popl %esi
leave
RET
#ifdef PARANOID
/* The logic is wrong if we got here */
L_bugged:
pushl EX_INTERNAL|0x240
call EXCEPTION
pop %ebx
jmp L_exit
L_bugged_1:
pushl EX_INTERNAL|0x241
call EXCEPTION
pop %ebx
jmp L_exit
L_bugged_2:
pushl EX_INTERNAL|0x242
call EXCEPTION
pop %ebx
jmp L_exit
#endif /* PARANOID */
SYM_FUNC_END(div_Xsig)
-48
View File
@@ -1,48 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
.file "div_small.S"
/*---------------------------------------------------------------------------+
| div_small.S |
| |
| Divide a 64 bit integer by a 32 bit integer & return remainder. |
| |
| Copyright (C) 1992,1995 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@jacobi.maths.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| unsigned long FPU_div_small(unsigned long long *x, unsigned long y) |
+---------------------------------------------------------------------------*/
#include "fpu_emu.h"
.text
SYM_FUNC_START(FPU_div_small)
pushl %ebp
movl %esp,%ebp
pushl %esi
movl PARAM1,%esi /* pointer to num */
movl PARAM2,%ecx /* The denominator */
movl 4(%esi),%eax /* Get the current num msw */
xorl %edx,%edx
divl %ecx
movl %eax,4(%esi)
movl (%esi),%eax /* Get the num lsw */
divl %ecx
movl %eax,(%esi)
movl %edx,%eax /* Return the remainder in eax */
popl %esi
leave
RET
SYM_FUNC_END(FPU_div_small)
-686
View File
@@ -1,686 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*---------------------------------------------------------------------------+
| errors.c |
| |
| The error handling functions for wm-FPU-emu |
| |
| Copyright (C) 1992,1993,1994,1996 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| E-mail billm@jacobi.maths.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <linux/signal.h>
#include <linux/uaccess.h>
#include "fpu_emu.h"
#include "fpu_system.h"
#include "exception.h"
#include "status_w.h"
#include "control_w.h"
#include "reg_constant.h"
#include "version.h"
/* */
#undef PRINT_MESSAGES
/* */
#if 0
void Un_impl(void)
{
u_char byte1, FPU_modrm;
unsigned long address = FPU_ORIG_EIP;
RE_ENTRANT_CHECK_OFF;
/* No need to check access_ok(), we have previously fetched these bytes. */
printk("Unimplemented FPU Opcode at eip=%p : ", (void __user *)address);
if (FPU_CS == __USER_CS) {
while (1) {
FPU_get_user(byte1, (u_char __user *) address);
if ((byte1 & 0xf8) == 0xd8)
break;
printk("[%02x]", byte1);
address++;
}
printk("%02x ", byte1);
FPU_get_user(FPU_modrm, 1 + (u_char __user *) address);
if (FPU_modrm >= 0300)
printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8,
FPU_modrm & 7);
else
printk("/%d\n", (FPU_modrm >> 3) & 7);
} else {
printk("cs selector = %04x\n", FPU_CS);
}
RE_ENTRANT_CHECK_ON;
EXCEPTION(EX_Invalid);
}
#endif /* 0 */
/*
Called for opcodes which are illegal and which are known to result in a
SIGILL with a real 80486.
*/
void FPU_illegal(void)
{
math_abort(FPU_info, SIGILL);
}
void FPU_printall(void)
{
int i;
static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "Empty",
"DeNorm", "Inf", "NaN"
};
u_char byte1, FPU_modrm;
unsigned long address = FPU_ORIG_EIP;
RE_ENTRANT_CHECK_OFF;
/* No need to check access_ok(), we have previously fetched these bytes. */
printk("At %p:", (void *)address);
if (FPU_CS == __USER_CS) {
#define MAX_PRINTED_BYTES 20
for (i = 0; i < MAX_PRINTED_BYTES; i++) {
FPU_get_user(byte1, (u_char __user *) address);
if ((byte1 & 0xf8) == 0xd8) {
printk(" %02x", byte1);
break;
}
printk(" [%02x]", byte1);
address++;
}
if (i == MAX_PRINTED_BYTES)
printk(" [more..]\n");
else {
FPU_get_user(FPU_modrm, 1 + (u_char __user *) address);
if (FPU_modrm >= 0300)
printk(" %02x (%02x+%d)\n", FPU_modrm,
FPU_modrm & 0xf8, FPU_modrm & 7);
else
printk(" /%d, mod=%d rm=%d\n",
(FPU_modrm >> 3) & 7,
(FPU_modrm >> 6) & 3, FPU_modrm & 7);
}
} else {
printk("%04x\n", FPU_CS);
}
partial_status = status_word();
#ifdef DEBUGGING
if (partial_status & SW_Backward)
printk("SW: backward compatibility\n");
if (partial_status & SW_C3)
printk("SW: condition bit 3\n");
if (partial_status & SW_C2)
printk("SW: condition bit 2\n");
if (partial_status & SW_C1)
printk("SW: condition bit 1\n");
if (partial_status & SW_C0)
printk("SW: condition bit 0\n");
if (partial_status & SW_Summary)
printk("SW: exception summary\n");
if (partial_status & SW_Stack_Fault)
printk("SW: stack fault\n");
if (partial_status & SW_Precision)
printk("SW: loss of precision\n");
if (partial_status & SW_Underflow)
printk("SW: underflow\n");
if (partial_status & SW_Overflow)
printk("SW: overflow\n");
if (partial_status & SW_Zero_Div)
printk("SW: divide by zero\n");
if (partial_status & SW_Denorm_Op)
printk("SW: denormalized operand\n");
if (partial_status & SW_Invalid)
printk("SW: invalid operation\n");
#endif /* DEBUGGING */
printk(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n", partial_status & 0x8000 ? 1 : 0, /* busy */
(partial_status & 0x3800) >> 11, /* stack top pointer */
partial_status & 0x80 ? 1 : 0, /* Error summary status */
partial_status & 0x40 ? 1 : 0, /* Stack flag */
partial_status & SW_C3 ? 1 : 0, partial_status & SW_C2 ? 1 : 0, /* cc */
partial_status & SW_C1 ? 1 : 0, partial_status & SW_C0 ? 1 : 0, /* cc */
partial_status & SW_Precision ? 1 : 0,
partial_status & SW_Underflow ? 1 : 0,
partial_status & SW_Overflow ? 1 : 0,
partial_status & SW_Zero_Div ? 1 : 0,
partial_status & SW_Denorm_Op ? 1 : 0,
partial_status & SW_Invalid ? 1 : 0);
printk(" CW: ic=%d rc=%d%d pc=%d%d iem=%d ef=%d%d%d%d%d%d\n",
control_word & 0x1000 ? 1 : 0,
(control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
(control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
control_word & 0x80 ? 1 : 0,
control_word & SW_Precision ? 1 : 0,
control_word & SW_Underflow ? 1 : 0,
control_word & SW_Overflow ? 1 : 0,
control_word & SW_Zero_Div ? 1 : 0,
control_word & SW_Denorm_Op ? 1 : 0,
control_word & SW_Invalid ? 1 : 0);
for (i = 0; i < 8; i++) {
FPU_REG *r = &st(i);
u_char tagi = FPU_gettagi(i);
switch (tagi) {
case TAG_Empty:
continue;
case TAG_Zero:
case TAG_Special:
/* Update tagi for the printk below */
tagi = FPU_Special(r);
fallthrough;
case TAG_Valid:
printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6d ", i,
getsign(r) ? '-' : '+',
(long)(r->sigh >> 16),
(long)(r->sigh & 0xFFFF),
(long)(r->sigl >> 16),
(long)(r->sigl & 0xFFFF),
exponent(r) - EXP_BIAS + 1);
break;
default:
printk("Whoops! Error in errors.c: tag%d is %d ", i,
tagi);
continue;
}
printk("%s\n", tag_desc[(int)(unsigned)tagi]);
}
RE_ENTRANT_CHECK_ON;
}
static struct {
int type;
const char *name;
} exception_names[] = {
{
EX_StackOver, "stack overflow"}, {
EX_StackUnder, "stack underflow"}, {
EX_Precision, "loss of precision"}, {
EX_Underflow, "underflow"}, {
EX_Overflow, "overflow"}, {
EX_ZeroDiv, "divide by zero"}, {
EX_Denormal, "denormalized operand"}, {
EX_Invalid, "invalid operation"}, {
EX_INTERNAL, "INTERNAL BUG in " FPU_VERSION}, {
0, NULL}
};
/*
EX_INTERNAL is always given with a code which indicates where the
error was detected.
Internal error types:
0x14 in fpu_etc.c
0x1nn in a *.c file:
0x101 in reg_add_sub.c
0x102 in reg_mul.c
0x104 in poly_atan.c
0x105 in reg_mul.c
0x107 in fpu_trig.c
0x108 in reg_compare.c
0x109 in reg_compare.c
0x110 in reg_add_sub.c
0x111 in fpe_entry.c
0x112 in fpu_trig.c
0x113 in errors.c
0x115 in fpu_trig.c
0x116 in fpu_trig.c
0x117 in fpu_trig.c
0x118 in fpu_trig.c
0x119 in fpu_trig.c
0x120 in poly_atan.c
0x121 in reg_compare.c
0x122 in reg_compare.c
0x123 in reg_compare.c
0x125 in fpu_trig.c
0x126 in fpu_entry.c
0x127 in poly_2xm1.c
0x128 in fpu_entry.c
0x129 in fpu_entry.c
0x130 in get_address.c
0x131 in get_address.c
0x132 in get_address.c
0x133 in get_address.c
0x140 in load_store.c
0x141 in load_store.c
0x150 in poly_sin.c
0x151 in poly_sin.c
0x160 in reg_ld_str.c
0x161 in reg_ld_str.c
0x162 in reg_ld_str.c
0x163 in reg_ld_str.c
0x164 in reg_ld_str.c
0x170 in fpu_tags.c
0x171 in fpu_tags.c
0x172 in fpu_tags.c
0x180 in reg_convert.c
0x2nn in an *.S file:
0x201 in reg_u_add.S
0x202 in reg_u_div.S
0x203 in reg_u_div.S
0x204 in reg_u_div.S
0x205 in reg_u_mul.S
0x206 in reg_u_sub.S
0x207 in wm_sqrt.S
0x208 in reg_div.S
0x209 in reg_u_sub.S
0x210 in reg_u_sub.S
0x211 in reg_u_sub.S
0x212 in reg_u_sub.S
0x213 in wm_sqrt.S
0x214 in wm_sqrt.S
0x215 in wm_sqrt.S
0x220 in reg_norm.S
0x221 in reg_norm.S
0x230 in reg_round.S
0x231 in reg_round.S
0x232 in reg_round.S
0x233 in reg_round.S
0x234 in reg_round.S
0x235 in reg_round.S
0x236 in reg_round.S
0x240 in div_Xsig.S
0x241 in div_Xsig.S
0x242 in div_Xsig.S
*/
asmlinkage __visible void FPU_exception(int n)
{
int i, int_type;
int_type = 0; /* Needed only to stop compiler warnings */
if (n & EX_INTERNAL) {
int_type = n - EX_INTERNAL;
n = EX_INTERNAL;
/* Set lots of exception bits! */
partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward);
} else {
/* Extract only the bits which we use to set the status word */
n &= (SW_Exc_Mask);
/* Set the corresponding exception bit */
partial_status |= n;
/* Set summary bits iff exception isn't masked */
if (partial_status & ~control_word & CW_Exceptions)
partial_status |= (SW_Summary | SW_Backward);
if (n & (SW_Stack_Fault | EX_Precision)) {
if (!(n & SW_C1))
/* This bit distinguishes over- from underflow for a stack fault,
and roundup from round-down for precision loss. */
partial_status &= ~SW_C1;
}
}
RE_ENTRANT_CHECK_OFF;
if ((~control_word & n & CW_Exceptions) || (n == EX_INTERNAL)) {
/* Get a name string for error reporting */
for (i = 0; exception_names[i].type; i++)
if ((exception_names[i].type & n) ==
exception_names[i].type)
break;
if (exception_names[i].type) {
#ifdef PRINT_MESSAGES
printk("FP Exception: %s!\n", exception_names[i].name);
#endif /* PRINT_MESSAGES */
} else
printk("FPU emulator: Unknown Exception: 0x%04x!\n", n);
if (n == EX_INTERNAL) {
printk("FPU emulator: Internal error type 0x%04x\n",
int_type);
FPU_printall();
}
#ifdef PRINT_MESSAGES
else
FPU_printall();
#endif /* PRINT_MESSAGES */
/*
* The 80486 generates an interrupt on the next non-control FPU
* instruction. So we need some means of flagging it.
* We use the ES (Error Summary) bit for this.
*/
}
RE_ENTRANT_CHECK_ON;
#ifdef __DEBUG__
math_abort(FPU_info, SIGFPE);
#endif /* __DEBUG__ */
}
/* Real operation attempted on a NaN. */
/* Returns < 0 if the exception is unmasked */
int real_1op_NaN(FPU_REG *a)
{
int signalling, isNaN;
isNaN = (exponent(a) == EXP_OVER) && (a->sigh & 0x80000000);
/* The default result for the case of two "equal" NaNs (signs may
differ) is chosen to reproduce 80486 behaviour */
signalling = isNaN && !(a->sigh & 0x40000000);
if (!signalling) {
if (!isNaN) { /* pseudo-NaN, or other unsupported? */
if (control_word & CW_Invalid) {
/* Masked response */
reg_copy(&CONST_QNaN, a);
}
EXCEPTION(EX_Invalid);
return (!(control_word & CW_Invalid) ? FPU_Exception :
0) | TAG_Special;
}
return TAG_Special;
}
if (control_word & CW_Invalid) {
/* The masked response */
if (!(a->sigh & 0x80000000)) { /* pseudo-NaN ? */
reg_copy(&CONST_QNaN, a);
}
/* ensure a Quiet NaN */
a->sigh |= 0x40000000;
}
EXCEPTION(EX_Invalid);
return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
}
/* Real operation attempted on two operands, one a NaN. */
/* Returns < 0 if the exception is unmasked */
int real_2op_NaN(FPU_REG const *b, u_char tagb,
int deststnr, FPU_REG const *defaultNaN)
{
FPU_REG *dest = &st(deststnr);
FPU_REG const *a = dest;
u_char taga = FPU_gettagi(deststnr);
FPU_REG const *x;
int signalling, unsupported;
if (taga == TAG_Special)
taga = FPU_Special(a);
if (tagb == TAG_Special)
tagb = FPU_Special(b);
/* TW_NaN is also used for unsupported data types. */
unsupported = ((taga == TW_NaN)
&& !((exponent(a) == EXP_OVER)
&& (a->sigh & 0x80000000)))
|| ((tagb == TW_NaN)
&& !((exponent(b) == EXP_OVER) && (b->sigh & 0x80000000)));
if (unsupported) {
if (control_word & CW_Invalid) {
/* Masked response */
FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
}
EXCEPTION(EX_Invalid);
return (!(control_word & CW_Invalid) ? FPU_Exception : 0) |
TAG_Special;
}
if (taga == TW_NaN) {
x = a;
if (tagb == TW_NaN) {
signalling = !(a->sigh & b->sigh & 0x40000000);
if (significand(b) > significand(a))
x = b;
else if (significand(b) == significand(a)) {
/* The default result for the case of two "equal" NaNs (signs may
differ) is chosen to reproduce 80486 behaviour */
x = defaultNaN;
}
} else {
/* return the quiet version of the NaN in a */
signalling = !(a->sigh & 0x40000000);
}
} else
#ifdef PARANOID
if (tagb == TW_NaN)
#endif /* PARANOID */
{
signalling = !(b->sigh & 0x40000000);
x = b;
}
#ifdef PARANOID
else {
signalling = 0;
EXCEPTION(EX_INTERNAL | 0x113);
x = &CONST_QNaN;
}
#endif /* PARANOID */
if ((!signalling) || (control_word & CW_Invalid)) {
if (!x)
x = b;
if (!(x->sigh & 0x80000000)) /* pseudo-NaN ? */
x = &CONST_QNaN;
FPU_copy_to_regi(x, TAG_Special, deststnr);
if (!signalling)
return TAG_Special;
/* ensure a Quiet NaN */
dest->sigh |= 0x40000000;
}
EXCEPTION(EX_Invalid);
return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
}
/* Invalid arith operation on Valid registers */
/* Returns < 0 if the exception is unmasked */
asmlinkage __visible int arith_invalid(int deststnr)
{
EXCEPTION(EX_Invalid);
if (control_word & CW_Invalid) {
/* The masked response */
FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
}
return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Valid;
}
/* Divide a finite number by zero */
asmlinkage __visible int FPU_divide_by_zero(int deststnr, u_char sign)
{
FPU_REG *dest = &st(deststnr);
int tag = TAG_Valid;
if (control_word & CW_ZeroDiv) {
/* The masked response */
FPU_copy_to_regi(&CONST_INF, TAG_Special, deststnr);
setsign(dest, sign);
tag = TAG_Special;
}
EXCEPTION(EX_ZeroDiv);
return (!(control_word & CW_ZeroDiv) ? FPU_Exception : 0) | tag;
}
/* This may be called often, so keep it lean */
int set_precision_flag(int flags)
{
if (control_word & CW_Precision) {
partial_status &= ~(SW_C1 & flags);
partial_status |= flags; /* The masked response */
return 0;
} else {
EXCEPTION(flags);
return 1;
}
}
/* This may be called often, so keep it lean */
asmlinkage __visible void set_precision_flag_up(void)
{
if (control_word & CW_Precision)
partial_status |= (SW_Precision | SW_C1); /* The masked response */
else
EXCEPTION(EX_Precision | SW_C1);
}
/* This may be called often, so keep it lean */
asmlinkage __visible void set_precision_flag_down(void)
{
if (control_word & CW_Precision) { /* The masked response */
partial_status &= ~SW_C1;
partial_status |= SW_Precision;
} else
EXCEPTION(EX_Precision);
}
asmlinkage __visible int denormal_operand(void)
{
if (control_word & CW_Denormal) { /* The masked response */
partial_status |= SW_Denorm_Op;
return TAG_Special;
} else {
EXCEPTION(EX_Denormal);
return TAG_Special | FPU_Exception;
}
}
asmlinkage __visible int arith_overflow(FPU_REG *dest)
{
int tag = TAG_Valid;
if (control_word & CW_Overflow) {
/* The masked response */
/* ###### The response here depends upon the rounding mode */
reg_copy(&CONST_INF, dest);
tag = TAG_Special;
} else {
/* Subtract the magic number from the exponent */
addexponent(dest, (-3 * (1 << 13)));
}
EXCEPTION(EX_Overflow);
if (control_word & CW_Overflow) {
/* The overflow exception is masked. */
/* By definition, precision is lost.
The roundup bit (C1) is also set because we have
"rounded" upwards to Infinity. */
EXCEPTION(EX_Precision | SW_C1);
return tag;
}
return tag;
}
asmlinkage __visible int arith_underflow(FPU_REG *dest)
{
int tag = TAG_Valid;
if (control_word & CW_Underflow) {
/* The masked response */
if (exponent16(dest) <= EXP_UNDER - 63) {
reg_copy(&CONST_Z, dest);
partial_status &= ~SW_C1; /* Round down. */
tag = TAG_Zero;
} else {
stdexp(dest);
}
} else {
/* Add the magic number to the exponent. */
addexponent(dest, (3 * (1 << 13)) + EXTENDED_Ebias);
}
EXCEPTION(EX_Underflow);
if (control_word & CW_Underflow) {
/* The underflow exception is masked. */
EXCEPTION(EX_Precision);
return tag;
}
return tag;
}
void FPU_stack_overflow(void)
{
if (control_word & CW_Invalid) {
/* The masked response */
top--;
FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
}
EXCEPTION(EX_StackOver);
return;
}
void FPU_stack_underflow(void)
{
if (control_word & CW_Invalid) {
/* The masked response */
FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
}
EXCEPTION(EX_StackUnder);
return;
}
void FPU_stack_underflow_i(int i)
{
if (control_word & CW_Invalid) {
/* The masked response */
FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
}
EXCEPTION(EX_StackUnder);
return;
}
void FPU_stack_underflow_pop(int i)
{
if (control_word & CW_Invalid) {
/* The masked response */
FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
FPU_pop();
}
EXCEPTION(EX_StackUnder);
return;
}
-51
View File
@@ -1,51 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*---------------------------------------------------------------------------+
| exception.h |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _EXCEPTION_H_
#define _EXCEPTION_H_
#ifdef __ASSEMBLER__
#define Const_(x) $##x
#else
#define Const_(x) x
#endif
#ifndef SW_C1
#include "fpu_emu.h"
#endif /* SW_C1 */
#define FPU_BUSY Const_(0x8000) /* FPU busy bit (8087 compatibility) */
#define EX_ErrorSummary Const_(0x0080) /* Error summary status */
/* Special exceptions: */
#define EX_INTERNAL Const_(0x8000) /* Internal error in wm-FPU-emu */
#define EX_StackOver Const_(0x0041|SW_C1) /* stack overflow */
#define EX_StackUnder Const_(0x0041) /* stack underflow */
/* Exception flags: */
#define EX_Precision Const_(0x0020) /* loss of precision */
#define EX_Underflow Const_(0x0010) /* underflow */
#define EX_Overflow Const_(0x0008) /* overflow */
#define EX_ZeroDiv Const_(0x0004) /* divide by zero */
#define EX_Denormal Const_(0x0002) /* denormalized operand */
#define EX_Invalid Const_(0x0001) /* invalid operation */
#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1))
#define PRECISION_LOST_DOWN Const_(EX_Precision)
#ifndef __ASSEMBLER__
#ifdef DEBUG
#define EXCEPTION(x) { printk("exception in %s at line %d\n", \
__FILE__, __LINE__); FPU_exception(x); }
#else
#define EXCEPTION(x) FPU_exception(x)
#endif
#endif /* __ASSEMBLER__ */
#endif /* _EXCEPTION_H_ */
-153
View File
@@ -1,153 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*---------------------------------------------------------------------------+
| fpu_arith.c |
| |
| Code to implement the FPU register/register arithmetic instructions |
| |
| Copyright (C) 1992,1993,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| E-mail billm@suburbia.net |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "status_w.h"
void fadd__(void)
{
/* fadd st,st(i) */
int i = FPU_rm;
clear_C1();
FPU_add(&st(i), FPU_gettagi(i), 0, control_word);
}
void fmul__(void)
{
/* fmul st,st(i) */
int i = FPU_rm;
clear_C1();
FPU_mul(&st(i), FPU_gettagi(i), 0, control_word);
}
void fsub__(void)
{
/* fsub st,st(i) */
clear_C1();
FPU_sub(0, FPU_rm, control_word);
}
void fsubr_(void)
{
/* fsubr st,st(i) */
clear_C1();
FPU_sub(REV, FPU_rm, control_word);
}
void fdiv__(void)
{
/* fdiv st,st(i) */
clear_C1();
FPU_div(0, FPU_rm, control_word);
}
void fdivr_(void)
{
/* fdivr st,st(i) */
clear_C1();
FPU_div(REV, FPU_rm, control_word);
}
void fadd_i(void)
{
/* fadd st(i),st */
int i = FPU_rm;
clear_C1();
FPU_add(&st(i), FPU_gettagi(i), i, control_word);
}
void fmul_i(void)
{
/* fmul st(i),st */
clear_C1();
FPU_mul(&st(0), FPU_gettag0(), FPU_rm, control_word);
}
void fsubri(void)
{
/* fsubr st(i),st */
clear_C1();
FPU_sub(DEST_RM, FPU_rm, control_word);
}
void fsub_i(void)
{
/* fsub st(i),st */
clear_C1();
FPU_sub(REV | DEST_RM, FPU_rm, control_word);
}
void fdivri(void)
{
/* fdivr st(i),st */
clear_C1();
FPU_div(DEST_RM, FPU_rm, control_word);
}
void fdiv_i(void)
{
/* fdiv st(i),st */
clear_C1();
FPU_div(REV | DEST_RM, FPU_rm, control_word);
}
void faddp_(void)
{
/* faddp st(i),st */
int i = FPU_rm;
clear_C1();
if (FPU_add(&st(i), FPU_gettagi(i), i, control_word) >= 0)
FPU_pop();
}
void fmulp_(void)
{
/* fmulp st(i),st */
clear_C1();
if (FPU_mul(&st(0), FPU_gettag0(), FPU_rm, control_word) >= 0)
FPU_pop();
}
void fsubrp(void)
{
/* fsubrp st(i),st */
clear_C1();
if (FPU_sub(DEST_RM, FPU_rm, control_word) >= 0)
FPU_pop();
}
void fsubp_(void)
{
/* fsubp st(i),st */
clear_C1();
if (FPU_sub(REV | DEST_RM, FPU_rm, control_word) >= 0)
FPU_pop();
}
void fdivrp(void)
{
/* fdivrp st(i),st */
clear_C1();
if (FPU_div(DEST_RM, FPU_rm, control_word) >= 0)
FPU_pop();
}
void fdivp_(void)
{
/* fdivp st(i),st */
clear_C1();
if (FPU_div(REV | DEST_RM, FPU_rm, control_word) >= 0)
FPU_pop();
}
-32
View File
@@ -1,32 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*---------------------------------------------------------------------------+
| fpu_asm.h |
| |
| Copyright (C) 1992,1995,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@suburbia.net |
| |
+---------------------------------------------------------------------------*/
#ifndef _FPU_ASM_H_
#define _FPU_ASM_H_
#include <linux/linkage.h>
#define EXCEPTION FPU_exception
#define PARAM1 8(%ebp)
#define PARAM2 12(%ebp)
#define PARAM3 16(%ebp)
#define PARAM4 20(%ebp)
#define PARAM5 24(%ebp)
#define PARAM6 28(%ebp)
#define PARAM7 32(%ebp)
#define SIGL_OFFSET 0
#define EXP(x) 8(x)
#define SIG(x) SIGL_OFFSET##(x)
#define SIGL(x) SIGL_OFFSET##(x)
#define SIGH(x) 4(x)
#endif /* _FPU_ASM_H_ */
-267
View File
@@ -1,267 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*---------------------------------------------------------------------------+
| fpu_aux.c |
| |
| Code to implement some of the FPU auxiliary instructions. |
| |
| Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| E-mail billm@suburbia.net |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "control_w.h"
static void fnop(void)
{
}
static void fclex(void)
{
partial_status &=
~(SW_Backward | SW_Summary | SW_Stack_Fault | SW_Precision |
SW_Underflow | SW_Overflow | SW_Zero_Div | SW_Denorm_Op |
SW_Invalid);
no_ip_update = 1;
}
/* Needs to be externally visible */
void fpstate_init_soft(struct swregs_state *soft)
{
struct address *oaddr, *iaddr;
memset(soft, 0, sizeof(*soft));
soft->cwd = 0x037f;
soft->swd = 0;
soft->ftop = 0; /* We don't keep top in the status word internally. */
soft->twd = 0xffff;
/* The behaviour is different from that detailed in
Section 15.1.6 of the Intel manual */
oaddr = (struct address *)&soft->foo;
oaddr->offset = 0;
oaddr->selector = 0;
iaddr = (struct address *)&soft->fip;
iaddr->offset = 0;
iaddr->selector = 0;
iaddr->opcode = 0;
soft->no_update = 1;
}
void finit(void)
{
fpstate_init_soft(&x86_task_fpu(current)->fpstate->regs.soft);
}
/*
* These are nops on the i387..
*/
#define feni fnop
#define fdisi fnop
#define fsetpm fnop
static FUNC const finit_table[] = {
feni, fdisi, fclex, finit,
fsetpm, FPU_illegal, FPU_illegal, FPU_illegal
};
void finit_(void)
{
(finit_table[FPU_rm]) ();
}
static void fstsw_ax(void)
{
*(short *)&FPU_EAX = status_word();
no_ip_update = 1;
}
static FUNC const fstsw_table[] = {
fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal,
FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
};
void fstsw_(void)
{
(fstsw_table[FPU_rm]) ();
}
static FUNC const fp_nop_table[] = {
fnop, FPU_illegal, FPU_illegal, FPU_illegal,
FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
};
void fp_nop(void)
{
(fp_nop_table[FPU_rm]) ();
}
void fld_i_(void)
{
FPU_REG *st_new_ptr;
int i;
u_char tag;
if (STACK_OVERFLOW) {
FPU_stack_overflow();
return;
}
/* fld st(i) */
i = FPU_rm;
if (NOT_EMPTY(i)) {
reg_copy(&st(i), st_new_ptr);
tag = FPU_gettagi(i);
push();
FPU_settag0(tag);
} else {
if (control_word & CW_Invalid) {
/* The masked response */
FPU_stack_underflow();
} else
EXCEPTION(EX_StackUnder);
}
}
void fxch_i(void)
{
/* fxch st(i) */
FPU_REG t;
int i = FPU_rm;
FPU_REG *st0_ptr = &st(0), *sti_ptr = &st(i);
long tag_word = fpu_tag_word;
int regnr = top & 7, regnri = ((regnr + i) & 7);
u_char st0_tag = (tag_word >> (regnr * 2)) & 3;
u_char sti_tag = (tag_word >> (regnri * 2)) & 3;
if (st0_tag == TAG_Empty) {
if (sti_tag == TAG_Empty) {
FPU_stack_underflow();
FPU_stack_underflow_i(i);
return;
}
if (control_word & CW_Invalid) {
/* Masked response */
FPU_copy_to_reg0(sti_ptr, sti_tag);
}
FPU_stack_underflow_i(i);
return;
}
if (sti_tag == TAG_Empty) {
if (control_word & CW_Invalid) {
/* Masked response */
FPU_copy_to_regi(st0_ptr, st0_tag, i);
}
FPU_stack_underflow();
return;
}
clear_C1();
reg_copy(st0_ptr, &t);
reg_copy(sti_ptr, st0_ptr);
reg_copy(&t, sti_ptr);
tag_word &= ~(3 << (regnr * 2)) & ~(3 << (regnri * 2));
tag_word |= (sti_tag << (regnr * 2)) | (st0_tag << (regnri * 2));
fpu_tag_word = tag_word;
}
static void fcmovCC(void)
{
/* fcmovCC st(i) */
int i = FPU_rm;
FPU_REG *st0_ptr = &st(0);
FPU_REG *sti_ptr = &st(i);
long tag_word = fpu_tag_word;
int regnr = top & 7;
int regnri = (top + i) & 7;
u_char sti_tag = (tag_word >> (regnri * 2)) & 3;
if (sti_tag == TAG_Empty) {
FPU_stack_underflow();
clear_C1();
return;
}
reg_copy(sti_ptr, st0_ptr);
tag_word &= ~(3 << (regnr * 2));
tag_word |= (sti_tag << (regnr * 2));
fpu_tag_word = tag_word;
}
void fcmovb(void)
{
if (FPU_EFLAGS & X86_EFLAGS_CF)
fcmovCC();
}
void fcmove(void)
{
if (FPU_EFLAGS & X86_EFLAGS_ZF)
fcmovCC();
}
void fcmovbe(void)
{
if (FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF))
fcmovCC();
}
void fcmovu(void)
{
if (FPU_EFLAGS & X86_EFLAGS_PF)
fcmovCC();
}
void fcmovnb(void)
{
if (!(FPU_EFLAGS & X86_EFLAGS_CF))
fcmovCC();
}
void fcmovne(void)
{
if (!(FPU_EFLAGS & X86_EFLAGS_ZF))
fcmovCC();
}
void fcmovnbe(void)
{
if (!(FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF)))
fcmovCC();
}
void fcmovnu(void)
{
if (!(FPU_EFLAGS & X86_EFLAGS_PF))
fcmovCC();
}
void ffree_(void)
{
/* ffree st(i) */
FPU_settagi(FPU_rm, TAG_Empty);
}
void ffreep(void)
{
/* ffree st(i) + pop - unofficial code */
FPU_settagi(FPU_rm, TAG_Empty);
FPU_pop();
}
void fst_i_(void)
{
/* fst st(i) */
FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
}
void fstp_i(void)
{
/* fstp st(i) */
FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
FPU_pop();
}
-218
View File
@@ -1,218 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*---------------------------------------------------------------------------+
| fpu_emu.h |
| |
| Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@suburbia.net |
| |
+---------------------------------------------------------------------------*/
#ifndef _FPU_EMU_H_
#define _FPU_EMU_H_
/*
* Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
* rather than behaviour which appears to be cleaner.
* This is a matter of opinion: for all I know, the 80486 may simply
* be complying with the IEEE spec. Maybe one day I'll get to see the
* spec...
*/
#define PECULIAR_486
#ifdef __ASSEMBLER__
#include "fpu_asm.h"
#define Const(x) $##x
#else
#define Const(x) x
#endif
#define EXP_BIAS Const(0)
#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
#define EXP_WAY_UNDER Const(-0x6000) /* Below the smallest denormal, but
still a 16 bit nr. */
#define EXP_Infinity EXP_OVER
#define EXP_NaN EXP_OVER
#define EXTENDED_Ebias Const(0x3fff)
#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */
#define SIGN_POS Const(0)
#define SIGN_NEG Const(0x80)
#define SIGN_Positive Const(0)
#define SIGN_Negative Const(0x8000)
/* Keep the order TAG_Valid, TAG_Zero, TW_Denormal */
/* The following fold to 2 (Special) in the Tag Word */
#define TW_Denormal Const(4) /* De-normal */
#define TW_Infinity Const(5) /* + or - infinity */
#define TW_NaN Const(6) /* Not a Number */
#define TW_Unsupported Const(7) /* Not supported by an 80486 */
#define TAG_Valid Const(0) /* valid */
#define TAG_Zero Const(1) /* zero */
#define TAG_Special Const(2) /* De-normal, + or - infinity,
or Not a Number */
#define TAG_Empty Const(3) /* empty */
#define TAG_Error Const(0x80) /* probably need to abort */
#define LOADED_DATA Const(10101) /* Special st() number to identify
loaded data (not on stack). */
/* A few flags (must be >= 0x10). */
#define REV 0x10
#define DEST_RM 0x20
#define LOADED 0x40
#define FPU_Exception Const(0x80000000) /* Added to tag returns. */
#ifndef __ASSEMBLER__
#include "fpu_system.h"
#include <uapi/asm/sigcontext.h> /* for struct _fpstate */
#include <asm/math_emu.h>
#include <linux/linkage.h>
/*
#define RE_ENTRANT_CHECKING
*/
#ifdef RE_ENTRANT_CHECKING
extern u_char emulating;
# define RE_ENTRANT_CHECK_OFF emulating = 0
# define RE_ENTRANT_CHECK_ON emulating = 1
#else
# define RE_ENTRANT_CHECK_OFF
# define RE_ENTRANT_CHECK_ON
#endif /* RE_ENTRANT_CHECKING */
#define FWAIT_OPCODE 0x9b
#define OP_SIZE_PREFIX 0x66
#define ADDR_SIZE_PREFIX 0x67
#define PREFIX_CS 0x2e
#define PREFIX_DS 0x3e
#define PREFIX_ES 0x26
#define PREFIX_SS 0x36
#define PREFIX_FS 0x64
#define PREFIX_GS 0x65
#define PREFIX_REPE 0xf3
#define PREFIX_REPNE 0xf2
#define PREFIX_LOCK 0xf0
#define PREFIX_CS_ 1
#define PREFIX_DS_ 2
#define PREFIX_ES_ 3
#define PREFIX_FS_ 4
#define PREFIX_GS_ 5
#define PREFIX_SS_ 6
#define PREFIX_DEFAULT 7
struct address {
unsigned int offset;
unsigned int selector:16;
unsigned int opcode:11;
unsigned int empty:5;
};
struct fpu__reg {
unsigned sigl;
unsigned sigh;
short exp;
};
typedef void (*FUNC) (void);
typedef struct fpu__reg FPU_REG;
typedef void (*FUNC_ST0) (FPU_REG *st0_ptr, u_char st0_tag);
typedef struct {
u_char address_size, operand_size, segment;
} overrides;
/* This structure is 32 bits: */
typedef struct {
overrides override;
u_char default_mode;
} fpu_addr_modes;
/* PROTECTED has a restricted meaning in the emulator; it is used
to signal that the emulator needs to do special things to ensure
that protection is respected in a segmented model. */
#define PROTECTED 4
#define SIXTEEN 1 /* We rely upon this being 1 (true) */
#define VM86 SIXTEEN
#define PM16 (SIXTEEN | PROTECTED)
#define SEG32 PROTECTED
extern u_char const data_sizes_16[32];
#define register_base ((u_char *) registers )
#define fpu_register(x) ( * ((FPU_REG *)( register_base + 10 * (x & 7) )) )
#define st(x) ( * ((FPU_REG *)( register_base + 10 * ((top+x) & 7) )) )
#define STACK_OVERFLOW (FPU_stackoverflow(&st_new_ptr))
#define NOT_EMPTY(i) (!FPU_empty_i(i))
#define NOT_EMPTY_ST0 (st0_tag ^ TAG_Empty)
#define poppop() { FPU_pop(); FPU_pop(); }
/* push() does not affect the tags */
#define push() { top--; }
#define signbyte(a) (((u_char *)(a))[9])
#define getsign(a) (signbyte(a) & 0x80)
#define setsign(a,b) { if ((b) != 0) signbyte(a) |= 0x80; else signbyte(a) &= 0x7f; }
#define copysign(a,b) { if (getsign(a)) signbyte(b) |= 0x80; \
else signbyte(b) &= 0x7f; }
#define changesign(a) { signbyte(a) ^= 0x80; }
#define setpositive(a) { signbyte(a) &= 0x7f; }
#define setnegative(a) { signbyte(a) |= 0x80; }
#define signpositive(a) ( (signbyte(a) & 0x80) == 0 )
#define signnegative(a) (signbyte(a) & 0x80)
static inline void reg_copy(FPU_REG const *x, FPU_REG *y)
{
*(short *)&(y->exp) = *(const short *)&(x->exp);
*(long long *)&(y->sigl) = *(const long long *)&(x->sigl);
}
#define exponent(x) (((*(short *)&((x)->exp)) & 0x7fff) - EXTENDED_Ebias)
#define setexponentpos(x,y) { (*(short *)&((x)->exp)) = \
((y) + EXTENDED_Ebias) & 0x7fff; }
#define exponent16(x) (*(short *)&((x)->exp))
#define setexponent16(x,y) { (*(short *)&((x)->exp)) = (u16)(y); }
#define addexponent(x,y) { (*(short *)&((x)->exp)) += (y); }
#define stdexp(x) { (*(short *)&((x)->exp)) += EXTENDED_Ebias; }
#define isdenormal(ptr) (exponent(ptr) == EXP_BIAS+EXP_UNDER)
#define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] )
/*----- Prototypes for functions written in assembler -----*/
/* extern void reg_move(FPU_REG *a, FPU_REG *b); */
asmlinkage int FPU_normalize(FPU_REG *x);
asmlinkage int FPU_normalize_nuo(FPU_REG *x);
asmlinkage int FPU_u_sub(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG * answ, unsigned int control_w, u_char sign,
int expa, int expb);
asmlinkage int FPU_u_mul(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG * answ, unsigned int control_w, u_char sign,
int expon);
asmlinkage int FPU_u_div(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG * answ, unsigned int control_w, u_char sign);
asmlinkage int FPU_u_add(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG * answ, unsigned int control_w, u_char sign,
int expa, int expb);
asmlinkage int wm_sqrt(FPU_REG *n, int dummy1, int dummy2,
unsigned int control_w, u_char sign);
asmlinkage unsigned FPU_shrx(void *l, unsigned x);
asmlinkage unsigned FPU_shrxs(void *v, unsigned x);
asmlinkage unsigned long FPU_div_small(unsigned long long *x, unsigned long y);
asmlinkage int FPU_round(FPU_REG *arg, unsigned int extent, int dummy,
unsigned int control_w, u_char sign);
#ifndef MAKING_PROTO
#include "fpu_proto.h"
#endif
#endif /* __ASSEMBLER__ */
#endif /* _FPU_EMU_H_ */
-718
View File
@@ -1,718 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*---------------------------------------------------------------------------+
| fpu_entry.c |
| |
| The entry functions for wm-FPU-emu |
| |
| Copyright (C) 1992,1993,1994,1996,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| E-mail billm@suburbia.net |
| |
| See the files "README" and "COPYING" for further copyright and warranty |
| information. |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| math_emulate(), restore_i387_soft() and save_i387_soft() are the only |
| entry points for wm-FPU-emu. |
+---------------------------------------------------------------------------*/
#include <linux/signal.h>
#include <linux/regset.h>
#include <linux/uaccess.h>
#include <asm/traps.h>
#include <asm/user.h>
#include <asm/fpu/api.h>
#include <asm/fpu/regset.h>
#include "fpu_system.h"
#include "fpu_emu.h"
#include "exception.h"
#include "control_w.h"
#include "status_w.h"
#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
/* fcmovCC and f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */
/* WARNING: "u" entries are not documented by Intel in their 80486 manual
and may not work on FPU clones or later Intel FPUs.
Changes to support them provided by Linus Torvalds. */
static FUNC const st_instr_table[64] = {
/* Opcode: d8 d9 da db */
/* dc dd de df */
/* c0..7 */ fadd__, fld_i_, fcmovb, fcmovnb,
/* c0..7 */ fadd_i, ffree_, faddp_, ffreep,/*u*/
/* c8..f */ fmul__, fxch_i, fcmove, fcmovne,
/* c8..f */ fmul_i, fxch_i,/*u*/ fmulp_, fxch_i,/*u*/
/* d0..7 */ fcom_st, fp_nop, fcmovbe, fcmovnbe,
/* d0..7 */ fcom_st,/*u*/ fst_i_, fcompst,/*u*/ fstp_i,/*u*/
/* d8..f */ fcompst, fstp_i,/*u*/ fcmovu, fcmovnu,
/* d8..f */ fcompst,/*u*/ fstp_i, fcompp, fstp_i,/*u*/
/* e0..7 */ fsub__, FPU_etc, __BAD__, finit_,
/* e0..7 */ fsubri, fucom_, fsubrp, fstsw_,
/* e8..f */ fsubr_, fconst, fucompp, fucomi_,
/* e8..f */ fsub_i, fucomp, fsubp_, fucomip,
/* f0..7 */ fdiv__, FPU_triga, __BAD__, fcomi_,
/* f0..7 */ fdivri, __BAD__, fdivrp, fcomip,
/* f8..f */ fdivr_, FPU_trigb, __BAD__, __BAD__,
/* f8..f */ fdiv_i, __BAD__, fdivp_, __BAD__,
};
#define _NONE_ 0 /* Take no special action */
#define _REG0_ 1 /* Need to check for not empty st(0) */
#define _REGI_ 2 /* Need to check for not empty st(0) and st(rm) */
#define _REGi_ 0 /* Uses st(rm) */
#define _PUSH_ 3 /* Need to check for space to push onto stack */
#define _null_ 4 /* Function illegal or not implemented */
#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */
#define _REGIc 0 /* Compare st(0) and st(rm) */
#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
static u_char const type_table[64] = {
/* Opcode: d8 d9 da db dc dd de df */
/* c0..7 */ _REGI_, _NONE_, _REGIn, _REGIn, _REGIi, _REGi_, _REGIp, _REGi_,
/* c8..f */ _REGI_, _REGIn, _REGIn, _REGIn, _REGIi, _REGI_, _REGIp, _REGI_,
/* d0..7 */ _REGIc, _NONE_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
/* d8..f */ _REGIc, _REG0_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
/* e0..7 */ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
/* e8..f */ _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc,
/* f0..7 */ _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc,
/* f8..f */ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
};
#ifdef RE_ENTRANT_CHECKING
u_char emulating = 0;
#endif /* RE_ENTRANT_CHECKING */
static int valid_prefix(u_char *Byte, u_char __user ** fpu_eip,
overrides * override);
void math_emulate(struct math_emu_info *info)
{
u_char FPU_modrm, byte1;
unsigned short code;
fpu_addr_modes addr_modes;
int unmasked;
FPU_REG loaded_data;
FPU_REG *st0_ptr;
u_char loaded_tag, st0_tag;
void __user *data_address;
struct address data_sel_off;
struct address entry_sel_off;
unsigned long code_base = 0;
unsigned long code_limit = 0; /* Initialized to stop compiler warnings */
struct desc_struct code_descriptor;
#ifdef RE_ENTRANT_CHECKING
if (emulating) {
printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
}
RE_ENTRANT_CHECK_ON;
#endif /* RE_ENTRANT_CHECKING */
FPU_info = info;
FPU_ORIG_EIP = FPU_EIP;
if ((FPU_EFLAGS & 0x00020000) != 0) {
/* Virtual 8086 mode */
addr_modes.default_mode = VM86;
FPU_EIP += code_base = FPU_CS << 4;
code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */
} else if (FPU_CS == __USER_CS && FPU_DS == __USER_DS) {
addr_modes.default_mode = 0;
} else if (FPU_CS == __KERNEL_CS) {
printk("math_emulate: %04x:%08lx\n", FPU_CS, FPU_EIP);
panic("Math emulation needed in kernel");
} else {
if ((FPU_CS & 4) != 4) { /* Must be in the LDT */
/* Can only handle segmented addressing via the LDT
for now, and it must be 16 bit */
printk("FPU emulator: Unsupported addressing mode\n");
math_abort(FPU_info, SIGILL);
}
code_descriptor = FPU_get_ldt_descriptor(FPU_CS);
if (code_descriptor.d) {
/* The above test may be wrong, the book is not clear */
/* Segmented 32 bit protected mode */
addr_modes.default_mode = SEG32;
} else {
/* 16 bit protected mode */
addr_modes.default_mode = PM16;
}
FPU_EIP += code_base = seg_get_base(&code_descriptor);
code_limit = seg_get_limit(&code_descriptor) + 1;
code_limit *= seg_get_granularity(&code_descriptor);
code_limit += code_base - 1;
if (code_limit < code_base)
code_limit = 0xffffffff;
}
FPU_lookahead = !(FPU_EFLAGS & X86_EFLAGS_TF);
if (!valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
&addr_modes.override)) {
RE_ENTRANT_CHECK_OFF;
printk
("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n"
"FPU emulator: self-modifying code! (emulation impossible)\n",
byte1);
RE_ENTRANT_CHECK_ON;
EXCEPTION(EX_INTERNAL | 0x126);
math_abort(FPU_info, SIGILL);
}
do_another_FPU_instruction:
no_ip_update = 0;
FPU_EIP++; /* We have fetched the prefix and first code bytes. */
if (addr_modes.default_mode) {
/* This checks for the minimum instruction bytes.
We also need to check any extra (address mode) code access. */
if (FPU_EIP > code_limit)
math_abort(FPU_info, SIGSEGV);
}
if ((byte1 & 0xf8) != 0xd8) {
if (byte1 == FWAIT_OPCODE) {
if (partial_status & SW_Summary)
goto do_the_FPU_interrupt;
else
goto FPU_fwait_done;
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL | 0x128);
math_abort(FPU_info, SIGILL);
#endif /* PARANOID */
}
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(1);
FPU_get_user(FPU_modrm, (u_char __user *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
if (partial_status & SW_Summary) {
/* Ignore the error for now if the current instruction is a no-wait
control instruction */
/* The 80486 manual contradicts itself on this topic,
but a real 80486 uses the following instructions:
fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
*/
code = (FPU_modrm << 8) | byte1;
if (!((((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */
(((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv,
fnstsw */
((code & 0xc000) != 0xc000))))) {
/*
* We need to simulate the action of the kernel to FPU
* interrupts here.
*/
do_the_FPU_interrupt:
FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */
RE_ENTRANT_CHECK_OFF;
current->thread.trap_nr = X86_TRAP_MF;
current->thread.error_code = 0;
send_sig(SIGFPE, current, 1);
return;
}
}
entry_sel_off.offset = FPU_ORIG_EIP;
entry_sel_off.selector = FPU_CS;
entry_sel_off.opcode = (byte1 << 8) | FPU_modrm;
entry_sel_off.empty = 0;
FPU_rm = FPU_modrm & 7;
if (FPU_modrm < 0300) {
/* All of these instructions use the mod/rm byte to get a data address */
if ((addr_modes.default_mode & SIXTEEN)
^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX))
data_address =
FPU_get_address_16(FPU_modrm, &FPU_EIP,
&data_sel_off, addr_modes);
else
data_address =
FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
addr_modes);
if (addr_modes.default_mode) {
if (FPU_EIP - 1 > code_limit)
math_abort(FPU_info, SIGSEGV);
}
if (!(byte1 & 1)) {
unsigned short status1 = partial_status;
st0_ptr = &st(0);
st0_tag = FPU_gettag0();
/* Stack underflow has priority */
if (NOT_EMPTY_ST0) {
if (addr_modes.default_mode & PROTECTED) {
/* This table works for 16 and 32 bit protected mode */
if (access_limit <
data_sizes_16[(byte1 >> 1) & 3])
math_abort(FPU_info, SIGSEGV);
}
unmasked = 0; /* Do this here to stop compiler warnings. */
switch ((byte1 >> 1) & 3) {
case 0:
unmasked =
FPU_load_single((float __user *)
data_address,
&loaded_data);
loaded_tag = unmasked & 0xff;
unmasked &= ~0xff;
break;
case 1:
loaded_tag =
FPU_load_int32((long __user *)
data_address,
&loaded_data);
break;
case 2:
unmasked =
FPU_load_double((double __user *)
data_address,
&loaded_data);
loaded_tag = unmasked & 0xff;
unmasked &= ~0xff;
break;
case 3:
default: /* Used here to suppress gcc warnings. */
loaded_tag =
FPU_load_int16((short __user *)
data_address,
&loaded_data);
break;
}
/* No more access to user memory, it is safe
to use static data now */
/* NaN operands have the next priority. */
/* We have to delay looking at st(0) until after
loading the data, because that data might contain an SNaN */
if (((st0_tag == TAG_Special) && isNaN(st0_ptr))
|| ((loaded_tag == TAG_Special)
&& isNaN(&loaded_data))) {
/* Restore the status word; we might have loaded a
denormal. */
partial_status = status1;
if ((FPU_modrm & 0x30) == 0x10) {
/* fcom or fcomp */
EXCEPTION(EX_Invalid);
setcc(SW_C3 | SW_C2 | SW_C0);
if ((FPU_modrm & 0x08)
&& (control_word &
CW_Invalid))
FPU_pop(); /* fcomp, masked, so we pop. */
} else {
if (loaded_tag == TAG_Special)
loaded_tag =
FPU_Special
(&loaded_data);
#ifdef PECULIAR_486
/* This is not really needed, but gives behaviour
identical to an 80486 */
if ((FPU_modrm & 0x28) == 0x20)
/* fdiv or fsub */
real_2op_NaN
(&loaded_data,
loaded_tag, 0,
&loaded_data);
else
#endif /* PECULIAR_486 */
/* fadd, fdivr, fmul, or fsubr */
real_2op_NaN
(&loaded_data,
loaded_tag, 0,
st0_ptr);
}
goto reg_mem_instr_done;
}
if (unmasked && !((FPU_modrm & 0x30) == 0x10)) {
/* Is not a comparison instruction. */
if ((FPU_modrm & 0x38) == 0x38) {
/* fdivr */
if ((st0_tag == TAG_Zero) &&
((loaded_tag == TAG_Valid)
|| (loaded_tag ==
TAG_Special
&&
isdenormal
(&loaded_data)))) {
if (FPU_divide_by_zero
(0,
getsign
(&loaded_data))
< 0) {
/* We use the fact here that the unmasked
exception in the loaded data was for a
denormal operand */
/* Restore the state of the denormal op bit */
partial_status
&=
~SW_Denorm_Op;
partial_status
|=
status1 &
SW_Denorm_Op;
} else
setsign(st0_ptr,
getsign
(&loaded_data));
}
}
goto reg_mem_instr_done;
}
switch ((FPU_modrm >> 3) & 7) {
case 0: /* fadd */
clear_C1();
FPU_add(&loaded_data, loaded_tag, 0,
control_word);
break;
case 1: /* fmul */
clear_C1();
FPU_mul(&loaded_data, loaded_tag, 0,
control_word);
break;
case 2: /* fcom */
FPU_compare_st_data(&loaded_data,
loaded_tag);
break;
case 3: /* fcomp */
if (!FPU_compare_st_data
(&loaded_data, loaded_tag)
&& !unmasked)
FPU_pop();
break;
case 4: /* fsub */
clear_C1();
FPU_sub(LOADED | loaded_tag,
(int)&loaded_data,
control_word);
break;
case 5: /* fsubr */
clear_C1();
FPU_sub(REV | LOADED | loaded_tag,
(int)&loaded_data,
control_word);
break;
case 6: /* fdiv */
clear_C1();
FPU_div(LOADED | loaded_tag,
(int)&loaded_data,
control_word);
break;
case 7: /* fdivr */
clear_C1();
if (st0_tag == TAG_Zero)
partial_status = status1; /* Undo any denorm tag,
zero-divide has priority. */
FPU_div(REV | LOADED | loaded_tag,
(int)&loaded_data,
control_word);
break;
}
} else {
if ((FPU_modrm & 0x30) == 0x10) {
/* The instruction is fcom or fcomp */
EXCEPTION(EX_StackUnder);
setcc(SW_C3 | SW_C2 | SW_C0);
if ((FPU_modrm & 0x08)
&& (control_word & CW_Invalid))
FPU_pop(); /* fcomp */
} else
FPU_stack_underflow();
}
reg_mem_instr_done:
operand_address = data_sel_off;
} else {
if (!(no_ip_update =
FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6))
>> 1, addr_modes, data_address))) {
operand_address = data_sel_off;
}
}
} else {
/* None of these instructions access user memory */
u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
#ifdef PECULIAR_486
/* This is supposed to be undefined, but a real 80486 seems
to do this: */
operand_address.offset = 0;
operand_address.selector = FPU_DS;
#endif /* PECULIAR_486 */
st0_ptr = &st(0);
st0_tag = FPU_gettag0();
switch (type_table[(int)instr_index]) {
case _NONE_: /* also _REGIc: _REGIn */
break;
case _REG0_:
if (!NOT_EMPTY_ST0) {
FPU_stack_underflow();
goto FPU_instruction_done;
}
break;
case _REGIi:
if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
FPU_stack_underflow_i(FPU_rm);
goto FPU_instruction_done;
}
break;
case _REGIp:
if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
FPU_stack_underflow_pop(FPU_rm);
goto FPU_instruction_done;
}
break;
case _REGI_:
if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
FPU_stack_underflow();
goto FPU_instruction_done;
}
break;
case _PUSH_: /* Only used by the fld st(i) instruction */
break;
case _null_:
FPU_illegal();
goto FPU_instruction_done;
default:
EXCEPTION(EX_INTERNAL | 0x111);
goto FPU_instruction_done;
}
(*st_instr_table[(int)instr_index]) ();
FPU_instruction_done:
;
}
if (!no_ip_update)
instruction_address = entry_sel_off;
FPU_fwait_done:
#ifdef DEBUG
RE_ENTRANT_CHECK_OFF;
FPU_printall();
RE_ENTRANT_CHECK_ON;
#endif /* DEBUG */
if (FPU_lookahead && !need_resched()) {
FPU_ORIG_EIP = FPU_EIP - code_base;
if (valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
&addr_modes.override))
goto do_another_FPU_instruction;
}
if (addr_modes.default_mode)
FPU_EIP -= code_base;
RE_ENTRANT_CHECK_OFF;
}
/* Support for prefix bytes is not yet complete. To properly handle
all prefix bytes, further changes are needed in the emulator code
which accesses user address space. Access to separate segments is
important for msdos emulation. */
static int valid_prefix(u_char *Byte, u_char __user **fpu_eip,
overrides * override)
{
u_char byte;
u_char __user *ip = *fpu_eip;
*override = (overrides) {
0, 0, PREFIX_DEFAULT}; /* defaults */
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(1);
FPU_get_user(byte, ip);
RE_ENTRANT_CHECK_ON;
while (1) {
switch (byte) {
case ADDR_SIZE_PREFIX:
override->address_size = ADDR_SIZE_PREFIX;
goto do_next_byte;
case OP_SIZE_PREFIX:
override->operand_size = OP_SIZE_PREFIX;
goto do_next_byte;
case PREFIX_CS:
override->segment = PREFIX_CS_;
goto do_next_byte;
case PREFIX_ES:
override->segment = PREFIX_ES_;
goto do_next_byte;
case PREFIX_SS:
override->segment = PREFIX_SS_;
goto do_next_byte;
case PREFIX_FS:
override->segment = PREFIX_FS_;
goto do_next_byte;
case PREFIX_GS:
override->segment = PREFIX_GS_;
goto do_next_byte;
case PREFIX_DS:
override->segment = PREFIX_DS_;
goto do_next_byte;
/* lock is not a valid prefix for FPU instructions,
let the cpu handle it to generate a SIGILL. */
/* case PREFIX_LOCK: */
/* rep.. prefixes have no meaning for FPU instructions */
case PREFIX_REPE:
case PREFIX_REPNE:
do_next_byte:
ip++;
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(1);
FPU_get_user(byte, ip);
RE_ENTRANT_CHECK_ON;
break;
case FWAIT_OPCODE:
*Byte = byte;
return 1;
default:
if ((byte & 0xf8) == 0xd8) {
*Byte = byte;
*fpu_eip = ip;
return 1;
} else {
/* Not a valid sequence of prefix bytes followed by
an FPU instruction. */
*Byte = byte; /* Needed for error message. */
return 0;
}
}
}
}
void math_abort(struct math_emu_info *info, unsigned int signal)
{
FPU_EIP = FPU_ORIG_EIP;
current->thread.trap_nr = X86_TRAP_MF;
current->thread.error_code = 0;
send_sig(signal, current, 1);
RE_ENTRANT_CHECK_OFF;
__asm__("movl %0,%%esp ; ret": :"g"(((long)info) - 4));
#ifdef PARANOID
printk("ERROR: wm-FPU-emu math_abort failed!\n");
#endif /* PARANOID */
}
#define S387 ((struct swregs_state *)s387)
#define sstatus_word() \
((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
int fpregs_soft_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
struct swregs_state *s387 = &x86_task_fpu(target)->fpstate->regs.soft;
void *space = s387->st_space;
int ret;
int offset, other, i, tags, regnr, tag, newtop;
RE_ENTRANT_CHECK_OFF;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, s387, 0,
offsetof(struct swregs_state, st_space));
RE_ENTRANT_CHECK_ON;
if (ret)
return ret;
S387->ftop = (S387->swd >> SW_Top_Shift) & 7;
offset = (S387->ftop & 7) * 10;
other = 80 - offset;
RE_ENTRANT_CHECK_OFF;
/* Copy all registers in stack order. */
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
space + offset, 0, other);
if (!ret && offset)
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
space, 0, offset);
RE_ENTRANT_CHECK_ON;
/* The tags may need to be corrected now. */
tags = S387->twd;
newtop = S387->ftop;
for (i = 0; i < 8; i++) {
regnr = (i + newtop) & 7;
if (((tags >> ((regnr & 7) * 2)) & 3) != TAG_Empty) {
/* The loaded data over-rides all other cases. */
tag =
FPU_tagof((FPU_REG *) ((u_char *) S387->st_space +
10 * regnr));
tags &= ~(3 << (regnr * 2));
tags |= (tag & 3) << (regnr * 2);
}
}
S387->twd = tags;
return ret;
}
int fpregs_soft_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
struct swregs_state *s387 = &x86_task_fpu(target)->fpstate->regs.soft;
const void *space = s387->st_space;
int offset = (S387->ftop & 7) * 10, other = 80 - offset;
RE_ENTRANT_CHECK_OFF;
#ifdef PECULIAR_486
S387->cwd &= ~0xe080;
/* An 80486 sets nearly all of the reserved bits to 1. */
S387->cwd |= 0xffff0040;
S387->swd = sstatus_word() | 0xffff0000;
S387->twd |= 0xffff0000;
S387->fcs &= ~0xf8000000;
S387->fos |= 0xffff0000;
#endif /* PECULIAR_486 */
membuf_write(&to, s387, offsetof(struct swregs_state, st_space));
membuf_write(&to, space + offset, other);
membuf_write(&to, space, offset);
RE_ENTRANT_CHECK_ON;
return 0;
}
-136
View File
@@ -1,136 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*---------------------------------------------------------------------------+
| fpu_etc.c |
| |
| Implement a few FPU instructions. |
| |
| Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@suburbia.net |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "reg_constant.h"
static void fchs(FPU_REG *st0_ptr, u_char st0tag)
{
if (st0tag ^ TAG_Empty) {
signbyte(st0_ptr) ^= SIGN_NEG;
clear_C1();
} else
FPU_stack_underflow();
}
static void fabs(FPU_REG *st0_ptr, u_char st0tag)
{
if (st0tag ^ TAG_Empty) {
setpositive(st0_ptr);
clear_C1();
} else
FPU_stack_underflow();
}
static void ftst_(FPU_REG *st0_ptr, u_char st0tag)
{
switch (st0tag) {
case TAG_Zero:
setcc(SW_C3);
break;
case TAG_Valid:
if (getsign(st0_ptr) == SIGN_POS)
setcc(0);
else
setcc(SW_C0);
break;
case TAG_Special:
switch (FPU_Special(st0_ptr)) {
case TW_Denormal:
if (getsign(st0_ptr) == SIGN_POS)
setcc(0);
else
setcc(SW_C0);
if (denormal_operand() < 0) {
#ifdef PECULIAR_486
/* This is weird! */
if (getsign(st0_ptr) == SIGN_POS)
setcc(SW_C3);
#endif /* PECULIAR_486 */
return;
}
break;
case TW_NaN:
setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */
EXCEPTION(EX_Invalid);
break;
case TW_Infinity:
if (getsign(st0_ptr) == SIGN_POS)
setcc(0);
else
setcc(SW_C0);
break;
default:
setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */
EXCEPTION(EX_INTERNAL | 0x14);
break;
}
break;
case TAG_Empty:
setcc(SW_C0 | SW_C2 | SW_C3);
EXCEPTION(EX_StackUnder);
break;
}
}
static void fxam(FPU_REG *st0_ptr, u_char st0tag)
{
int c = 0;
switch (st0tag) {
case TAG_Empty:
c = SW_C3 | SW_C0;
break;
case TAG_Zero:
c = SW_C3;
break;
case TAG_Valid:
c = SW_C2;
break;
case TAG_Special:
switch (FPU_Special(st0_ptr)) {
case TW_Denormal:
c = SW_C2 | SW_C3; /* Denormal */
break;
case TW_NaN:
/* We also use NaN for unsupported types. */
if ((st0_ptr->sigh & 0x80000000)
&& (exponent(st0_ptr) == EXP_OVER))
c = SW_C0;
break;
case TW_Infinity:
c = SW_C2 | SW_C0;
break;
}
}
if (getsign(st0_ptr) == SIGN_NEG)
c |= SW_C1;
setcc(c);
}
static void FPU_ST0_illegal(FPU_REG *st0_ptr, u_char st0_tag)
{
FPU_illegal();
}
static FUNC_ST0 const fp_etc_table[] = {
fchs, fabs, FPU_ST0_illegal, FPU_ST0_illegal,
ftst_, fxam, FPU_ST0_illegal, FPU_ST0_illegal,
};
void FPU_etc(void)
{
(fp_etc_table[FPU_rm]) (&st(0), FPU_gettag0());
}
-157
View File
@@ -1,157 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _FPU_PROTO_H
#define _FPU_PROTO_H
/* errors.c */
extern void FPU_illegal(void);
extern void FPU_printall(void);
asmlinkage void FPU_exception(int n);
extern int real_1op_NaN(FPU_REG *a);
extern int real_2op_NaN(FPU_REG const *b, u_char tagb, int deststnr,
FPU_REG const *defaultNaN);
asmlinkage int arith_invalid(int deststnr);
asmlinkage int FPU_divide_by_zero(int deststnr, u_char sign);
extern int set_precision_flag(int flags);
asmlinkage void set_precision_flag_up(void);
asmlinkage void set_precision_flag_down(void);
asmlinkage int denormal_operand(void);
asmlinkage int arith_overflow(FPU_REG *dest);
asmlinkage int arith_underflow(FPU_REG *dest);
extern void FPU_stack_overflow(void);
extern void FPU_stack_underflow(void);
extern void FPU_stack_underflow_i(int i);
extern void FPU_stack_underflow_pop(int i);
/* fpu_arith.c */
extern void fadd__(void);
extern void fmul__(void);
extern void fsub__(void);
extern void fsubr_(void);
extern void fdiv__(void);
extern void fdivr_(void);
extern void fadd_i(void);
extern void fmul_i(void);
extern void fsubri(void);
extern void fsub_i(void);
extern void fdivri(void);
extern void fdiv_i(void);
extern void faddp_(void);
extern void fmulp_(void);
extern void fsubrp(void);
extern void fsubp_(void);
extern void fdivrp(void);
extern void fdivp_(void);
/* fpu_aux.c */
extern void finit(void);
extern void finit_(void);
extern void fstsw_(void);
extern void fp_nop(void);
extern void fld_i_(void);
extern void fxch_i(void);
extern void fcmovb(void);
extern void fcmove(void);
extern void fcmovbe(void);
extern void fcmovu(void);
extern void fcmovnb(void);
extern void fcmovne(void);
extern void fcmovnbe(void);
extern void fcmovnu(void);
extern void ffree_(void);
extern void ffreep(void);
extern void fst_i_(void);
extern void fstp_i(void);
/* fpu_entry.c */
extern void math_emulate(struct math_emu_info *info);
extern void math_abort(struct math_emu_info *info, unsigned int signal);
/* fpu_etc.c */
extern void FPU_etc(void);
/* fpu_tags.c */
extern int FPU_gettag0(void);
extern int FPU_gettagi(int stnr);
extern int FPU_gettag(int regnr);
extern void FPU_settag0(int tag);
extern void FPU_settagi(int stnr, int tag);
extern void FPU_settag(int regnr, int tag);
extern int FPU_Special(FPU_REG const *ptr);
extern int isNaN(FPU_REG const *ptr);
extern void FPU_pop(void);
extern int FPU_empty_i(int stnr);
extern int FPU_stackoverflow(FPU_REG ** st_new_ptr);
extern void FPU_copy_to_regi(FPU_REG const *r, u_char tag, int stnr);
extern void FPU_copy_to_reg1(FPU_REG const *r, u_char tag);
extern void FPU_copy_to_reg0(FPU_REG const *r, u_char tag);
/* fpu_trig.c */
extern void FPU_triga(void);
extern void FPU_trigb(void);
/* get_address.c */
extern void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
struct address *addr,
fpu_addr_modes addr_modes);
extern void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
struct address *addr,
fpu_addr_modes addr_modes);
/* load_store.c */
extern int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
void __user * data_address);
/* poly_2xm1.c */
extern int poly_2xm1(u_char sign, FPU_REG * arg, FPU_REG *result);
/* poly_atan.c */
extern void poly_atan(FPU_REG * st0_ptr, u_char st0_tag, FPU_REG *st1_ptr,
u_char st1_tag);
/* poly_l2.c */
extern void poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign);
extern int poly_l2p1(u_char s0, u_char s1, FPU_REG *r0, FPU_REG *r1,
FPU_REG * d);
/* poly_sin.c */
extern void poly_sine(FPU_REG *st0_ptr);
extern void poly_cos(FPU_REG *st0_ptr);
/* poly_tan.c */
extern void poly_tan(FPU_REG *st0_ptr);
/* reg_add_sub.c */
extern int FPU_add(FPU_REG const *b, u_char tagb, int destrnr, int control_w);
extern int FPU_sub(int flags, int rm, int control_w);
/* reg_compare.c */
extern int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag);
extern void fcom_st(void);
extern void fcompst(void);
extern void fcompp(void);
extern void fucom_(void);
extern void fucomp(void);
extern void fucompp(void);
extern void fcomi_(void);
extern void fcomip(void);
extern void fucomi_(void);
extern void fucomip(void);
/* reg_constant.c */
extern void fconst(void);
/* reg_ld_str.c */
extern int FPU_load_extended(long double __user *s, int stnr);
extern int FPU_load_double(double __user *dfloat, FPU_REG *loaded_data);
extern int FPU_load_single(float __user *single, FPU_REG *loaded_data);
extern int FPU_load_int64(long long __user *_s);
extern int FPU_load_int32(long __user *_s, FPU_REG *loaded_data);
extern int FPU_load_int16(short __user *_s, FPU_REG *loaded_data);
extern int FPU_load_bcd(u_char __user *s);
extern int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag,
long double __user * d);
extern int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag,
double __user * dfloat);
extern int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag,
float __user * single);
extern int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag,
long long __user * d);
extern int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long __user *d);
extern int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short __user *d);
extern int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char __user *d);
extern int FPU_round_to_int(FPU_REG *r, u_char tag);
extern u_char __user *fldenv(fpu_addr_modes addr_modes, u_char __user *s);
extern void FPU_frstor(fpu_addr_modes addr_modes, u_char __user *data_address);
extern u_char __user *fstenv(fpu_addr_modes addr_modes, u_char __user *d);
extern void fsave(fpu_addr_modes addr_modes, u_char __user *data_address);
extern int FPU_tagof(FPU_REG *ptr);
/* reg_mul.c */
extern int FPU_mul(FPU_REG const *b, u_char tagb, int deststnr, int control_w);
extern int FPU_div(int flags, int regrm, int control_w);
/* reg_convert.c */
extern int FPU_to_exp16(FPU_REG const *a, FPU_REG *x);
#endif /* _FPU_PROTO_H */
-130
View File
@@ -1,130 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*---------------------------------------------------------------------------+
| fpu_system.h |
| |
| Copyright (C) 1992,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@suburbia.net |
| |
+---------------------------------------------------------------------------*/
#ifndef _FPU_SYSTEM_H
#define _FPU_SYSTEM_H
/* system dependent definitions */
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/desc.h>
#include <asm/mmu_context.h>
static inline struct desc_struct FPU_get_ldt_descriptor(unsigned seg)
{
static struct desc_struct zero_desc;
struct desc_struct ret = zero_desc;
#ifdef CONFIG_MODIFY_LDT_SYSCALL
seg >>= 3;
mutex_lock(&current->mm->context.lock);
if (current->mm->context.ldt && seg < current->mm->context.ldt->nr_entries)
ret = current->mm->context.ldt->entries[seg];
mutex_unlock(&current->mm->context.lock);
#endif
return ret;
}
#define SEG_TYPE_WRITABLE (1U << 1)
#define SEG_TYPE_EXPANDS_DOWN (1U << 2)
#define SEG_TYPE_EXECUTE (1U << 3)
#define SEG_TYPE_EXPAND_MASK (SEG_TYPE_EXPANDS_DOWN | SEG_TYPE_EXECUTE)
#define SEG_TYPE_EXECUTE_MASK (SEG_TYPE_WRITABLE | SEG_TYPE_EXECUTE)
static inline unsigned long seg_get_base(struct desc_struct *d)
{
unsigned long base = (unsigned long)d->base2 << 24;
return base | ((unsigned long)d->base1 << 16) | d->base0;
}
static inline unsigned long seg_get_limit(struct desc_struct *d)
{
return ((unsigned long)d->limit1 << 16) | d->limit0;
}
static inline unsigned long seg_get_granularity(struct desc_struct *d)
{
return d->g ? 4096 : 1;
}
static inline bool seg_expands_down(struct desc_struct *d)
{
return (d->type & SEG_TYPE_EXPAND_MASK) == SEG_TYPE_EXPANDS_DOWN;
}
static inline bool seg_execute_only(struct desc_struct *d)
{
return (d->type & SEG_TYPE_EXECUTE_MASK) == SEG_TYPE_EXECUTE;
}
static inline bool seg_writable(struct desc_struct *d)
{
return (d->type & SEG_TYPE_EXECUTE_MASK) == SEG_TYPE_WRITABLE;
}
#define I387 (&x86_task_fpu(current)->fpstate->regs)
#define FPU_info (I387->soft.info)
#define FPU_CS (*(unsigned short *) &(FPU_info->regs->cs))
#define FPU_SS (*(unsigned short *) &(FPU_info->regs->ss))
#define FPU_DS (*(unsigned short *) &(FPU_info->regs->ds))
#define FPU_EAX (FPU_info->regs->ax)
#define FPU_EFLAGS (FPU_info->regs->flags)
#define FPU_EIP (FPU_info->regs->ip)
#define FPU_ORIG_EIP (FPU_info->___orig_eip)
#define FPU_lookahead (I387->soft.lookahead)
/* nz if ip_offset and cs_selector are not to be set for the current
instruction. */
#define no_ip_update (*(u_char *)&(I387->soft.no_update))
#define FPU_rm (*(u_char *)&(I387->soft.rm))
/* Number of bytes of data which can be legally accessed by the current
instruction. This only needs to hold a number <= 108, so a byte will do. */
#define access_limit (*(u_char *)&(I387->soft.alimit))
#define partial_status (I387->soft.swd)
#define control_word (I387->soft.cwd)
#define fpu_tag_word (I387->soft.twd)
#define registers (I387->soft.st_space)
#define top (I387->soft.ftop)
#define instruction_address (*(struct address *)&I387->soft.fip)
#define operand_address (*(struct address *)&I387->soft.foo)
#define FPU_access_ok(y,z) if ( !access_ok(y,z) ) \
math_abort(FPU_info,SIGSEGV)
#define FPU_abort math_abort(FPU_info, SIGSEGV)
#define FPU_copy_from_user(to, from, n) \
do { if (copy_from_user(to, from, n)) FPU_abort; } while (0)
#undef FPU_IGNORE_CODE_SEGV
#ifdef FPU_IGNORE_CODE_SEGV
/* access_ok() is very expensive, and causes the emulator to run
about 20% slower if applied to the code. Anyway, errors due to bad
code addresses should be much rarer than errors due to bad data
addresses. */
#define FPU_code_access_ok(z)
#else
/* A simpler test than access_ok() can probably be done for
FPU_code_access_ok() because the only possible error is to step
past the upper boundary of a legal code area. */
#define FPU_code_access_ok(z) FPU_access_ok((void __user *)FPU_EIP,z)
#endif
#define FPU_get_user(x,y) do { if (get_user((x),(y))) FPU_abort; } while (0)
#define FPU_put_user(x,y) do { if (put_user((x),(y))) FPU_abort; } while (0)
#endif
-116
View File
@@ -1,116 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*---------------------------------------------------------------------------+
| fpu_tags.c |
| |
| Set FPU register tags. |
| |
| Copyright (C) 1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| E-mail billm@jacobi.maths.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_emu.h"
#include "fpu_system.h"
#include "exception.h"
void FPU_pop(void)
{
fpu_tag_word |= 3 << ((top & 7) * 2);
top++;
}
int FPU_gettag0(void)
{
return (fpu_tag_word >> ((top & 7) * 2)) & 3;
}
int FPU_gettagi(int stnr)
{
return (fpu_tag_word >> (((top + stnr) & 7) * 2)) & 3;
}
int FPU_gettag(int regnr)
{
return (fpu_tag_word >> ((regnr & 7) * 2)) & 3;
}
void FPU_settag0(int tag)
{
int regnr = top;
regnr &= 7;
fpu_tag_word &= ~(3 << (regnr * 2));
fpu_tag_word |= (tag & 3) << (regnr * 2);
}
void FPU_settagi(int stnr, int tag)
{
int regnr = stnr + top;
regnr &= 7;
fpu_tag_word &= ~(3 << (regnr * 2));
fpu_tag_word |= (tag & 3) << (regnr * 2);
}
void FPU_settag(int regnr, int tag)
{
regnr &= 7;
fpu_tag_word &= ~(3 << (regnr * 2));
fpu_tag_word |= (tag & 3) << (regnr * 2);
}
int FPU_Special(FPU_REG const *ptr)
{
int exp = exponent(ptr);
if (exp == EXP_BIAS + EXP_UNDER)
return TW_Denormal;
else if (exp != EXP_BIAS + EXP_OVER)
return TW_NaN;
else if ((ptr->sigh == 0x80000000) && (ptr->sigl == 0))
return TW_Infinity;
return TW_NaN;
}
int isNaN(FPU_REG const *ptr)
{
return ((exponent(ptr) == EXP_BIAS + EXP_OVER)
&& !((ptr->sigh == 0x80000000) && (ptr->sigl == 0)));
}
int FPU_empty_i(int stnr)
{
int regnr = (top + stnr) & 7;
return ((fpu_tag_word >> (regnr * 2)) & 3) == TAG_Empty;
}
int FPU_stackoverflow(FPU_REG ** st_new_ptr)
{
*st_new_ptr = &st(-1);
return ((fpu_tag_word >> (((top - 1) & 7) * 2)) & 3) != TAG_Empty;
}
void FPU_copy_to_regi(FPU_REG const *r, u_char tag, int stnr)
{
reg_copy(r, &st(stnr));
FPU_settagi(stnr, tag);
}
void FPU_copy_to_reg1(FPU_REG const *r, u_char tag)
{
reg_copy(r, &st(1));
FPU_settagi(1, tag);
}
void FPU_copy_to_reg0(FPU_REG const *r, u_char tag)
{
int regnr = top;
regnr &= 7;
reg_copy(r, &st(0));
fpu_tag_word &= ~(3 << (regnr * 2));
fpu_tag_word |= (tag & 3) << (regnr * 2);
}
File diff suppressed because it is too large Load Diff
-401
View File
@@ -1,401 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*---------------------------------------------------------------------------+
| get_address.c |
| |
| Get the effective address from an FPU instruction. |
| |
| Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@suburbia.net |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <linux/stddef.h>
#include <linux/uaccess.h>
#include <asm/vm86.h>
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#define FPU_WRITE_BIT 0x10
static int reg_offset[] = {
offsetof(struct pt_regs, ax),
offsetof(struct pt_regs, cx),
offsetof(struct pt_regs, dx),
offsetof(struct pt_regs, bx),
offsetof(struct pt_regs, sp),
offsetof(struct pt_regs, bp),
offsetof(struct pt_regs, si),
offsetof(struct pt_regs, di)
};
#define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs))
static int reg_offset_vm86[] = {
offsetof(struct pt_regs, cs),
offsetof(struct kernel_vm86_regs, ds),
offsetof(struct kernel_vm86_regs, es),
offsetof(struct kernel_vm86_regs, fs),
offsetof(struct kernel_vm86_regs, gs),
offsetof(struct pt_regs, ss),
offsetof(struct kernel_vm86_regs, ds)
};
#define VM86_REG_(x) (*(unsigned short *) \
(reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs))
static int reg_offset_pm[] = {
offsetof(struct pt_regs, cs),
offsetof(struct pt_regs, ds),
offsetof(struct pt_regs, es),
offsetof(struct pt_regs, fs),
offsetof(struct pt_regs, ds), /* dummy, not saved on stack */
offsetof(struct pt_regs, ss),
offsetof(struct pt_regs, ds)
};
#define PM_REG_(x) (*(unsigned short *) \
(reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs))
/* Decode the SIB byte. This function assumes mod != 0 */
static int sib(int mod, unsigned long *fpu_eip)
{
u_char ss, index, base;
long offset;
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(1);
FPU_get_user(base, (u_char __user *) (*fpu_eip)); /* The SIB byte */
RE_ENTRANT_CHECK_ON;
(*fpu_eip)++;
ss = base >> 6;
index = (base >> 3) & 7;
base &= 7;
if ((mod == 0) && (base == 5))
offset = 0; /* No base register */
else
offset = REG_(base);
if (index == 4) {
/* No index register */
/* A non-zero ss is illegal */
if (ss)
EXCEPTION(EX_Invalid);
} else {
offset += (REG_(index)) << ss;
}
if (mod == 1) {
/* 8 bit signed displacement */
long displacement;
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(1);
FPU_get_user(displacement, (signed char __user *)(*fpu_eip));
offset += displacement;
RE_ENTRANT_CHECK_ON;
(*fpu_eip)++;
} else if (mod == 2 || base == 5) { /* The second condition also has mod==0 */
/* 32 bit displacement */
long displacement;
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(4);
FPU_get_user(displacement, (long __user *)(*fpu_eip));
offset += displacement;
RE_ENTRANT_CHECK_ON;
(*fpu_eip) += 4;
}
return offset;
}
static unsigned long vm86_segment(u_char segment, struct address *addr)
{
segment--;
#ifdef PARANOID
if (segment > PREFIX_SS_) {
EXCEPTION(EX_INTERNAL | 0x130);
math_abort(FPU_info, SIGSEGV);
}
#endif /* PARANOID */
addr->selector = VM86_REG_(segment);
return (unsigned long)VM86_REG_(segment) << 4;
}
/* This should work for 16 and 32 bit protected mode. */
static long pm_address(u_char FPU_modrm, u_char segment,
struct address *addr, long offset)
{
struct desc_struct descriptor;
unsigned long base_address, limit, address, seg_top;
segment--;
#ifdef PARANOID
/* segment is unsigned, so this also detects if segment was 0: */
if (segment > PREFIX_SS_) {
EXCEPTION(EX_INTERNAL | 0x132);
math_abort(FPU_info, SIGSEGV);
}
#endif /* PARANOID */
switch (segment) {
case PREFIX_GS_ - 1:
/* user gs handling can be lazy, use special accessors */
savesegment(gs, addr->selector);
break;
default:
addr->selector = PM_REG_(segment);
}
descriptor = FPU_get_ldt_descriptor(addr->selector);
base_address = seg_get_base(&descriptor);
address = base_address + offset;
limit = seg_get_limit(&descriptor) + 1;
limit *= seg_get_granularity(&descriptor);
limit += base_address - 1;
if (limit < base_address)
limit = 0xffffffff;
if (seg_expands_down(&descriptor)) {
if (descriptor.g) {
seg_top = 0xffffffff;
} else {
seg_top = base_address + (1 << 20);
if (seg_top < base_address)
seg_top = 0xffffffff;
}
access_limit =
(address <= limit) || (address >= seg_top) ? 0 :
((seg_top - address) >= 255 ? 255 : seg_top - address);
} else {
access_limit =
(address > limit) || (address < base_address) ? 0 :
((limit - address) >= 254 ? 255 : limit - address + 1);
}
if (seg_execute_only(&descriptor) ||
(!seg_writable(&descriptor) && (FPU_modrm & FPU_WRITE_BIT))) {
access_limit = 0;
}
return address;
}
/*
MOD R/M byte: MOD == 3 has a special use for the FPU
SIB byte used iff R/M = 100b
7 6 5 4 3 2 1 0
..... ......... .........
MOD OPCODE(2) R/M
SIB byte
7 6 5 4 3 2 1 0
..... ......... .........
SS INDEX BASE
*/
void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
struct address *addr, fpu_addr_modes addr_modes)
{
u_char mod;
unsigned rm = FPU_modrm & 7;
long *cpu_reg_ptr;
int address = 0; /* Initialized just to stop compiler warnings. */
/* Memory accessed via the cs selector is write protected
in `non-segmented' 32 bit protected mode. */
if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
&& (addr_modes.override.segment == PREFIX_CS_)) {
math_abort(FPU_info, SIGSEGV);
}
addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */
mod = (FPU_modrm >> 6) & 3;
if (rm == 4 && mod != 3) {
address = sib(mod, fpu_eip);
} else {
cpu_reg_ptr = &REG_(rm);
switch (mod) {
case 0:
if (rm == 5) {
/* Special case: disp32 */
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(4);
FPU_get_user(address,
(unsigned long __user
*)(*fpu_eip));
(*fpu_eip) += 4;
RE_ENTRANT_CHECK_ON;
addr->offset = address;
return (void __user *)address;
} else {
address = *cpu_reg_ptr; /* Just return the contents
of the cpu register */
addr->offset = address;
return (void __user *)address;
}
case 1:
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(1);
FPU_get_user(address, (signed char __user *)(*fpu_eip));
RE_ENTRANT_CHECK_ON;
(*fpu_eip)++;
break;
case 2:
/* 32 bit displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(4);
FPU_get_user(address, (long __user *)(*fpu_eip));
(*fpu_eip) += 4;
RE_ENTRANT_CHECK_ON;
break;
case 3:
/* Not legal for the FPU */
EXCEPTION(EX_Invalid);
}
address += *cpu_reg_ptr;
}
addr->offset = address;
switch (addr_modes.default_mode) {
case 0:
break;
case VM86:
address += vm86_segment(addr_modes.override.segment, addr);
break;
case PM16:
case SEG32:
address = pm_address(FPU_modrm, addr_modes.override.segment,
addr, address);
break;
default:
EXCEPTION(EX_INTERNAL | 0x133);
}
return (void __user *)address;
}
void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
struct address *addr, fpu_addr_modes addr_modes)
{
u_char mod;
unsigned rm = FPU_modrm & 7;
int address = 0; /* Default used for mod == 0 */
/* Memory accessed via the cs selector is write protected
in `non-segmented' 32 bit protected mode. */
if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
&& (addr_modes.override.segment == PREFIX_CS_)) {
math_abort(FPU_info, SIGSEGV);
}
addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */
mod = (FPU_modrm >> 6) & 3;
switch (mod) {
case 0:
if (rm == 6) {
/* Special case: disp16 */
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(2);
FPU_get_user(address,
(unsigned short __user *)(*fpu_eip));
(*fpu_eip) += 2;
RE_ENTRANT_CHECK_ON;
goto add_segment;
}
break;
case 1:
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(1);
FPU_get_user(address, (signed char __user *)(*fpu_eip));
RE_ENTRANT_CHECK_ON;
(*fpu_eip)++;
break;
case 2:
/* 16 bit displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_access_ok(2);
FPU_get_user(address, (unsigned short __user *)(*fpu_eip));
(*fpu_eip) += 2;
RE_ENTRANT_CHECK_ON;
break;
case 3:
/* Not legal for the FPU */
EXCEPTION(EX_Invalid);
break;
}
switch (rm) {
case 0:
address += FPU_info->regs->bx + FPU_info->regs->si;
break;
case 1:
address += FPU_info->regs->bx + FPU_info->regs->di;
break;
case 2:
address += FPU_info->regs->bp + FPU_info->regs->si;
if (addr_modes.override.segment == PREFIX_DEFAULT)
addr_modes.override.segment = PREFIX_SS_;
break;
case 3:
address += FPU_info->regs->bp + FPU_info->regs->di;
if (addr_modes.override.segment == PREFIX_DEFAULT)
addr_modes.override.segment = PREFIX_SS_;
break;
case 4:
address += FPU_info->regs->si;
break;
case 5:
address += FPU_info->regs->di;
break;
case 6:
address += FPU_info->regs->bp;
if (addr_modes.override.segment == PREFIX_DEFAULT)
addr_modes.override.segment = PREFIX_SS_;
break;
case 7:
address += FPU_info->regs->bx;
break;
}
add_segment:
address &= 0xffff;
addr->offset = address;
switch (addr_modes.default_mode) {
case 0:
break;
case VM86:
address += vm86_segment(addr_modes.override.segment, addr);
break;
case PM16:
case SEG32:
address = pm_address(FPU_modrm, addr_modes.override.segment,
addr, address);
break;
default:
EXCEPTION(EX_INTERNAL | 0x131);
}
return (void __user *)address;
}
-322
View File
@@ -1,322 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*---------------------------------------------------------------------------+
| load_store.c |
| |
| This file contains most of the code to interpret the FPU instructions |
| which load and store from user memory. |
| |
| Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@suburbia.net |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <linux/uaccess.h>
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "control_w.h"
#define _NONE_ 0 /* st0_ptr etc not needed */
#define _REG0_ 1 /* Will be storing st(0) */
#define _PUSH_ 3 /* Need to check for space to push onto stack */
#define _null_ 4 /* Function illegal or not implemented */
#define pop_0() { FPU_settag0(TAG_Empty); top++; }
/* index is a 5-bit value: (3-bit FPU_modrm.reg field | opcode[2,1]) */
static u_char const type_table[32] = {
_PUSH_, _PUSH_, _PUSH_, _PUSH_, /* /0: d9:fld f32, db:fild m32, dd:fld f64, df:fild m16 */
_null_, _REG0_, _REG0_, _REG0_, /* /1: d9:undef, db,dd,df:fisttp m32/64/16 */
_REG0_, _REG0_, _REG0_, _REG0_, /* /2: d9:fst f32, db:fist m32, dd:fst f64, df:fist m16 */
_REG0_, _REG0_, _REG0_, _REG0_, /* /3: d9:fstp f32, db:fistp m32, dd:fstp f64, df:fistp m16 */
_NONE_, _null_, _NONE_, _PUSH_,
_NONE_, _PUSH_, _null_, _PUSH_,
_NONE_, _null_, _NONE_, _REG0_,
_NONE_, _REG0_, _NONE_, _REG0_
};
u_char const data_sizes_16[32] = {
4, 4, 8, 2,
0, 4, 8, 2, /* /1: d9:undef, db,dd,df:fisttp */
4, 4, 8, 2,
4, 4, 8, 2,
14, 0, 94, 10, 2, 10, 0, 8,
14, 0, 94, 10, 2, 10, 2, 8
};
static u_char const data_sizes_32[32] = {
4, 4, 8, 2,
0, 4, 8, 2, /* /1: d9:undef, db,dd,df:fisttp */
4, 4, 8, 2,
4, 4, 8, 2,
28, 0, 108, 10, 2, 10, 0, 8,
28, 0, 108, 10, 2, 10, 2, 8
};
int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
void __user * data_address)
{
FPU_REG loaded_data;
FPU_REG *st0_ptr;
u_char st0_tag = TAG_Empty; /* This is just to stop a gcc warning. */
u_char loaded_tag;
int sv_cw;
st0_ptr = NULL; /* Initialized just to stop compiler warnings. */
if (addr_modes.default_mode & PROTECTED) {
if (addr_modes.default_mode == SEG32) {
if (access_limit < data_sizes_32[type])
math_abort(FPU_info, SIGSEGV);
} else if (addr_modes.default_mode == PM16) {
if (access_limit < data_sizes_16[type])
math_abort(FPU_info, SIGSEGV);
}
#ifdef PARANOID
else
EXCEPTION(EX_INTERNAL | 0x140);
#endif /* PARANOID */
}
switch (type_table[type]) {
case _NONE_:
break;
case _REG0_:
st0_ptr = &st(0); /* Some of these instructions pop after
storing */
st0_tag = FPU_gettag0();
break;
case _PUSH_:
{
if (FPU_gettagi(-1) != TAG_Empty) {
FPU_stack_overflow();
return 0;
}
top--;
st0_ptr = &st(0);
}
break;
case _null_:
FPU_illegal();
return 0;
#ifdef PARANOID
default:
EXCEPTION(EX_INTERNAL | 0x141);
return 0;
#endif /* PARANOID */
}
switch (type) {
/* type is a 5-bit value: (3-bit FPU_modrm.reg field | opcode[2,1]) */
case 000: /* fld m32real (d9 /0) */
clear_C1();
loaded_tag =
FPU_load_single((float __user *)data_address, &loaded_data);
if ((loaded_tag == TAG_Special)
&& isNaN(&loaded_data)
&& (real_1op_NaN(&loaded_data) < 0)) {
top++;
break;
}
FPU_copy_to_reg0(&loaded_data, loaded_tag);
break;
case 001: /* fild m32int (db /0) */
clear_C1();
loaded_tag =
FPU_load_int32((long __user *)data_address, &loaded_data);
FPU_copy_to_reg0(&loaded_data, loaded_tag);
break;
case 002: /* fld m64real (dd /0) */
clear_C1();
loaded_tag =
FPU_load_double((double __user *)data_address,
&loaded_data);
if ((loaded_tag == TAG_Special)
&& isNaN(&loaded_data)
&& (real_1op_NaN(&loaded_data) < 0)) {
top++;
break;
}
FPU_copy_to_reg0(&loaded_data, loaded_tag);
break;
case 003: /* fild m16int (df /0) */
clear_C1();
loaded_tag =
FPU_load_int16((short __user *)data_address, &loaded_data);
FPU_copy_to_reg0(&loaded_data, loaded_tag);
break;
/* case 004: undefined (d9 /1) */
/* fisttp are enabled if CPUID(1).ECX(0) "sse3" is set */
case 005: /* fisttp m32int (db /1) */
clear_C1();
sv_cw = control_word;
control_word |= RC_CHOP;
if (FPU_store_int32
(st0_ptr, st0_tag, (long __user *)data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
control_word = sv_cw;
break;
case 006: /* fisttp m64int (dd /1) */
clear_C1();
sv_cw = control_word;
control_word |= RC_CHOP;
if (FPU_store_int64
(st0_ptr, st0_tag, (long long __user *)data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
control_word = sv_cw;
break;
case 007: /* fisttp m16int (df /1) */
clear_C1();
sv_cw = control_word;
control_word |= RC_CHOP;
if (FPU_store_int16
(st0_ptr, st0_tag, (short __user *)data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
control_word = sv_cw;
break;
case 010: /* fst m32real */
clear_C1();
FPU_store_single(st0_ptr, st0_tag,
(float __user *)data_address);
break;
case 011: /* fist m32int */
clear_C1();
FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address);
break;
case 012: /* fst m64real */
clear_C1();
FPU_store_double(st0_ptr, st0_tag,
(double __user *)data_address);
break;
case 013: /* fist m16int */
clear_C1();
FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address);
break;
case 014: /* fstp m32real */
clear_C1();
if (FPU_store_single
(st0_ptr, st0_tag, (float __user *)data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 015: /* fistp m32int */
clear_C1();
if (FPU_store_int32
(st0_ptr, st0_tag, (long __user *)data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 016: /* fstp m64real */
clear_C1();
if (FPU_store_double
(st0_ptr, st0_tag, (double __user *)data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 017: /* fistp m16int */
clear_C1();
if (FPU_store_int16
(st0_ptr, st0_tag, (short __user *)data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 020: /* fldenv m14/28byte */
fldenv(addr_modes, (u_char __user *) data_address);
/* Ensure that the values just loaded are not changed by
fix-up operations. */
return 1;
case 022: /* frstor m94/108byte */
FPU_frstor(addr_modes, (u_char __user *) data_address);
/* Ensure that the values just loaded are not changed by
fix-up operations. */
return 1;
case 023: /* fbld m80dec */
clear_C1();
loaded_tag = FPU_load_bcd((u_char __user *) data_address);
FPU_settag0(loaded_tag);
break;
case 024: /* fldcw */
RE_ENTRANT_CHECK_OFF;
FPU_access_ok(data_address, 2);
FPU_get_user(control_word,
(unsigned short __user *)data_address);
RE_ENTRANT_CHECK_ON;
if (partial_status & ~control_word & CW_Exceptions)
partial_status |= (SW_Summary | SW_Backward);
else
partial_status &= ~(SW_Summary | SW_Backward);
#ifdef PECULIAR_486
control_word |= 0x40; /* An 80486 appears to always set this bit */
#endif /* PECULIAR_486 */
return 1;
case 025: /* fld m80real */
clear_C1();
loaded_tag =
FPU_load_extended((long double __user *)data_address, 0);
FPU_settag0(loaded_tag);
break;
case 027: /* fild m64int */
clear_C1();
loaded_tag = FPU_load_int64((long long __user *)data_address);
if (loaded_tag == TAG_Error)
return 0;
FPU_settag0(loaded_tag);
break;
case 030: /* fstenv m14/28byte */
fstenv(addr_modes, (u_char __user *) data_address);
return 1;
case 032: /* fsave */
fsave(addr_modes, (u_char __user *) data_address);
return 1;
case 033: /* fbstp m80dec */
clear_C1();
if (FPU_store_bcd
(st0_ptr, st0_tag, (u_char __user *) data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 034: /* fstcw m16int */
RE_ENTRANT_CHECK_OFF;
FPU_access_ok(data_address, 2);
FPU_put_user(control_word,
(unsigned short __user *)data_address);
RE_ENTRANT_CHECK_ON;
return 1;
case 035: /* fstp m80real */
clear_C1();
if (FPU_store_extended
(st0_ptr, st0_tag, (long double __user *)data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 036: /* fstsw m2byte */
RE_ENTRANT_CHECK_OFF;
FPU_access_ok(data_address, 2);
FPU_put_user(status_word(),
(unsigned short __user *)data_address);
RE_ENTRANT_CHECK_ON;
return 1;
case 037: /* fistp m64int */
clear_C1();
if (FPU_store_int64
(st0_ptr, st0_tag, (long long __user *)data_address))
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
}
return 0;
}
-179
View File
@@ -1,179 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*---------------------------------------------------------------------------+
| mul_Xsig.S |
| |
| Multiply a 12 byte fixed point number by another fixed point number. |
| |
| Copyright (C) 1992,1994,1995 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@jacobi.maths.monash.edu.au |
| |
| Call from C as: |
| void mul32_Xsig(Xsig *x, unsigned b) |
| |
| void mul64_Xsig(Xsig *x, unsigned long long *b) |
| |
| void mul_Xsig_Xsig(Xsig *x, unsigned *b) |
| |
| The result is neither rounded nor normalized, and the ls bit or so may |
| be wrong. |
| |
+---------------------------------------------------------------------------*/
.file "mul_Xsig.S"
#include "fpu_emu.h"
.text
SYM_FUNC_START(mul32_Xsig)
pushl %ebp
movl %esp,%ebp
subl $16,%esp
pushl %esi
movl PARAM1,%esi
movl PARAM2,%ecx
xor %eax,%eax
movl %eax,-4(%ebp)
movl %eax,-8(%ebp)
movl (%esi),%eax /* lsl of Xsig */
mull %ecx /* msl of b */
movl %edx,-12(%ebp)
movl 4(%esi),%eax /* midl of Xsig */
mull %ecx /* msl of b */
addl %eax,-12(%ebp)
adcl %edx,-8(%ebp)
adcl $0,-4(%ebp)
movl 8(%esi),%eax /* msl of Xsig */
mull %ecx /* msl of b */
addl %eax,-8(%ebp)
adcl %edx,-4(%ebp)
movl -12(%ebp),%eax
movl %eax,(%esi)
movl -8(%ebp),%eax
movl %eax,4(%esi)
movl -4(%ebp),%eax
movl %eax,8(%esi)
popl %esi
leave
RET
SYM_FUNC_END(mul32_Xsig)
SYM_FUNC_START(mul64_Xsig)
pushl %ebp
movl %esp,%ebp
subl $16,%esp
pushl %esi
movl PARAM1,%esi
movl PARAM2,%ecx
xor %eax,%eax
movl %eax,-4(%ebp)
movl %eax,-8(%ebp)
movl (%esi),%eax /* lsl of Xsig */
mull 4(%ecx) /* msl of b */
movl %edx,-12(%ebp)
movl 4(%esi),%eax /* midl of Xsig */
mull (%ecx) /* lsl of b */
addl %edx,-12(%ebp)
adcl $0,-8(%ebp)
adcl $0,-4(%ebp)
movl 4(%esi),%eax /* midl of Xsig */
mull 4(%ecx) /* msl of b */
addl %eax,-12(%ebp)
adcl %edx,-8(%ebp)
adcl $0,-4(%ebp)
movl 8(%esi),%eax /* msl of Xsig */
mull (%ecx) /* lsl of b */
addl %eax,-12(%ebp)
adcl %edx,-8(%ebp)
adcl $0,-4(%ebp)
movl 8(%esi),%eax /* msl of Xsig */
mull 4(%ecx) /* msl of b */
addl %eax,-8(%ebp)
adcl %edx,-4(%ebp)
movl -12(%ebp),%eax
movl %eax,(%esi)
movl -8(%ebp),%eax
movl %eax,4(%esi)
movl -4(%ebp),%eax
movl %eax,8(%esi)
popl %esi
leave
RET
SYM_FUNC_END(mul64_Xsig)
SYM_FUNC_START(mul_Xsig_Xsig)
pushl %ebp
movl %esp,%ebp
subl $16,%esp
pushl %esi
movl PARAM1,%esi
movl PARAM2,%ecx
xor %eax,%eax
movl %eax,-4(%ebp)
movl %eax,-8(%ebp)
movl (%esi),%eax /* lsl of Xsig */
mull 8(%ecx) /* msl of b */
movl %edx,-12(%ebp)
movl 4(%esi),%eax /* midl of Xsig */
mull 4(%ecx) /* midl of b */
addl %edx,-12(%ebp)
adcl $0,-8(%ebp)
adcl $0,-4(%ebp)
movl 8(%esi),%eax /* msl of Xsig */
mull (%ecx) /* lsl of b */
addl %edx,-12(%ebp)
adcl $0,-8(%ebp)
adcl $0,-4(%ebp)
movl 4(%esi),%eax /* midl of Xsig */
mull 8(%ecx) /* msl of b */
addl %eax,-12(%ebp)
adcl %edx,-8(%ebp)
adcl $0,-4(%ebp)
movl 8(%esi),%eax /* msl of Xsig */
mull 4(%ecx) /* midl of b */
addl %eax,-12(%ebp)
adcl %edx,-8(%ebp)
adcl $0,-4(%ebp)
movl 8(%esi),%eax /* msl of Xsig */
mull 8(%ecx) /* msl of b */
addl %eax,-8(%ebp)
adcl %edx,-4(%ebp)
movl -12(%ebp),%edx
movl %edx,(%esi)
movl -8(%ebp),%edx
movl %edx,4(%esi)
movl -4(%ebp),%edx
movl %edx,8(%esi)
popl %esi
leave
RET
SYM_FUNC_END(mul_Xsig_Xsig)
-115
View File
@@ -1,115 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*---------------------------------------------------------------------------+
| poly.h |
| |
| Header file for the FPU-emu poly*.c source files. |
| |
| Copyright (C) 1994,1999 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@melbpc.org.au |
| |
| Declarations and definitions for functions operating on Xsig (12-byte |
| extended-significand) quantities. |
| |
+---------------------------------------------------------------------------*/
#ifndef _POLY_H
#define _POLY_H
/* This 12-byte structure is used to improve the accuracy of computation
of transcendental functions.
Intended to be used to get results better than 8-byte computation
allows. 9-byte would probably be sufficient.
*/
typedef struct {
unsigned long lsw;
unsigned long midw;
unsigned long msw;
} Xsig;
asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b,
unsigned long long *result);
asmlinkage void polynomial_Xsig(Xsig *, const unsigned long long *x,
const unsigned long long terms[], const int n);
asmlinkage void mul32_Xsig(Xsig *, const unsigned long mult);
asmlinkage void mul64_Xsig(Xsig *, const unsigned long long *mult);
asmlinkage void mul_Xsig_Xsig(Xsig *dest, const Xsig *mult);
asmlinkage void shr_Xsig(Xsig *, const int n);
asmlinkage int round_Xsig(Xsig *);
asmlinkage int norm_Xsig(Xsig *);
asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, Xsig *dest);
/* Macro to extract the most significant 32 bits from a long long */
#define LL_MSW(x) (((unsigned long *)&x)[1])
/* Macro to initialize an Xsig struct */
#define MK_XSIG(a,b,c) { c, b, a }
/* Macro to access the 8 ms bytes of an Xsig as a long long */
#define XSIG_LL(x) (*(unsigned long long *)&x.midw)
/*
Need to run gcc with optimizations on to get these to
actually be in-line.
*/
/* Multiply two fixed-point 32 bit numbers, producing a 32 bit result.
The answer is the ms word of the product. */
/* Some versions of gcc make it difficult to stop eax from being clobbered.
Merely specifying that it is used doesn't work...
*/
static inline unsigned long mul_32_32(const unsigned long arg1,
const unsigned long arg2)
{
int retval;
asm volatile ("mull %2; movl %%edx,%%eax":"=a" (retval)
:"0"(arg1), "g"(arg2)
:"dx");
return retval;
}
/* Add the 12 byte Xsig x2 to Xsig dest, with no checks for overflow. */
static inline void add_Xsig_Xsig(Xsig *dest, const Xsig *x2)
{
asm volatile ("movl %1,%%edi; movl %2,%%esi;\n"
"movl (%%esi),%%eax; addl %%eax,(%%edi);\n"
"movl 4(%%esi),%%eax; adcl %%eax,4(%%edi);\n"
"movl 8(%%esi),%%eax; adcl %%eax,8(%%edi);\n":"=g"
(*dest):"g"(dest), "g"(x2)
:"ax", "si", "di");
}
/* Add the 12 byte Xsig x2 to Xsig dest, adjust exp if overflow occurs. */
/* Note: the constraints in the asm statement didn't always work properly
with gcc 2.5.8. Changing from using edi to using ecx got around the
problem, but keep fingers crossed! */
static inline void add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp)
{
asm volatile ("movl %2,%%ecx; movl %3,%%esi;\n"
"movl (%%esi),%%eax; addl %%eax,(%%ecx);\n"
"movl 4(%%esi),%%eax; adcl %%eax,4(%%ecx);\n"
"movl 8(%%esi),%%eax; adcl %%eax,8(%%ecx);\n"
"jnc 0f;\n"
"rcrl 8(%%ecx); rcrl 4(%%ecx); rcrl (%%ecx)\n"
"movl %4,%%ecx; incl (%%ecx)\n"
"movl $1,%%eax; jmp 1f;\n"
"0: xorl %%eax,%%eax;\n" "1:\n":"=g" (*exp), "=g"(*dest)
:"g"(dest), "g"(x2), "g"(exp)
:"cx", "si", "ax");
}
/* Negate (subtract from 1.0) the 12 byte Xsig */
/* This is faster in a loop on my 386 than using the "neg" instruction. */
static inline void negate_Xsig(Xsig *x)
{
asm volatile ("movl %1,%%esi;\n"
"xorl %%ecx,%%ecx;\n"
"movl %%ecx,%%eax; subl (%%esi),%%eax; movl %%eax,(%%esi);\n"
"movl %%ecx,%%eax; sbbl 4(%%esi),%%eax; movl %%eax,4(%%esi);\n"
"movl %%ecx,%%eax; sbbl 8(%%esi),%%eax; movl %%eax,8(%%esi);\n":"=g"
(*x):"g"(x):"si", "ax", "cx");
}
#endif /* _POLY_H */

Some files were not shown because too many files have changed in this diff Show More