Fix a race in the FP copy code. If we setup our temporary FP save area
[dragonfly.git] / sys / i386 / i386 / bcopy.s
1 /*
2  * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $DragonFly: src/sys/i386/i386/Attic/bcopy.s,v 1.3 2004/04/30 02:59:14 dillon Exp $
27  */
28 /*
29  * bcopy(source:%esi, target:%edi, count:%ecx)
30  *
31  *      note: esi, edi, eax, ecx, and edx may be destroyed
32  */
33
34 #include "use_npx.h"
35
36 #include <machine/asmacros.h>
37 #include <machine/cputypes.h>
38 #include <machine/pmap.h>
39 #include <machine/specialreg.h>
40
41 #include "assym.s"
42
43         .text
44
45         /*
46          * If memcpy/bcopy is called as part of a copyin or copyout, the
47          * on-fault routine is set up to do a 'ret'.  We hve to restore
48          * %ebx and return to the copyin/copyout fault handler.
49          */
50 generic_onfault:
51         popl    %ebx
52         addl    $4,%esp         /* skip normal return vector */
53         ret                     /* return to copyin/copyout fault handler */
54
55         /*
56          * GENERIC BCOPY() - COPY DIRECTION CHECK AND FORWARDS COPY
57          *
58          *      Reasonably optimal on all modern machines.
59          */
60
61         SUPERALIGN_TEXT
62 ENTRY(asm_generic_memcpy)       /* memcpy() entry point use optimal copy */
63         pushl   %ebx
64         pushl   $generic_onfault
65         jmp     2f
66
67         SUPERALIGN_TEXT
68 ENTRY(asm_generic_bcopy)
69         pushl   %ebx
70         pushl   $generic_onfault
71         cmpl    %esi,%edi       /* if (edi < esi) fwd copy ok */
72         jb      2f
73         addl    %ecx,%esi
74         cmpl    %esi,%edi       /* if (edi < esi + count) do bkwrds copy */
75         jb      10f
76         subl    %ecx,%esi
77         jmp     2f
78
79         SUPERALIGN_TEXT
80 1:
81         movl    (%esi),%eax
82         movl    4(%esi),%ebx
83         movl    8(%esi),%edx
84         movl    %eax,(%edi)
85         movl    12(%esi),%eax
86         movl    %ebx,4(%edi)
87         movl    16(%esi),%ebx
88         movl    %edx,8(%edi)
89         movl    20(%esi),%edx
90         movl    %eax,12(%edi)
91         movl    24(%esi),%eax
92         movl    %ebx,16(%edi)
93         movl    28(%esi),%ebx
94         movl    %edx,20(%edi)
95         movl    %eax,24(%edi)
96         addl    $32,%esi
97         movl    %ebx,28(%edi)
98         addl    $32,%edi
99 2:
100         subl    $32,%ecx
101         jae     1b
102         addl    $32,%ecx
103         jz      3f
104         cld
105         rep
106         movsb
107 3:
108         addl    $4,%esp
109         popl    %ebx
110         ret
111
112         /*
113          * GENERIC_BCOPY() - BACKWARDS COPY
114          */
115         SUPERALIGN_TEXT
116 10:
117         addl    %ecx,%edi
118         jmp     12f
119
120         SUPERALIGN_TEXT
121 11:
122         movl    -4(%esi),%eax
123         movl    -8(%esi),%ebx
124         movl    -12(%esi),%edx
125         movl    %eax,-4(%edi)
126         movl    -16(%esi),%eax
127         movl    %ebx,-8(%edi)
128         movl    -20(%esi),%ebx
129         movl    %edx,-12(%edi)
130         movl    -24(%esi),%edx
131         movl    %eax,-16(%edi)
132         movl    -28(%esi),%eax
133         movl    %ebx,-20(%edi)
134         movl    -32(%esi),%ebx
135         movl    %edx,-24(%edi)
136         movl    %eax,-28(%edi)
137         subl    $32,%esi
138         movl    %ebx,-32(%edi)
139         subl    $32,%edi
140 12:
141         subl    $32,%ecx
142         jae     11b
143         addl    $32,%ecx
144         jz      13f
145         decl    %esi
146         decl    %edi
147         std
148         rep
149         movsb
150         cld
151 13:
152         addl    $4,%esp
153         popl    %ebx
154         ret
155
156         /*
157          * MMX BCOPY() - COPY DIRECTION CHECK AND FORWARDS COPY
158          *
159          * Reasonably optimal on all modern machines with MMX or SSE2.
160          * XXX But very messy, we need a better way to use fp in the kernel.
161          *
162          * note: esi, edi, eax, ecx, and edx may be destroyed
163          *
164          * In order for the kernel to be able to use the FPU:
165          *
166          *      (1) The kernel may not already be using the fpu
167          *
168          *      (2) If the fpu is owned by the application, we must save
169          *          its state.  If the fpu is not owned by the application
170          *          the application's saved fp state may already exist
171          *          in TD_SAVEFPU.
172          *
173          *      (3) We cannot allow the kernel overwrite the application's
174          *          FPU state with our own, so we allocate space on the
175          *          stack and create a new TD_SAVEFPU, saving the old
176          *          pointer.
177          *          
178          *      (4) While we are using the FP unit, an interrupt may come
179          *          along and preempt us, causing our FP state to be saved.
180          *          We will fault/restore upon resumption.  Our FP state
181          *          will be saved on the stack.
182          *
183          *      (5) To clean up we throw away our FP state and, zero out
184          *          npxthread to indicate that the application's FP state
185          *          is stored in TD_SAVEFPU, and we then restore the original
186          *          TD_SAVEFPU.
187          *
188          *          We do not attempt to restore the application's FP state.
189          *          We set the TS bit to guarentee that the application will
190          *          fault when it next tries to access the FP (to restore its
191          *          state).
192          *
193          *  NOTE: fxsave requires a 16-byte aligned address
194          *
195          *  NOTE: RACES (which are ok): 
196          *
197          *      + interrupt saves fp state after we check npxthread but
198          *        before we call fxsave
199          *      + interrupt saves application fp state after we change
200          *        TD_SAVEFPU.  Data will be ignored.
201          *      + interrupt occurs in critical section.  interrupt will be
202          *        delayed until we return or block (unless we check for
203          *        pending interrupts but I'm not going to bother for now).
204          *
205          *  MMX+XMM (SSE2): Typical on Athlons, later P4s. 128 bit media insn.
206          *  MMX: Typical on XPs and P3s.  64 bit media insn.
207          */
208
209 #define MMX_SAVE_BLOCK(missfunc)                \
210         cmpl    $2048,%ecx ;                    \
211         jb      missfunc ;                      \
212         btsl    $1,PCPU(kernel_fpu_lock) ;      \
213         jc      missfunc ;                      \
214         pushl   %ebx ;                          \
215         pushl   %ebp ;                          \
216         movl    %esp, %ebp ;                    \
217         movl    PCPU(curthread),%edx ;          \
218         movl    TD_SAVEFPU(%edx),%ebx ;         \
219         subl    $512,%esp ;                     \
220         andl    $0xfffffff0,%esp ;              \
221         addl    $TDPRI_CRIT,TD_PRI(%edx) ;      \
222         cmpl    %edx,PCPU(npxthread) ;          \
223         jne     100f ;                          \
224         fxsave  0(%ebx) ;                       \
225 100: ;                                          \
226         movl    %esp,TD_SAVEFPU(%edx) ;         \
227         movl    %edx,PCPU(npxthread) ;          \
228         clts ;                                  \
229         fninit ;                                \
230         subl    $TDPRI_CRIT,TD_PRI(%edx) ;      \
231         pushl   $mmx_onfault
232
233         /*
234          *  NOTE: RACES (which are ok): 
235          *
236          *      + interrupt occurs after we store NULL to npxthread.  No
237          *        state will be saved (because npxthread is NULL).  Thread
238          *        switches never restore npxthread, only a DNA trap does that.
239          *      + we can safely restore TD_SAFEFPU after NULLing npxthread.
240          *      + we can safely set TS any time after NULLing npxthread.
241          */
242
243 #define MMX_RESTORE_BLOCK                       \
244         addl    $4,%esp ;                       \
245         MMX_RESTORE_BLOCK2
246
247 #define MMX_RESTORE_BLOCK2                      \
248         movl    PCPU(curthread),%edx ;          \
249         movl    $0,PCPU(npxthread) ;            \
250         movl    %ebx,TD_SAVEFPU(%edx) ;         \
251         smsw    %ax ;                           \
252         movl    %ebp,%esp ;                     \
253         orb     $CR0_TS,%al ;                   \
254         popl    %ebp ;                          \
255         lmsw    %ax ;                           \
256         popl    %ebx ;                          \
257         movl    $0,PCPU(kernel_fpu_lock)
258
259         /*
260          * xmm/mmx_onfault routine.  Restore the fpu state, skip the normal
261          * return vector, and return to the caller's on-fault routine
262          * (which was pushed on the callers stack just before he calle us)
263          */
264 mmx_onfault:
265         MMX_RESTORE_BLOCK2
266         addl    $4,%esp
267         ret
268
269         /*
270          * MXX entry points - only support 64 bit media instructions
271          */
272         SUPERALIGN_TEXT
273 ENTRY(asm_mmx_memcpy)           /* memcpy() entry point use optimal copy */
274         MMX_SAVE_BLOCK(asm_generic_memcpy)
275         jmp     5f
276
277         SUPERALIGN_TEXT
278 ENTRY(asm_mmx_bcopy)
279         MMX_SAVE_BLOCK(asm_generic_bcopy)
280         cmpl    %esi,%edi       /* if (edi < esi) fwd copy ok */
281         jb      5f
282         addl    %ecx,%esi
283         cmpl    %esi,%edi       /* if (edi < esi + count) do bkwrds copy */
284         jb      10f
285         subl    %ecx,%esi
286         jmp     5f
287
288         /*
289          * XMM entry points - support 128 bit media instructions
290          */
291         SUPERALIGN_TEXT
292 ENTRY(asm_xmm_memcpy)           /* memcpy() entry point use optimal copy */
293         MMX_SAVE_BLOCK(asm_generic_memcpy)
294         jmp     1f
295
296         SUPERALIGN_TEXT
297 ENTRY(asm_xmm_bcopy)
298         MMX_SAVE_BLOCK(asm_generic_bcopy)
299         cmpl    %esi,%edi       /* if (edi < esi) fwd copy ok */
300         jb      1f
301         addl    %ecx,%esi
302         cmpl    %esi,%edi       /* if (edi < esi + count) do bkwrds copy */
303         jb      10f
304         subl    %ecx,%esi
305 1:
306         movl    %esi,%eax       /* skip xmm if the data is not aligned */
307         andl    $15,%eax
308         jnz     5f
309         movl    %edi,%eax
310         andl    $15,%eax
311         jz      3f
312         jmp     5f
313
314         SUPERALIGN_TEXT
315 2:
316         movdqa  (%esi),%xmm0
317         movdqa  16(%esi),%xmm1
318         movdqa  32(%esi),%xmm2
319         movdqa  48(%esi),%xmm3
320         movdqa  64(%esi),%xmm4
321         movdqa  80(%esi),%xmm5
322         movdqa  96(%esi),%xmm6
323         movdqa  112(%esi),%xmm7
324         /*prefetchnta 128(%esi) 3dNOW */
325         addl    $128,%esi
326
327         /*
328          * movdqa or movntdq can be used.
329          */
330         movdqa  %xmm0,(%edi)
331         movdqa  %xmm1,16(%edi)
332         movdqa  %xmm2,32(%edi)
333         movdqa  %xmm3,48(%edi)
334         movdqa  %xmm4,64(%edi)
335         movdqa  %xmm5,80(%edi)
336         movdqa  %xmm6,96(%edi)
337         movdqa  %xmm7,112(%edi)
338         addl    $128,%edi
339 3:
340         subl    $128,%ecx
341         jae     2b
342         addl    $128,%ecx
343         jz      6f
344         jmp     5f
345         SUPERALIGN_TEXT
346 4:
347         movq    (%esi),%mm0
348         movq    8(%esi),%mm1
349         movq    16(%esi),%mm2
350         movq    24(%esi),%mm3
351         movq    32(%esi),%mm4
352         movq    40(%esi),%mm5
353         movq    48(%esi),%mm6
354         movq    56(%esi),%mm7
355         /*prefetchnta 128(%esi) 3dNOW */
356         addl    $64,%esi
357         movq    %mm0,(%edi)
358         movq    %mm1,8(%edi)
359         movq    %mm2,16(%edi)
360         movq    %mm3,24(%edi)
361         movq    %mm4,32(%edi)
362         movq    %mm5,40(%edi)
363         movq    %mm6,48(%edi)
364         movq    %mm7,56(%edi)
365         addl    $64,%edi
366 5:
367         subl    $64,%ecx
368         jae     4b
369         addl    $64,%ecx
370         jz      6f
371         cld
372         rep
373         movsb
374 6:
375         MMX_RESTORE_BLOCK
376         ret
377
378         /*
379          * GENERIC_BCOPY() - BACKWARDS COPY
380          *
381          * Don't bother using xmm optimizations, just stick with mmx.
382          */
383         SUPERALIGN_TEXT
384 10:
385         addl    %ecx,%edi
386         jmp     12f
387
388         SUPERALIGN_TEXT
389 11:
390         movq    -64(%esi),%mm0
391         movq    -56(%esi),%mm1
392         movq    -48(%esi),%mm2
393         movq    -40(%esi),%mm3
394         movq    -32(%esi),%mm4
395         movq    -24(%esi),%mm5
396         movq    -16(%esi),%mm6
397         movq    -8(%esi),%mm7
398         /*prefetchnta -128(%esi)*/
399         subl    $64,%esi
400         movq    %mm0,-64(%edi)
401         movq    %mm1,-56(%edi)
402         movq    %mm2,-48(%edi)
403         movq    %mm3,-40(%edi)
404         movq    %mm4,-32(%edi)
405         movq    %mm5,-24(%edi)
406         movq    %mm6,-16(%edi)
407         movq    %mm7,-8(%edi)
408         subl    $64,%edi
409 12:
410         subl    $64,%ecx
411         jae     11b
412         addl    $64,%ecx
413         jz      13f
414         decl    %esi
415         decl    %edi
416         std
417         rep
418         movsb
419         cld
420 13:
421         MMX_RESTORE_BLOCK
422         ret
423