]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/ia64/ia64/unaligned.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / ia64 / ia64 / unaligned.c
1 /*-
2  * Copyright (c) 2003 Marcel Moolenaar
3  * Copyright (c) 2001 Doug Rabson
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/proc.h>
34 #include <sys/sysctl.h>
35 #include <vm/vm.h>
36 #include <vm/vm_extern.h>
37 #include <machine/frame.h>
38 #include <machine/md_var.h>
39 #include <ia64/disasm/disasm.h>
40
41 static int ia64_unaligned_print = 0;
42 SYSCTL_INT(_debug, OID_AUTO, unaligned_print, CTLFLAG_RW,
43     &ia64_unaligned_print, 0, "warn about unaligned accesses");
44
45 static int ia64_unaligned_test = 0;
46 SYSCTL_INT(_debug, OID_AUTO, unaligned_test, CTLFLAG_RW,
47     &ia64_unaligned_test, 0, "test emulation when PSR.ac is set");
48
49 static void *
50 fpreg_ptr(mcontext_t *mc, int fr)
51 {
52         union _ia64_fpreg *p;
53
54         if (fr <= 1 || fr >= 128)
55                 return (NULL);
56         if (fr >= 32) {
57                 p = &mc->mc_high_fp.fr32;
58                 fr -= 32;
59         } else if (fr >= 16) {
60                 p = &mc->mc_preserved_fp.fr16;
61                 fr -= 16;
62         } else if (fr >= 6) {
63                 p = &mc->mc_scratch_fp.fr6;
64                 fr -= 6;
65         } else {
66                 p = &mc->mc_preserved_fp.fr2;
67                 fr -= 2;
68         }
69         return ((void*)(p + fr));
70 }
71
72 static void *
73 greg_ptr(mcontext_t *mc, int gr)
74 {
75         uint64_t *p;
76         int nslots;
77
78         if (gr <= 0 || gr >= 32 + (mc->mc_special.cfm & 0x7f))
79                 return (NULL);
80         if (gr >= 32) {
81                 nslots = IA64_CFM_SOF(mc->mc_special.cfm) - gr + 32;
82                 p = (void *)ia64_bsp_adjust(mc->mc_special.bspstore, -nslots);
83                 gr = 0;
84         } else if (gr >= 14) {
85                 p = &mc->mc_scratch.gr14;
86                 gr -= 14;
87         } else if (gr == 13) {
88                 p = &mc->mc_special.tp;
89                 gr = 0;
90         } else if (gr == 12) {
91                 p = &mc->mc_special.sp;
92                 gr = 0;
93         } else if (gr >= 8) {
94                 p = &mc->mc_scratch.gr8;
95                 gr -= 8;
96         } else if (gr >= 4) {
97                 p = &mc->mc_preserved.gr4;
98                 gr -= 4;
99         } else if (gr >= 2) {
100                 p = &mc->mc_scratch.gr2;
101                 gr -= 2;
102         } else {
103                 p = &mc->mc_special.gp;
104                 gr = 0;
105         }
106         return ((void*)(p + gr));
107 }
108
109 static uint64_t
110 rdreg(uint64_t *addr)
111 {
112         if ((uintptr_t)addr < VM_MAXUSER_ADDRESS)
113                 return (fuword(addr));
114         return (*addr);
115 }
116
117 static void
118 wrreg(uint64_t *addr, uint64_t val)
119 {
120         if ((uintptr_t)addr < VM_MAXUSER_ADDRESS)
121                 suword(addr, val);
122         else
123                 *addr = val;
124 }
125
126 static int
127 fixup(struct asm_inst *i, mcontext_t *mc, uint64_t va)
128 {
129         union {
130                 double d;
131                 long double e;
132                 uint64_t i;
133                 float s;
134         } buf;
135         void *reg;
136         uint64_t postinc;
137
138         switch (i->i_op) {
139         case ASM_OP_LD2:
140                 copyin((void*)va, (void*)&buf.i, 2);
141                 reg = greg_ptr(mc, (int)i->i_oper[1].o_value);
142                 if (reg == NULL)
143                         return (EINVAL);
144                 wrreg(reg, buf.i & 0xffffU);
145                 break;
146         case ASM_OP_LD4:
147                 copyin((void*)va, (void*)&buf.i, 4);
148                 reg = greg_ptr(mc, (int)i->i_oper[1].o_value);
149                 if (reg == NULL)
150                         return (EINVAL);
151                 wrreg(reg, buf.i & 0xffffffffU);
152                 break;
153         case ASM_OP_LD8:
154                 copyin((void*)va, (void*)&buf.i, 8);
155                 reg = greg_ptr(mc, (int)i->i_oper[1].o_value);
156                 if (reg == NULL)
157                         return (EINVAL);
158                 wrreg(reg, buf.i);
159                 break;
160         case ASM_OP_LDFD:
161                 copyin((void*)va, (void*)&buf.d, sizeof(buf.d));
162                 reg = fpreg_ptr(mc, (int)i->i_oper[1].o_value);
163                 if (reg == NULL)
164                         return (EINVAL);
165                 __asm("ldfd f6=%1;; stf.spill %0=f6" : "=m"(*(double *)reg) :
166                     "m"(buf.d) : "f6");
167                 break;
168         case ASM_OP_LDFE:
169                 copyin((void*)va, (void*)&buf.e, sizeof(buf.e));
170                 reg = fpreg_ptr(mc, (int)i->i_oper[1].o_value);
171                 if (reg == NULL)
172                         return (EINVAL);
173                 __asm("ldfe f6=%1;; stf.spill %0=f6" :
174                     "=m"(*(long double *)reg) : "m"(buf.e) : "f6");
175                 break;
176         case ASM_OP_LDFS:
177                 copyin((void*)va, (void*)&buf.s, sizeof(buf.s));
178                 reg = fpreg_ptr(mc, (int)i->i_oper[1].o_value);
179                 if (reg == NULL)
180                         return (EINVAL);
181                 __asm("ldfs f6=%1;; stf.spill %0=f6" : "=m"(*(float *)reg) :
182                     "m"(buf.s) : "f6");
183                 break;
184         case ASM_OP_ST2:
185                 reg = greg_ptr(mc, (int)i->i_oper[2].o_value);
186                 if (reg == NULL)
187                         return (EINVAL);
188                 buf.i = rdreg(reg);
189                 copyout((void*)&buf.i, (void*)va, 2);
190                 break;
191         case ASM_OP_ST4:
192                 reg = greg_ptr(mc, (int)i->i_oper[2].o_value);
193                 if (reg == NULL)
194                         return (EINVAL);
195                 buf.i = rdreg(reg);
196                 copyout((void*)&buf.i, (void*)va, 4);
197                 break;
198         case ASM_OP_ST8:
199                 reg = greg_ptr(mc, (int)i->i_oper[2].o_value);
200                 if (reg == NULL)
201                         return (EINVAL);
202                 buf.i = rdreg(reg);
203                 copyout((void*)&buf.i, (void*)va, 8);
204                 break;
205         case ASM_OP_STFD:
206                 reg = fpreg_ptr(mc, (int)i->i_oper[2].o_value);
207                 if (reg == NULL)
208                         return (EINVAL);
209                 __asm("ldf.fill f6=%1;; stfd %0=f6" : "=m"(buf.d) :
210                     "m"(*(double *)reg) : "f6");
211                 copyout((void*)&buf.d, (void*)va, sizeof(buf.d));
212                 break;
213         case ASM_OP_STFE:
214                 reg = fpreg_ptr(mc, (int)i->i_oper[2].o_value);
215                 if (reg == NULL)
216                         return (EINVAL);
217                 __asm("ldf.fill f6=%1;; stfe %0=f6" : "=m"(buf.e) :
218                     "m"(*(long double *)reg) : "f6");
219                 copyout((void*)&buf.e, (void*)va, sizeof(buf.e));
220                 break;
221         case ASM_OP_STFS:
222                 reg = fpreg_ptr(mc, (int)i->i_oper[2].o_value);
223                 if (reg == NULL)
224                         return (EINVAL);
225                 __asm("ldf.fill f6=%1;; stfs %0=f6" : "=m"(buf.s) :
226                     "m"(*(float *)reg) : "f6");
227                 copyout((void*)&buf.s, (void*)va, sizeof(buf.s));
228                 break;
229         default:
230                 return (ENOENT);
231         }
232
233         /* Handle post-increment. */
234         if (i->i_oper[3].o_type == ASM_OPER_GREG) {
235                 reg = greg_ptr(mc, (int)i->i_oper[3].o_value);
236                 if (reg == NULL)
237                         return (EINVAL);
238                 postinc = rdreg(reg);
239         } else
240                 postinc = (i->i_oper[3].o_type == ASM_OPER_IMM)
241                     ? i->i_oper[3].o_value : 0;
242         if (postinc != 0) {
243                 if (i->i_oper[1].o_type == ASM_OPER_MEM)
244                         reg = greg_ptr(mc, (int)i->i_oper[1].o_value);
245                 else
246                         reg = greg_ptr(mc, (int)i->i_oper[2].o_value);
247                 if (reg == NULL)
248                         return (EINVAL);
249                 postinc += rdreg(reg);
250                 wrreg(reg, postinc);
251         }
252         return (0);
253 }
254
255 int
256 unaligned_fixup(struct trapframe *tf, struct thread *td)
257 {
258         mcontext_t context;
259         struct asm_bundle bundle;
260         int error, slot;
261
262         slot = ((tf->tf_special.psr & IA64_PSR_RI) == IA64_PSR_RI_0) ? 0 :
263             ((tf->tf_special.psr & IA64_PSR_RI) == IA64_PSR_RI_1) ? 1 : 2;
264
265         if (ia64_unaligned_print) {
266                 uprintf("pid %d (%s): unaligned access: va=0x%lx, pc=0x%lx\n",
267                     td->td_proc->p_pid, td->td_proc->p_comm,
268                     tf->tf_special.ifa, tf->tf_special.iip + slot);
269         }
270
271         /*
272          * If PSR.ac is set, the process wants to be signalled about mis-
273          * aligned loads and stores. Send it a SIGBUS. In order for us to
274          * test the emulation of misaligned loads and stores, we have a
275          * sysctl that tells us that we must emulate the load or store,
276          * instead of sending the signal. We need the sysctl because if
277          * PSR.ac is not set, the CPU may (and likely will) deal with the
278          * misaligned load or store itself. As such, we won't get the
279          * exception.
280          */
281         if ((tf->tf_special.psr & IA64_PSR_AC) && !ia64_unaligned_test)
282                 return (SIGBUS);
283
284         if (!asm_decode(tf->tf_special.iip, &bundle))
285                 return (SIGILL);
286
287         get_mcontext(td, &context, 0);
288
289         error = fixup(bundle.b_inst + slot, &context, tf->tf_special.ifa);
290         if (error == ENOENT) {
291                 printf("unhandled misaligned memory access:\n\t");
292                 asm_print_inst(&bundle, slot, tf->tf_special.iip);
293                 return (SIGILL);
294         } else if (error != 0)
295                 return (SIGBUS);
296
297         set_mcontext(td, &context);
298
299         /* Advance to the next instruction. */
300         if (slot == 2) {
301                 tf->tf_special.psr &= ~IA64_PSR_RI;
302                 tf->tf_special.iip += 16;
303         } else
304                 tf->tf_special.psr += IA64_PSR_RI_1;
305
306         return (0);
307 }