Merge from vendor branch GPERF:
[dragonfly.git] / sys / i386 / i386 / bcopy.s
1 /*
2  * Copyright (c) 2004 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sys/i386/i386/Attic/bcopy.s,v 1.6 2004/07/16 05:48:29 dillon Exp $
35  */
36 /*
37  * bcopy(source:%esi, target:%edi, count:%ecx)
38  *
39  *      note: esi, edi, eax, ecx, and edx may be destroyed
40  */
41
42 #include "use_npx.h"
43
44 #include <machine/asmacros.h>
45 #include <machine/cputypes.h>
46 #include <machine/pmap.h>
47 #include <machine/specialreg.h>
48
49 #include "assym.s"
50
51         .text
52
53         /*
54          * bcopyb() is a 'dumb' byte-granular bcopy.  It is only used by
55          * devices which need to bcopy device-mapped memory which cannot
56          * otherwise handle 16 or 32 bit ops.
57          */
58         ALIGN_TEXT
59 ENTRY(bcopyb)
60         pushl   %esi
61         pushl   %edi
62         movl    12(%esp),%esi
63         movl    16(%esp),%edi
64         movl    20(%esp),%ecx
65         movl    %edi,%eax
66         subl    %esi,%eax
67         cmpl    %ecx,%eax                       /* overlapping && src < dst? */
68         jb      1f
69         cld                                     /* nope, copy forwards */
70         rep
71         movsb
72         popl    %edi
73         popl    %esi
74         ret
75
76         ALIGN_TEXT
77 1:
78         addl    %ecx,%edi                       /* copy backwards. */
79         addl    %ecx,%esi
80         decl    %edi
81         decl    %esi
82         std
83         rep
84         movsb
85         popl    %edi
86         popl    %esi
87         cld
88         ret
89
90
91         /*
92          * If memcpy/bcopy is called as part of a copyin or copyout, the
93          * on-fault routine is set up to do a 'ret'.  We have to restore
94          * %ebx and return to the copyin/copyout fault handler.
95          */
96 generic_onfault:
97         popl    %ebx
98         addl    $4,%esp         /* skip normal return vector */
99         ret                     /* return to copyin/copyout fault handler */
100
101         /*
102          * GENERIC BCOPY() - COPY DIRECTION CHECK AND FORWARDS COPY
103          *
104          *      Reasonably optimal on all modern machines.
105          */
106
107         SUPERALIGN_TEXT
108 ENTRY(asm_generic_memcpy)       /* memcpy() entry point use optimal copy */
109         pushl   %ebx
110         pushl   $generic_onfault
111         jmp     2f
112
113         SUPERALIGN_TEXT
114 ENTRY(asm_generic_bcopy)
115         pushl   %ebx
116         pushl   $generic_onfault
117         cmpl    %esi,%edi       /* if (edi < esi) fwd copy ok */
118         jb      2f
119         addl    %ecx,%esi
120         cmpl    %esi,%edi       /* if (edi < esi + count) do bkwrds copy */
121         jb      10f
122         subl    %ecx,%esi
123         jmp     2f
124
125         SUPERALIGN_TEXT
126 1:
127         movl    (%esi),%eax
128         movl    4(%esi),%ebx
129         movl    8(%esi),%edx
130         movl    %eax,(%edi)
131         movl    12(%esi),%eax
132         movl    %ebx,4(%edi)
133         movl    16(%esi),%ebx
134         movl    %edx,8(%edi)
135         movl    20(%esi),%edx
136         movl    %eax,12(%edi)
137         movl    24(%esi),%eax
138         movl    %ebx,16(%edi)
139         movl    28(%esi),%ebx
140         movl    %edx,20(%edi)
141         movl    %eax,24(%edi)
142         addl    $32,%esi
143         movl    %ebx,28(%edi)
144         addl    $32,%edi
145 2:
146         subl    $32,%ecx
147         jae     1b
148         addl    $32,%ecx
149         jz      3f
150         cld
151         rep
152         movsb
153 3:
154         addl    $4,%esp
155         popl    %ebx
156         ret
157
158         /*
159          * GENERIC_BCOPY() - BACKWARDS COPY
160          */
161         SUPERALIGN_TEXT
162 10:
163         addl    %ecx,%edi
164         jmp     12f
165
166         SUPERALIGN_TEXT
167 11:
168         movl    -4(%esi),%eax
169         movl    -8(%esi),%ebx
170         movl    -12(%esi),%edx
171         movl    %eax,-4(%edi)
172         movl    -16(%esi),%eax
173         movl    %ebx,-8(%edi)
174         movl    -20(%esi),%ebx
175         movl    %edx,-12(%edi)
176         movl    -24(%esi),%edx
177         movl    %eax,-16(%edi)
178         movl    -28(%esi),%eax
179         movl    %ebx,-20(%edi)
180         movl    -32(%esi),%ebx
181         movl    %edx,-24(%edi)
182         movl    %eax,-28(%edi)
183         subl    $32,%esi
184         movl    %ebx,-32(%edi)
185         subl    $32,%edi
186 12:
187         subl    $32,%ecx
188         jae     11b
189         addl    $32,%ecx
190         jz      13f
191         decl    %esi
192         decl    %edi
193         std
194         rep
195         movsb
196         cld
197 13:
198         addl    $4,%esp
199         popl    %ebx
200         ret
201
202         /*
203          * MMX BCOPY() - COPY DIRECTION CHECK AND FORWARDS COPY
204          *
205          * note: esi, edi, eax, ecx, and edx are allowed to be destroyed.
206          *
207          * In order for the kernel to be able to use the FPU:
208          *
209          *      (1) The kernel may not already be using the fpu.
210          *
211          *      (2) If the fpu is owned by the application, we must save
212          *          its state.  If the fpu is not owned by the application
213          *          the application's saved fp state may already exist
214          *          in TD_SAVEFPU.
215          *
216          *      (3) We cannot allow the kernel to overwrite the application's
217          *          FPU state with our own, so we make sure the application's
218          *          FPU state has been saved and then point TD_SAVEFPU at a
219          *          temporary fpu save area in the globaldata structure.
220          *          
221          * RACES/ALGORITHM:
222          *
223          *      If gd_npxthread is not NULL we must save the application's
224          *      current FP state to the current save area and then NULL
225          *      out gd_npxthread to interlock against new interruptions
226          *      changing the FP state further.
227          *
228          *      If gd_npxthread is NULL the FP unit is in a known 'safe'
229          *      state and may be used once the new save area is installed.
230          *
231          *      race(1): If an interrupt occurs just prior to calling fxsave
232          *      all that happens is that fxsave gets a npxdna trap, restores
233          *      the app's environment, and immediately traps, restores,
234          *      and saves it again.
235          *
236          *      race(2): No interrupt can safely occur after we NULL-out
237          *      npxthread until we fninit, because the kernel assumes that
238          *      the FP unit is in a safe state when npxthread is NULL.  It's
239          *      more convenient to use a cli sequence here (it is not
240          *      considered to be in the critical path), but a critical
241          *      section would also work.
242          *
243          *      race(3): The FP unit is in a known state (because npxthread
244          *      was either previously NULL or we saved and init'd and made
245          *      it NULL).  This is true even if we are preempted and the
246          *      preempting thread uses the FP unit, because it will be
247          *      fninit's again on return.  ANY STATE WE SAVE TO THE FPU MAY
248          *      BE DESTROYED BY PREEMPTION WHILE NPXTHREAD IS NULL!  However,
249          *      an interrupt occuring inbetween clts and the setting of
250          *      gd_npxthread may set the TS bit again and cause the next
251          *      npxdna() to panic when it sees a non-NULL gd_npxthread.
252          *      
253          *      We can safely set TD_SAVEFPU to point to a new uninitialized
254          *      save area and then set GD_NPXTHREAD to non-NULL.  If an
255          *      interrupt occurs after we set GD_NPXTHREAD, all that happens
256          *      is that the safe FP state gets saved and restored.  We do not
257          *      need to fninit again.
258          *
259          *      We can safely clts after setting up the new save-area, before
260          *      installing gd_npxthread, even if we get preempted just after
261          *      calling clts.  This is because the FP unit will be in a safe
262          *      state while gd_npxthread is NULL.  Setting gd_npxthread will
263          *      simply lock-in that safe-state.  Calling clts saves
264          *      unnecessary trap overhead since we are about to use the FP
265          *      unit anyway and don't need to 'restore' any state prior to
266          *      that first use.
267          *
268          *  MMX+XMM (SSE2): Typical on Athlons, later P4s. 128 bit media insn.
269          *  MMX: Typical on XPs and P3s.  64 bit media insn.
270          */
271
272 #define MMX_SAVE_BLOCK(missfunc)                                        \
273         cmpl    $2048,%ecx ;                                            \
274         jb      missfunc ;                                              \
275         movl    MYCPU,%eax ;                    /* EAX = MYCPU */       \
276         btsl    $1,GD_FPU_LOCK(%eax) ;                                  \
277         jc      missfunc ;                                              \
278         pushl   %ebx ;                                                  \
279         pushl   %ecx ;                                                  \
280         movl    GD_CURTHREAD(%eax),%edx ;       /* EDX = CURTHREAD */   \
281         movl    TD_SAVEFPU(%edx),%ebx ;         /* save app save area */\
282         addl    $TDPRI_CRIT,TD_PRI(%edx) ;                              \
283         cmpl    $0,GD_NPXTHREAD(%eax) ;                                 \
284         je      100f ;                                                  \
285         fxsave  0(%ebx) ;                       /* race(1) */           \
286         movl    $0,GD_NPXTHREAD(%eax) ;         /* interlock intr */    \
287         clts ;                                                          \
288         fninit ;                                /* race(2) */           \
289 100: ;                                                                  \
290         leal    GD_SAVEFPU(%eax),%ecx ;                                 \
291         movl    %ecx,TD_SAVEFPU(%edx) ;                                 \
292         clts ;                                                          \
293         movl    %edx,GD_NPXTHREAD(%eax) ;       /* race(3) */           \
294         subl    $TDPRI_CRIT,TD_PRI(%edx) ;      /* crit_exit() */       \
295         cmpl    $0,GD_REQFLAGS(%eax) ;                                  \
296         je      101f ;                                                  \
297         cmpl    $TDPRI_CRIT,TD_PRI(%edx) ;                              \
298         jge     101f ;                                                  \
299         call    lwkt_yield_quick ;                                      \
300         /* note: eax,ecx,edx destroyed */                               \
301 101: ;                                                                  \
302         movl    (%esp),%ecx ;                                           \
303         movl    $mmx_onfault,(%esp) ;                                   \
304
305         /*
306          * When restoring the application's FP state we must first clear
307          * npxthread to prevent further saves, then restore the pointer
308          * to the app's save area.  We do not have to (and should not)
309          * restore the app's FP state now.  Note that we do not have to
310          * call fninit because our use of the FP guarentees that it is in
311          * a 'safe' state (at least for kernel use).
312          *
313          * NOTE: it is not usually safe to mess with CR0 outside of a
314          * critical section, because TS may get set by a preemptive
315          * interrupt.  However, we *can* race a load/set-ts/store against
316          * an interrupt doing the same thing.
317          */
318
319 #define MMX_RESTORE_BLOCK                       \
320         addl    $4,%esp ;                       \
321         MMX_RESTORE_BLOCK2
322
323 #define MMX_RESTORE_BLOCK2                      \
324         movl    MYCPU,%ecx ;                    \
325         movl    GD_CURTHREAD(%ecx),%edx ;       \
326         movl    $0,GD_NPXTHREAD(%ecx) ;         \
327         movl    %ebx,TD_SAVEFPU(%edx) ;         \
328         smsw    %ax ;                           \
329         popl    %ebx ;                          \
330         orb     $CR0_TS,%al ;                   \
331         lmsw    %ax ;                           \
332         movl    $0,GD_FPU_LOCK(%ecx)
333
334         /*
335          * xmm/mmx_onfault routine.  Restore the fpu state, skip the normal
336          * return vector, and return to the caller's on-fault routine
337          * (which was pushed on the callers stack just before he called us)
338          */
339         ALIGN_TEXT
340 mmx_onfault:
341         MMX_RESTORE_BLOCK2
342         addl    $4,%esp
343         ret
344
345         /*
346          * MXX entry points - only support 64 bit media instructions
347          */
348         SUPERALIGN_TEXT
349 ENTRY(asm_mmx_memcpy)           /* memcpy() entry point use optimal copy */
350         MMX_SAVE_BLOCK(asm_generic_memcpy)
351         jmp     5f
352
353         SUPERALIGN_TEXT
354 ENTRY(asm_mmx_bcopy)
355         MMX_SAVE_BLOCK(asm_generic_bcopy)
356         cmpl    %esi,%edi       /* if (edi < esi) fwd copy ok */
357         jb      5f
358         addl    %ecx,%esi
359         cmpl    %esi,%edi       /* if (edi < esi + count) do bkwrds copy */
360         jb      10f
361         subl    %ecx,%esi
362         jmp     5f
363
364         /*
365          * XMM entry points - support 128 bit media instructions
366          */
367         SUPERALIGN_TEXT
368 ENTRY(asm_xmm_memcpy)           /* memcpy() entry point use optimal copy */
369         MMX_SAVE_BLOCK(asm_generic_memcpy)
370         jmp     1f
371
372         SUPERALIGN_TEXT
373 ENTRY(asm_xmm_bcopy)
374         MMX_SAVE_BLOCK(asm_generic_bcopy)
375         cmpl    %esi,%edi       /* if (edi < esi) fwd copy ok */
376         jb      1f
377         addl    %ecx,%esi
378         cmpl    %esi,%edi       /* if (edi < esi + count) do bkwrds copy */
379         jb      10f
380         subl    %ecx,%esi
381 1:
382         movl    %esi,%eax       /* skip xmm if the data is not aligned */
383         andl    $15,%eax
384         jnz     5f
385         movl    %edi,%eax
386         andl    $15,%eax
387         jz      3f
388         jmp     5f
389
390         SUPERALIGN_TEXT
391 2:
392         movdqa  (%esi),%xmm0
393         movdqa  16(%esi),%xmm1
394         movdqa  32(%esi),%xmm2
395         movdqa  48(%esi),%xmm3
396         movdqa  64(%esi),%xmm4
397         movdqa  80(%esi),%xmm5
398         movdqa  96(%esi),%xmm6
399         movdqa  112(%esi),%xmm7
400         /*prefetchnta 128(%esi) 3dNOW */
401         addl    $128,%esi
402
403         /*
404          * movdqa or movntdq can be used.
405          */
406         movdqa  %xmm0,(%edi)
407         movdqa  %xmm1,16(%edi)
408         movdqa  %xmm2,32(%edi)
409         movdqa  %xmm3,48(%edi)
410         movdqa  %xmm4,64(%edi)
411         movdqa  %xmm5,80(%edi)
412         movdqa  %xmm6,96(%edi)
413         movdqa  %xmm7,112(%edi)
414         addl    $128,%edi
415 3:
416         subl    $128,%ecx
417         jae     2b
418         addl    $128,%ecx
419         jz      6f
420         jmp     5f
421         SUPERALIGN_TEXT
422 4:
423         movq    (%esi),%mm0
424         movq    8(%esi),%mm1
425         movq    16(%esi),%mm2
426         movq    24(%esi),%mm3
427         movq    32(%esi),%mm4
428         movq    40(%esi),%mm5
429         movq    48(%esi),%mm6
430         movq    56(%esi),%mm7
431         /*prefetchnta 128(%esi) 3dNOW */
432         addl    $64,%esi
433         movq    %mm0,(%edi)
434         movq    %mm1,8(%edi)
435         movq    %mm2,16(%edi)
436         movq    %mm3,24(%edi)
437         movq    %mm4,32(%edi)
438         movq    %mm5,40(%edi)
439         movq    %mm6,48(%edi)
440         movq    %mm7,56(%edi)
441         addl    $64,%edi
442 5:
443         subl    $64,%ecx
444         jae     4b
445         addl    $64,%ecx
446         jz      6f
447         cld
448         rep
449         movsb
450 6:
451         MMX_RESTORE_BLOCK
452         ret
453
454         /*
455          * GENERIC_BCOPY() - BACKWARDS COPY
456          *
457          * Don't bother using xmm optimizations, just stick with mmx.
458          */
459         SUPERALIGN_TEXT
460 10:
461         addl    %ecx,%edi
462         jmp     12f
463
464         SUPERALIGN_TEXT
465 11:
466         movq    -64(%esi),%mm0
467         movq    -56(%esi),%mm1
468         movq    -48(%esi),%mm2
469         movq    -40(%esi),%mm3
470         movq    -32(%esi),%mm4
471         movq    -24(%esi),%mm5
472         movq    -16(%esi),%mm6
473         movq    -8(%esi),%mm7
474         /*prefetchnta -128(%esi)*/
475         subl    $64,%esi
476         movq    %mm0,-64(%edi)
477         movq    %mm1,-56(%edi)
478         movq    %mm2,-48(%edi)
479         movq    %mm3,-40(%edi)
480         movq    %mm4,-32(%edi)
481         movq    %mm5,-24(%edi)
482         movq    %mm6,-16(%edi)
483         movq    %mm7,-8(%edi)
484         subl    $64,%edi
485 12:
486         subl    $64,%ecx
487         jae     11b
488         addl    $64,%ecx
489         jz      13f
490         decl    %esi
491         decl    %edi
492         std
493         rep
494         movsb
495         cld
496 13:
497         MMX_RESTORE_BLOCK
498         ret
499