cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

get_address.c (10780B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*---------------------------------------------------------------------------+
      3 |  get_address.c                                                            |
      4 |                                                                           |
      5 | Get the effective address from an FPU instruction.                        |
      6 |                                                                           |
      7 | Copyright (C) 1992,1993,1994,1997                                         |
      8 |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
      9 |                       Australia.  E-mail   billm@suburbia.net             |
     10 |                                                                           |
     11 |                                                                           |
     12 +---------------------------------------------------------------------------*/
     13
     14/*---------------------------------------------------------------------------+
     15 | Note:                                                                     |
     16 |    The file contains code which accesses user memory.                     |
     17 |    Emulator static data may change when user memory is accessed, due to   |
     18 |    other processes using the emulator while swapping is in progress.      |
     19 +---------------------------------------------------------------------------*/
     20
     21#include <linux/stddef.h>
     22
     23#include <linux/uaccess.h>
     24#include <asm/vm86.h>
     25
     26#include "fpu_system.h"
     27#include "exception.h"
     28#include "fpu_emu.h"
     29
     30#define FPU_WRITE_BIT 0x10
     31
     32static int reg_offset[] = {
     33	offsetof(struct pt_regs, ax),
     34	offsetof(struct pt_regs, cx),
     35	offsetof(struct pt_regs, dx),
     36	offsetof(struct pt_regs, bx),
     37	offsetof(struct pt_regs, sp),
     38	offsetof(struct pt_regs, bp),
     39	offsetof(struct pt_regs, si),
     40	offsetof(struct pt_regs, di)
     41};
     42
     43#define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs))
     44
     45static int reg_offset_vm86[] = {
     46	offsetof(struct pt_regs, cs),
     47	offsetof(struct kernel_vm86_regs, ds),
     48	offsetof(struct kernel_vm86_regs, es),
     49	offsetof(struct kernel_vm86_regs, fs),
     50	offsetof(struct kernel_vm86_regs, gs),
     51	offsetof(struct pt_regs, ss),
     52	offsetof(struct kernel_vm86_regs, ds)
     53};
     54
     55#define VM86_REG_(x) (*(unsigned short *) \
     56		(reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs))
     57
     58static int reg_offset_pm[] = {
     59	offsetof(struct pt_regs, cs),
     60	offsetof(struct pt_regs, ds),
     61	offsetof(struct pt_regs, es),
     62	offsetof(struct pt_regs, fs),
     63	offsetof(struct pt_regs, ds),	/* dummy, not saved on stack */
     64	offsetof(struct pt_regs, ss),
     65	offsetof(struct pt_regs, ds)
     66};
     67
     68#define PM_REG_(x) (*(unsigned short *) \
     69		(reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs))
     70
     71/* Decode the SIB byte. This function assumes mod != 0 */
     72static int sib(int mod, unsigned long *fpu_eip)
     73{
     74	u_char ss, index, base;
     75	long offset;
     76
     77	RE_ENTRANT_CHECK_OFF;
     78	FPU_code_access_ok(1);
     79	FPU_get_user(base, (u_char __user *) (*fpu_eip));	/* The SIB byte */
     80	RE_ENTRANT_CHECK_ON;
     81	(*fpu_eip)++;
     82	ss = base >> 6;
     83	index = (base >> 3) & 7;
     84	base &= 7;
     85
     86	if ((mod == 0) && (base == 5))
     87		offset = 0;	/* No base register */
     88	else
     89		offset = REG_(base);
     90
     91	if (index == 4) {
     92		/* No index register */
     93		/* A non-zero ss is illegal */
     94		if (ss)
     95			EXCEPTION(EX_Invalid);
     96	} else {
     97		offset += (REG_(index)) << ss;
     98	}
     99
    100	if (mod == 1) {
    101		/* 8 bit signed displacement */
    102		long displacement;
    103		RE_ENTRANT_CHECK_OFF;
    104		FPU_code_access_ok(1);
    105		FPU_get_user(displacement, (signed char __user *)(*fpu_eip));
    106		offset += displacement;
    107		RE_ENTRANT_CHECK_ON;
    108		(*fpu_eip)++;
    109	} else if (mod == 2 || base == 5) {	/* The second condition also has mod==0 */
    110		/* 32 bit displacement */
    111		long displacement;
    112		RE_ENTRANT_CHECK_OFF;
    113		FPU_code_access_ok(4);
    114		FPU_get_user(displacement, (long __user *)(*fpu_eip));
    115		offset += displacement;
    116		RE_ENTRANT_CHECK_ON;
    117		(*fpu_eip) += 4;
    118	}
    119
    120	return offset;
    121}
    122
    123static unsigned long vm86_segment(u_char segment, struct address *addr)
    124{
    125	segment--;
    126#ifdef PARANOID
    127	if (segment > PREFIX_SS_) {
    128		EXCEPTION(EX_INTERNAL | 0x130);
    129		math_abort(FPU_info, SIGSEGV);
    130	}
    131#endif /* PARANOID */
    132	addr->selector = VM86_REG_(segment);
    133	return (unsigned long)VM86_REG_(segment) << 4;
    134}
    135
    136/* This should work for 16 and 32 bit protected mode. */
    137static long pm_address(u_char FPU_modrm, u_char segment,
    138		       struct address *addr, long offset)
    139{
    140	struct desc_struct descriptor;
    141	unsigned long base_address, limit, address, seg_top;
    142
    143	segment--;
    144
    145#ifdef PARANOID
    146	/* segment is unsigned, so this also detects if segment was 0: */
    147	if (segment > PREFIX_SS_) {
    148		EXCEPTION(EX_INTERNAL | 0x132);
    149		math_abort(FPU_info, SIGSEGV);
    150	}
    151#endif /* PARANOID */
    152
    153	switch (segment) {
    154	case PREFIX_GS_ - 1:
    155		/* user gs handling can be lazy, use special accessors */
    156		savesegment(gs, addr->selector);
    157		break;
    158	default:
    159		addr->selector = PM_REG_(segment);
    160	}
    161
    162	descriptor = FPU_get_ldt_descriptor(addr->selector);
    163	base_address = seg_get_base(&descriptor);
    164	address = base_address + offset;
    165	limit = seg_get_limit(&descriptor) + 1;
    166	limit *= seg_get_granularity(&descriptor);
    167	limit += base_address - 1;
    168	if (limit < base_address)
    169		limit = 0xffffffff;
    170
    171	if (seg_expands_down(&descriptor)) {
    172		if (descriptor.g) {
    173			seg_top = 0xffffffff;
    174		} else {
    175			seg_top = base_address + (1 << 20);
    176			if (seg_top < base_address)
    177				seg_top = 0xffffffff;
    178		}
    179		access_limit =
    180		    (address <= limit) || (address >= seg_top) ? 0 :
    181		    ((seg_top - address) >= 255 ? 255 : seg_top - address);
    182	} else {
    183		access_limit =
    184		    (address > limit) || (address < base_address) ? 0 :
    185		    ((limit - address) >= 254 ? 255 : limit - address + 1);
    186	}
    187	if (seg_execute_only(&descriptor) ||
    188	    (!seg_writable(&descriptor) && (FPU_modrm & FPU_WRITE_BIT))) {
    189		access_limit = 0;
    190	}
    191	return address;
    192}
    193
    194/*
    195       MOD R/M byte:  MOD == 3 has a special use for the FPU
    196                      SIB byte used iff R/M = 100b
    197
    198       7   6   5   4   3   2   1   0
    199       .....   .........   .........
    200        MOD    OPCODE(2)     R/M
    201
    202       SIB byte
    203
    204       7   6   5   4   3   2   1   0
    205       .....   .........   .........
    206        SS      INDEX        BASE
    207
    208*/
    209
    210void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
    211			     struct address *addr, fpu_addr_modes addr_modes)
    212{
    213	u_char mod;
    214	unsigned rm = FPU_modrm & 7;
    215	long *cpu_reg_ptr;
    216	int address = 0;	/* Initialized just to stop compiler warnings. */
    217
    218	/* Memory accessed via the cs selector is write protected
    219	   in `non-segmented' 32 bit protected mode. */
    220	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
    221	    && (addr_modes.override.segment == PREFIX_CS_)) {
    222		math_abort(FPU_info, SIGSEGV);
    223	}
    224
    225	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
    226
    227	mod = (FPU_modrm >> 6) & 3;
    228
    229	if (rm == 4 && mod != 3) {
    230		address = sib(mod, fpu_eip);
    231	} else {
    232		cpu_reg_ptr = &REG_(rm);
    233		switch (mod) {
    234		case 0:
    235			if (rm == 5) {
    236				/* Special case: disp32 */
    237				RE_ENTRANT_CHECK_OFF;
    238				FPU_code_access_ok(4);
    239				FPU_get_user(address,
    240					     (unsigned long __user
    241					      *)(*fpu_eip));
    242				(*fpu_eip) += 4;
    243				RE_ENTRANT_CHECK_ON;
    244				addr->offset = address;
    245				return (void __user *)address;
    246			} else {
    247				address = *cpu_reg_ptr;	/* Just return the contents
    248							   of the cpu register */
    249				addr->offset = address;
    250				return (void __user *)address;
    251			}
    252		case 1:
    253			/* 8 bit signed displacement */
    254			RE_ENTRANT_CHECK_OFF;
    255			FPU_code_access_ok(1);
    256			FPU_get_user(address, (signed char __user *)(*fpu_eip));
    257			RE_ENTRANT_CHECK_ON;
    258			(*fpu_eip)++;
    259			break;
    260		case 2:
    261			/* 32 bit displacement */
    262			RE_ENTRANT_CHECK_OFF;
    263			FPU_code_access_ok(4);
    264			FPU_get_user(address, (long __user *)(*fpu_eip));
    265			(*fpu_eip) += 4;
    266			RE_ENTRANT_CHECK_ON;
    267			break;
    268		case 3:
    269			/* Not legal for the FPU */
    270			EXCEPTION(EX_Invalid);
    271		}
    272		address += *cpu_reg_ptr;
    273	}
    274
    275	addr->offset = address;
    276
    277	switch (addr_modes.default_mode) {
    278	case 0:
    279		break;
    280	case VM86:
    281		address += vm86_segment(addr_modes.override.segment, addr);
    282		break;
    283	case PM16:
    284	case SEG32:
    285		address = pm_address(FPU_modrm, addr_modes.override.segment,
    286				     addr, address);
    287		break;
    288	default:
    289		EXCEPTION(EX_INTERNAL | 0x133);
    290	}
    291
    292	return (void __user *)address;
    293}
    294
    295void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
    296				struct address *addr, fpu_addr_modes addr_modes)
    297{
    298	u_char mod;
    299	unsigned rm = FPU_modrm & 7;
    300	int address = 0;	/* Default used for mod == 0 */
    301
    302	/* Memory accessed via the cs selector is write protected
    303	   in `non-segmented' 32 bit protected mode. */
    304	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
    305	    && (addr_modes.override.segment == PREFIX_CS_)) {
    306		math_abort(FPU_info, SIGSEGV);
    307	}
    308
    309	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
    310
    311	mod = (FPU_modrm >> 6) & 3;
    312
    313	switch (mod) {
    314	case 0:
    315		if (rm == 6) {
    316			/* Special case: disp16 */
    317			RE_ENTRANT_CHECK_OFF;
    318			FPU_code_access_ok(2);
    319			FPU_get_user(address,
    320				     (unsigned short __user *)(*fpu_eip));
    321			(*fpu_eip) += 2;
    322			RE_ENTRANT_CHECK_ON;
    323			goto add_segment;
    324		}
    325		break;
    326	case 1:
    327		/* 8 bit signed displacement */
    328		RE_ENTRANT_CHECK_OFF;
    329		FPU_code_access_ok(1);
    330		FPU_get_user(address, (signed char __user *)(*fpu_eip));
    331		RE_ENTRANT_CHECK_ON;
    332		(*fpu_eip)++;
    333		break;
    334	case 2:
    335		/* 16 bit displacement */
    336		RE_ENTRANT_CHECK_OFF;
    337		FPU_code_access_ok(2);
    338		FPU_get_user(address, (unsigned short __user *)(*fpu_eip));
    339		(*fpu_eip) += 2;
    340		RE_ENTRANT_CHECK_ON;
    341		break;
    342	case 3:
    343		/* Not legal for the FPU */
    344		EXCEPTION(EX_Invalid);
    345		break;
    346	}
    347	switch (rm) {
    348	case 0:
    349		address += FPU_info->regs->bx + FPU_info->regs->si;
    350		break;
    351	case 1:
    352		address += FPU_info->regs->bx + FPU_info->regs->di;
    353		break;
    354	case 2:
    355		address += FPU_info->regs->bp + FPU_info->regs->si;
    356		if (addr_modes.override.segment == PREFIX_DEFAULT)
    357			addr_modes.override.segment = PREFIX_SS_;
    358		break;
    359	case 3:
    360		address += FPU_info->regs->bp + FPU_info->regs->di;
    361		if (addr_modes.override.segment == PREFIX_DEFAULT)
    362			addr_modes.override.segment = PREFIX_SS_;
    363		break;
    364	case 4:
    365		address += FPU_info->regs->si;
    366		break;
    367	case 5:
    368		address += FPU_info->regs->di;
    369		break;
    370	case 6:
    371		address += FPU_info->regs->bp;
    372		if (addr_modes.override.segment == PREFIX_DEFAULT)
    373			addr_modes.override.segment = PREFIX_SS_;
    374		break;
    375	case 7:
    376		address += FPU_info->regs->bx;
    377		break;
    378	}
    379
    380      add_segment:
    381	address &= 0xffff;
    382
    383	addr->offset = address;
    384
    385	switch (addr_modes.default_mode) {
    386	case 0:
    387		break;
    388	case VM86:
    389		address += vm86_segment(addr_modes.override.segment, addr);
    390		break;
    391	case PM16:
    392	case SEG32:
    393		address = pm_address(FPU_modrm, addr_modes.override.segment,
    394				     addr, address);
    395		break;
    396	default:
    397		EXCEPTION(EX_INTERNAL | 0x131);
    398	}
    399
    400	return (void __user *)address;
    401}