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}
402