]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/amd64/support.S
amd64: replace libkern's memset and memmove with assembly variants
[FreeBSD/FreeBSD.git] / sys / amd64 / amd64 / support.S
1 /*-
2  * Copyright (c) 2003 Peter Wemm.
3  * Copyright (c) 1993 The Regents of the University of California.
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  * 3. Neither the name of the University nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32
33 #include "opt_ddb.h"
34
35 #include <machine/asmacros.h>
36 #include <machine/specialreg.h>
37 #include <machine/pmap.h>
38
39 #include "assym.inc"
40
41         .text
42
43 /*
44  * bcopy family
45  * void bzero(void *buf, u_int len)
46  */
47
48 /* done */
49 ENTRY(bzero)
50         PUSH_FRAME_POINTER
51         movq    %rsi,%rcx
52         xorl    %eax,%eax
53         shrq    $3,%rcx
54         rep
55         stosq
56         movq    %rsi,%rcx
57         andq    $7,%rcx
58         rep
59         stosb
60         POP_FRAME_POINTER
61         ret
62 END(bzero)
63
64 /* Address: %rdi */
65 ENTRY(pagezero)
66         PUSH_FRAME_POINTER
67         movq    $PAGE_SIZE/8,%rcx
68         xorl    %eax,%eax
69         rep
70         stosq
71         POP_FRAME_POINTER
72         ret
73 END(pagezero)
74
75 /* Address: %rdi */
76 ENTRY(sse2_pagezero)
77         PUSH_FRAME_POINTER
78         movq    $-PAGE_SIZE,%rdx
79         subq    %rdx,%rdi
80         xorl    %eax,%eax
81         jmp     1f
82         /*
83          * The loop takes 29 bytes.  Ensure that it doesn't cross a 32-byte
84          * cache line.
85          */
86         .p2align 5,0x90
87 1:
88         movnti  %rax,(%rdi,%rdx)
89         movnti  %rax,8(%rdi,%rdx)
90         movnti  %rax,16(%rdi,%rdx)
91         movnti  %rax,24(%rdi,%rdx)
92         addq    $32,%rdx
93         jne     1b
94         sfence
95         POP_FRAME_POINTER
96         ret
97 END(sse2_pagezero)
98
99 ENTRY(bcmp)
100         PUSH_FRAME_POINTER
101         movq    %rdx,%rcx
102         shrq    $3,%rcx
103         repe
104         cmpsq
105         jne     1f
106
107         movq    %rdx,%rcx
108         andq    $7,%rcx
109         repe
110         cmpsb
111 1:
112         setne   %al
113         movsbl  %al,%eax
114         POP_FRAME_POINTER
115         ret
116 END(bcmp)
117
118 /*
119  * bcopy(src, dst, cnt)
120  *       rdi, rsi, rdx
121  *  ws@tools.de     (Wolfgang Solfrank, TooLs GmbH) +49-228-985800
122  */
123 ENTRY(bcopy)
124         PUSH_FRAME_POINTER
125         xchgq   %rsi,%rdi
126         movq    %rdx,%rcx
127
128         movq    %rdi,%rax
129         subq    %rsi,%rax
130         cmpq    %rcx,%rax                       /* overlapping && src < dst? */
131         jb      1f
132
133         shrq    $3,%rcx                         /* copy by 64-bit words */
134         rep
135         movsq
136         movq    %rdx,%rcx
137         andq    $7,%rcx                         /* any bytes left? */
138         rep
139         movsb
140         POP_FRAME_POINTER
141         ret
142
143         /* ALIGN_TEXT */
144 1:
145         addq    %rcx,%rdi                       /* copy backwards */
146         addq    %rcx,%rsi
147         decq    %rdi
148         decq    %rsi
149         andq    $7,%rcx                         /* any fractional bytes? */
150         std
151         rep
152         movsb
153         movq    %rdx,%rcx                       /* copy remainder by 32-bit words */
154         shrq    $3,%rcx
155         subq    $7,%rsi
156         subq    $7,%rdi
157         rep
158         movsq
159         cld
160         POP_FRAME_POINTER
161         ret
162 END(bcopy)
163
164 /*
165  * memmove(dst, src, cnt)
166  *         rdi, rsi, rdx
167  * Original by:
168  *  ws@tools.de     (Wolfgang Solfrank, TooLs GmbH) +49-228-985800
169  */
170 ENTRY(memmove)
171         PUSH_FRAME_POINTER
172         movq    %rdi,%r9
173         movq    %rdx,%rcx
174
175         movq    %rdi,%rax
176         subq    %rsi,%rax
177         cmpq    %rcx,%rax                       /* overlapping && src < dst? */
178         jb      1f
179
180         shrq    $3,%rcx                         /* copy by 64-bit words */
181         rep
182         movsq
183         movq    %rdx,%rcx
184         andq    $7,%rcx                         /* any bytes left? */
185         rep
186         movsb
187         movq    %r9,%rax
188         POP_FRAME_POINTER
189         ret
190
191         /* ALIGN_TEXT */
192 1:
193         addq    %rcx,%rdi                       /* copy backwards */
194         addq    %rcx,%rsi
195         decq    %rdi
196         decq    %rsi
197         andq    $7,%rcx                         /* any fractional bytes? */
198         std
199         rep
200         movsb
201         movq    %rdx,%rcx                       /* copy remainder by 32-bit words */
202         shrq    $3,%rcx
203         subq    $7,%rsi
204         subq    $7,%rdi
205         rep
206         movsq
207         cld
208         movq    %r9,%rax
209         POP_FRAME_POINTER
210         ret
211 END(memmove)
212
213 /*
214  * memcpy(dst, src, len)
215  *        rdi, rsi, rdx
216  *
217  * Note: memcpy does not support overlapping copies
218  */
219 ENTRY(memcpy)
220         PUSH_FRAME_POINTER
221         movq    %rdi,%rax
222         movq    %rdx,%rcx
223         shrq    $3,%rcx                         /* copy by 64-bit words */
224         rep
225         movsq
226         movq    %rdx,%rcx
227         andq    $7,%rcx                         /* any bytes left? */
228         rep
229         movsb
230         POP_FRAME_POINTER
231         ret
232 END(memcpy)
233
234 /*
235  * memset(dst, c,   len)
236  *        rdi, rsi, rdx
237  */
238 ENTRY(memset)
239         PUSH_FRAME_POINTER
240         movq    %rdi,%r9
241         movq    %rdx,%rcx
242         movq    %rsi,%rax
243         shrq    $3,%rcx
244         rep
245         stosq
246         movq    %rdx,%rcx
247         andq    $7,%rcx
248         rep
249         stosb
250         movq    %r9,%rax
251         POP_FRAME_POINTER
252         ret
253 END(memset)
254
255 /*
256  * pagecopy(%rdi=from, %rsi=to)
257  */
258 ENTRY(pagecopy)
259         PUSH_FRAME_POINTER
260         movq    $-PAGE_SIZE,%rax
261         movq    %rax,%rdx
262         subq    %rax,%rdi
263         subq    %rax,%rsi
264 1:
265         prefetchnta (%rdi,%rax)
266         addq    $64,%rax
267         jne     1b
268 2:
269         movq    (%rdi,%rdx),%rax
270         movnti  %rax,(%rsi,%rdx)
271         movq    8(%rdi,%rdx),%rax
272         movnti  %rax,8(%rsi,%rdx)
273         movq    16(%rdi,%rdx),%rax
274         movnti  %rax,16(%rsi,%rdx)
275         movq    24(%rdi,%rdx),%rax
276         movnti  %rax,24(%rsi,%rdx)
277         addq    $32,%rdx
278         jne     2b
279         sfence
280         POP_FRAME_POINTER
281         ret
282 END(pagecopy)
283
284 /* fillw(pat, base, cnt) */
285 /*       %rdi,%rsi, %rdx */
286 ENTRY(fillw)
287         PUSH_FRAME_POINTER
288         movq    %rdi,%rax
289         movq    %rsi,%rdi
290         movq    %rdx,%rcx
291         rep
292         stosw
293         POP_FRAME_POINTER
294         ret
295 END(fillw)
296
297 /*****************************************************************************/
298 /* copyout and fubyte family                                                 */
299 /*****************************************************************************/
300 /*
301  * Access user memory from inside the kernel. These routines should be
302  * the only places that do this.
303  *
304  * These routines set curpcb->pcb_onfault for the time they execute. When a
305  * protection violation occurs inside the functions, the trap handler
306  * returns to *curpcb->pcb_onfault instead of the function.
307  */
308
309 /*
310  * copyout(from_kernel, to_user, len)
311  *         %rdi,        %rsi,    %rdx
312  */
313 ENTRY(copyout)
314         PUSH_FRAME_POINTER
315         movq    PCPU(CURPCB),%rax
316         movq    $copyout_fault,PCB_ONFAULT(%rax)
317         testq   %rdx,%rdx                       /* anything to do? */
318         jz      done_copyout
319
320         /*
321          * Check explicitly for non-user addresses.  This check is essential
322          * because it prevents usermode from writing into the kernel.  We do
323          * not verify anywhere else that the user did not specify a rogue
324          * address.
325          */
326         /*
327          * First, prevent address wrapping.
328          */
329         movq    %rsi,%rax
330         addq    %rdx,%rax
331         jc      copyout_fault
332 /*
333  * XXX STOP USING VM_MAXUSER_ADDRESS.
334  * It is an end address, not a max, so every time it is used correctly it
335  * looks like there is an off by one error, and of course it caused an off
336  * by one error in several places.
337  */
338         movq    $VM_MAXUSER_ADDRESS,%rcx
339         cmpq    %rcx,%rax
340         ja      copyout_fault
341
342         xchgq   %rdi,%rsi
343         /* bcopy(%rsi, %rdi, %rdx) */
344         movq    %rdx,%rcx
345
346         shrq    $3,%rcx
347         rep
348         movsq
349         movb    %dl,%cl
350         andb    $7,%cl
351         rep
352         movsb
353
354 done_copyout:
355         xorl    %eax,%eax
356         movq    PCPU(CURPCB),%rdx
357         movq    %rax,PCB_ONFAULT(%rdx)
358         POP_FRAME_POINTER
359         ret
360
361         ALIGN_TEXT
362 copyout_fault:
363         movq    PCPU(CURPCB),%rdx
364         movq    $0,PCB_ONFAULT(%rdx)
365         movq    $EFAULT,%rax
366         POP_FRAME_POINTER
367         ret
368 END(copyout)
369
370 /*
371  * copyin(from_user, to_kernel, len)
372  *        %rdi,      %rsi,      %rdx
373  */
374 ENTRY(copyin)
375         PUSH_FRAME_POINTER
376         movq    PCPU(CURPCB),%rax
377         movq    $copyin_fault,PCB_ONFAULT(%rax)
378         testq   %rdx,%rdx                       /* anything to do? */
379         jz      done_copyin
380
381         /*
382          * make sure address is valid
383          */
384         movq    %rdi,%rax
385         addq    %rdx,%rax
386         jc      copyin_fault
387         movq    $VM_MAXUSER_ADDRESS,%rcx
388         cmpq    %rcx,%rax
389         ja      copyin_fault
390
391         xchgq   %rdi,%rsi
392         movq    %rdx,%rcx
393         movb    %cl,%al
394         shrq    $3,%rcx                         /* copy longword-wise */
395         rep
396         movsq
397         movb    %al,%cl
398         andb    $7,%cl                          /* copy remaining bytes */
399         rep
400         movsb
401
402 done_copyin:
403         xorl    %eax,%eax
404         movq    PCPU(CURPCB),%rdx
405         movq    %rax,PCB_ONFAULT(%rdx)
406         POP_FRAME_POINTER
407         ret
408
409         ALIGN_TEXT
410 copyin_fault:
411         movq    PCPU(CURPCB),%rdx
412         movq    $0,PCB_ONFAULT(%rdx)
413         movq    $EFAULT,%rax
414         POP_FRAME_POINTER
415         ret
416 END(copyin)
417
418 /*
419  * casueword32.  Compare and set user integer.  Returns -1 on fault,
420  *        0 if access was successful.  Old value is written to *oldp.
421  *        dst = %rdi, old = %esi, oldp = %rdx, new = %ecx
422  */
423 ENTRY(casueword32)
424         PUSH_FRAME_POINTER
425         movq    PCPU(CURPCB),%r8
426         movq    $fusufault,PCB_ONFAULT(%r8)
427
428         movq    $VM_MAXUSER_ADDRESS-4,%rax
429         cmpq    %rax,%rdi                       /* verify address is valid */
430         ja      fusufault
431
432         movl    %esi,%eax                       /* old */
433 #ifdef SMP
434         lock
435 #endif
436         cmpxchgl %ecx,(%rdi)                    /* new = %ecx */
437
438         /*
439          * The old value is in %eax.  If the store succeeded it will be the
440          * value we expected (old) from before the store, otherwise it will
441          * be the current value.  Save %eax into %esi to prepare the return
442          * value.
443          */
444         movl    %eax,%esi
445         xorl    %eax,%eax
446         movq    %rax,PCB_ONFAULT(%r8)
447
448         /*
449          * Access the oldp after the pcb_onfault is cleared, to correctly
450          * catch corrupted pointer.
451          */
452         movl    %esi,(%rdx)                     /* oldp = %rdx */
453         POP_FRAME_POINTER
454         ret
455 END(casueword32)
456
457 /*
458  * casueword.  Compare and set user long.  Returns -1 on fault,
459  *        0 if access was successful.  Old value is written to *oldp.
460  *        dst = %rdi, old = %rsi, oldp = %rdx, new = %rcx
461  */
462 ENTRY(casueword)
463         PUSH_FRAME_POINTER
464         movq    PCPU(CURPCB),%r8
465         movq    $fusufault,PCB_ONFAULT(%r8)
466
467         movq    $VM_MAXUSER_ADDRESS-4,%rax
468         cmpq    %rax,%rdi                       /* verify address is valid */
469         ja      fusufault
470
471         movq    %rsi,%rax                       /* old */
472 #ifdef SMP
473         lock
474 #endif
475         cmpxchgq %rcx,(%rdi)                    /* new = %rcx */
476
477         /*
478          * The old value is in %rax.  If the store succeeded it will be the
479          * value we expected (old) from before the store, otherwise it will
480          * be the current value.
481          */
482         movq    %rax,%rsi
483         xorl    %eax,%eax
484         movq    %rax,PCB_ONFAULT(%r8)
485         movq    %rsi,(%rdx)
486         POP_FRAME_POINTER
487         ret
488 END(casueword)
489
490 /*
491  * Fetch (load) a 64-bit word, a 32-bit word, a 16-bit word, or an 8-bit
492  * byte from user memory.
493  * addr = %rdi, valp = %rsi
494  */
495
496 ALTENTRY(fueword64)
497 ENTRY(fueword)
498         PUSH_FRAME_POINTER
499         movq    PCPU(CURPCB),%rcx
500         movq    $fusufault,PCB_ONFAULT(%rcx)
501
502         movq    $VM_MAXUSER_ADDRESS-8,%rax
503         cmpq    %rax,%rdi                       /* verify address is valid */
504         ja      fusufault
505
506         xorl    %eax,%eax
507         movq    (%rdi),%r11
508         movq    %rax,PCB_ONFAULT(%rcx)
509         movq    %r11,(%rsi)
510         POP_FRAME_POINTER
511         ret
512 END(fueword64)
513 END(fueword)
514
515 ENTRY(fueword32)
516         PUSH_FRAME_POINTER
517         movq    PCPU(CURPCB),%rcx
518         movq    $fusufault,PCB_ONFAULT(%rcx)
519
520         movq    $VM_MAXUSER_ADDRESS-4,%rax
521         cmpq    %rax,%rdi                       /* verify address is valid */
522         ja      fusufault
523
524         xorl    %eax,%eax
525         movl    (%rdi),%r11d
526         movq    %rax,PCB_ONFAULT(%rcx)
527         movl    %r11d,(%rsi)
528         POP_FRAME_POINTER
529         ret
530 END(fueword32)
531
532 ENTRY(fuword16)
533         PUSH_FRAME_POINTER
534         movq    PCPU(CURPCB),%rcx
535         movq    $fusufault,PCB_ONFAULT(%rcx)
536
537         movq    $VM_MAXUSER_ADDRESS-2,%rax
538         cmpq    %rax,%rdi
539         ja      fusufault
540
541         movzwl  (%rdi),%eax
542         movq    $0,PCB_ONFAULT(%rcx)
543         POP_FRAME_POINTER
544         ret
545 END(fuword16)
546
547 ENTRY(fubyte)
548         PUSH_FRAME_POINTER
549         movq    PCPU(CURPCB),%rcx
550         movq    $fusufault,PCB_ONFAULT(%rcx)
551
552         movq    $VM_MAXUSER_ADDRESS-1,%rax
553         cmpq    %rax,%rdi
554         ja      fusufault
555
556         movzbl  (%rdi),%eax
557         movq    $0,PCB_ONFAULT(%rcx)
558         POP_FRAME_POINTER
559         ret
560 END(fubyte)
561
562         ALIGN_TEXT
563 fusufault:
564         movq    PCPU(CURPCB),%rcx
565         xorl    %eax,%eax
566         movq    %rax,PCB_ONFAULT(%rcx)
567         decq    %rax
568         POP_FRAME_POINTER
569         ret
570
571 /*
572  * Store a 64-bit word, a 32-bit word, a 16-bit word, or an 8-bit byte to
573  * user memory.
574  * addr = %rdi, value = %rsi
575  */
576 ALTENTRY(suword64)
577 ENTRY(suword)
578         PUSH_FRAME_POINTER
579         movq    PCPU(CURPCB),%rcx
580         movq    $fusufault,PCB_ONFAULT(%rcx)
581
582         movq    $VM_MAXUSER_ADDRESS-8,%rax
583         cmpq    %rax,%rdi                       /* verify address validity */
584         ja      fusufault
585
586         movq    %rsi,(%rdi)
587         xorl    %eax,%eax
588         movq    PCPU(CURPCB),%rcx
589         movq    %rax,PCB_ONFAULT(%rcx)
590         POP_FRAME_POINTER
591         ret
592 END(suword64)
593 END(suword)
594
595 ENTRY(suword32)
596         PUSH_FRAME_POINTER
597         movq    PCPU(CURPCB),%rcx
598         movq    $fusufault,PCB_ONFAULT(%rcx)
599
600         movq    $VM_MAXUSER_ADDRESS-4,%rax
601         cmpq    %rax,%rdi                       /* verify address validity */
602         ja      fusufault
603
604         movl    %esi,(%rdi)
605         xorl    %eax,%eax
606         movq    PCPU(CURPCB),%rcx
607         movq    %rax,PCB_ONFAULT(%rcx)
608         POP_FRAME_POINTER
609         ret
610 END(suword32)
611
612 ENTRY(suword16)
613         PUSH_FRAME_POINTER
614         movq    PCPU(CURPCB),%rcx
615         movq    $fusufault,PCB_ONFAULT(%rcx)
616
617         movq    $VM_MAXUSER_ADDRESS-2,%rax
618         cmpq    %rax,%rdi                       /* verify address validity */
619         ja      fusufault
620
621         movw    %si,(%rdi)
622         xorl    %eax,%eax
623         movq    PCPU(CURPCB),%rcx               /* restore trashed register */
624         movq    %rax,PCB_ONFAULT(%rcx)
625         POP_FRAME_POINTER
626         ret
627 END(suword16)
628
629 ENTRY(subyte)
630         PUSH_FRAME_POINTER
631         movq    PCPU(CURPCB),%rcx
632         movq    $fusufault,PCB_ONFAULT(%rcx)
633
634         movq    $VM_MAXUSER_ADDRESS-1,%rax
635         cmpq    %rax,%rdi                       /* verify address validity */
636         ja      fusufault
637
638         movl    %esi,%eax
639         movb    %al,(%rdi)
640         xorl    %eax,%eax
641         movq    PCPU(CURPCB),%rcx               /* restore trashed register */
642         movq    %rax,PCB_ONFAULT(%rcx)
643         POP_FRAME_POINTER
644         ret
645 END(subyte)
646
647 /*
648  * copyinstr(from, to, maxlen, int *lencopied)
649  *           %rdi, %rsi, %rdx, %rcx
650  *
651  *      copy a string from 'from' to 'to', stop when a 0 character is reached.
652  *      return ENAMETOOLONG if string is longer than maxlen, and
653  *      EFAULT on protection violations. If lencopied is non-zero,
654  *      return the actual length in *lencopied.
655  */
656 ENTRY(copyinstr)
657         PUSH_FRAME_POINTER
658         movq    %rdx,%r8                        /* %r8 = maxlen */
659         movq    %rcx,%r9                        /* %r9 = *len */
660         xchgq   %rdi,%rsi                       /* %rdi = from, %rsi = to */
661         movq    PCPU(CURPCB),%rcx
662         movq    $cpystrflt,PCB_ONFAULT(%rcx)
663
664         movq    $VM_MAXUSER_ADDRESS,%rax
665
666         /* make sure 'from' is within bounds */
667         subq    %rsi,%rax
668         jbe     cpystrflt
669
670         /* restrict maxlen to <= VM_MAXUSER_ADDRESS-from */
671         cmpq    %rdx,%rax
672         jae     1f
673         movq    %rax,%rdx
674         movq    %rax,%r8
675 1:
676         incq    %rdx
677
678 2:
679         decq    %rdx
680         jz      3f
681
682         lodsb
683         stosb
684         orb     %al,%al
685         jnz     2b
686
687         /* Success -- 0 byte reached */
688         decq    %rdx
689         xorl    %eax,%eax
690         jmp     cpystrflt_x
691 3:
692         /* rdx is zero - return ENAMETOOLONG or EFAULT */
693         movq    $VM_MAXUSER_ADDRESS,%rax
694         cmpq    %rax,%rsi
695         jae     cpystrflt
696 4:
697         movq    $ENAMETOOLONG,%rax
698         jmp     cpystrflt_x
699
700 cpystrflt:
701         movq    $EFAULT,%rax
702
703 cpystrflt_x:
704         /* set *lencopied and return %eax */
705         movq    PCPU(CURPCB),%rcx
706         movq    $0,PCB_ONFAULT(%rcx)
707
708         testq   %r9,%r9
709         jz      1f
710         subq    %rdx,%r8
711         movq    %r8,(%r9)
712 1:
713         POP_FRAME_POINTER
714         ret
715 END(copyinstr)
716
717 /*
718  * copystr(from, to, maxlen, int *lencopied)
719  *         %rdi, %rsi, %rdx, %rcx
720  */
721 ENTRY(copystr)
722         PUSH_FRAME_POINTER
723         movq    %rdx,%r8                        /* %r8 = maxlen */
724
725         xchgq   %rdi,%rsi
726         incq    %rdx
727 1:
728         decq    %rdx
729         jz      4f
730         lodsb
731         stosb
732         orb     %al,%al
733         jnz     1b
734
735         /* Success -- 0 byte reached */
736         decq    %rdx
737         xorl    %eax,%eax
738         jmp     6f
739 4:
740         /* rdx is zero -- return ENAMETOOLONG */
741         movq    $ENAMETOOLONG,%rax
742
743 6:
744
745         testq   %rcx,%rcx
746         jz      7f
747         /* set *lencopied and return %rax */
748         subq    %rdx,%r8
749         movq    %r8,(%rcx)
750 7:
751         POP_FRAME_POINTER
752         ret
753 END(copystr)
754
755 /*
756  * Handling of special amd64 registers and descriptor tables etc
757  */
758 /* void lgdt(struct region_descriptor *rdp); */
759 ENTRY(lgdt)
760         /* reload the descriptor table */
761         lgdt    (%rdi)
762
763         /* flush the prefetch q */
764         jmp     1f
765         nop
766 1:
767         movl    $KDSEL,%eax
768         movl    %eax,%ds
769         movl    %eax,%es
770         movl    %eax,%fs        /* Beware, use wrmsr to set 64 bit base */
771         movl    %eax,%gs
772         movl    %eax,%ss
773
774         /* reload code selector by turning return into intersegmental return */
775         popq    %rax
776         pushq   $KCSEL
777         pushq   %rax
778         MEXITCOUNT
779         lretq
780 END(lgdt)
781
782 /*****************************************************************************/
783 /* setjump, longjump                                                         */
784 /*****************************************************************************/
785
786 ENTRY(setjmp)
787         movq    %rbx,0(%rdi)                    /* save rbx */
788         movq    %rsp,8(%rdi)                    /* save rsp */
789         movq    %rbp,16(%rdi)                   /* save rbp */
790         movq    %r12,24(%rdi)                   /* save r12 */
791         movq    %r13,32(%rdi)                   /* save r13 */
792         movq    %r14,40(%rdi)                   /* save r14 */
793         movq    %r15,48(%rdi)                   /* save r15 */
794         movq    0(%rsp),%rdx                    /* get rta */
795         movq    %rdx,56(%rdi)                   /* save rip */
796         xorl    %eax,%eax                       /* return(0); */
797         ret
798 END(setjmp)
799
800 ENTRY(longjmp)
801         movq    0(%rdi),%rbx                    /* restore rbx */
802         movq    8(%rdi),%rsp                    /* restore rsp */
803         movq    16(%rdi),%rbp                   /* restore rbp */
804         movq    24(%rdi),%r12                   /* restore r12 */
805         movq    32(%rdi),%r13                   /* restore r13 */
806         movq    40(%rdi),%r14                   /* restore r14 */
807         movq    48(%rdi),%r15                   /* restore r15 */
808         movq    56(%rdi),%rdx                   /* get rta */
809         movq    %rdx,0(%rsp)                    /* put in return frame */
810         xorl    %eax,%eax                       /* return(1); */
811         incl    %eax
812         ret
813 END(longjmp)
814
815 /*
816  * Support for reading MSRs in the safe manner.  (Instead of panic on #gp,
817  * return an error.)
818  */
819 ENTRY(rdmsr_safe)
820 /* int rdmsr_safe(u_int msr, uint64_t *data) */
821         PUSH_FRAME_POINTER
822         movq    PCPU(CURPCB),%r8
823         movq    $msr_onfault,PCB_ONFAULT(%r8)
824         movl    %edi,%ecx
825         rdmsr                   /* Read MSR pointed by %ecx. Returns
826                                    hi byte in edx, lo in %eax */
827         salq    $32,%rdx        /* sign-shift %rdx left */
828         movl    %eax,%eax       /* zero-extend %eax -> %rax */
829         orq     %rdx,%rax
830         movq    %rax,(%rsi)
831         xorq    %rax,%rax
832         movq    %rax,PCB_ONFAULT(%r8)
833         POP_FRAME_POINTER
834         ret
835
836 /*
837  * Support for writing MSRs in the safe manner.  (Instead of panic on #gp,
838  * return an error.)
839  */
840 ENTRY(wrmsr_safe)
841 /* int wrmsr_safe(u_int msr, uint64_t data) */
842         PUSH_FRAME_POINTER
843         movq    PCPU(CURPCB),%r8
844         movq    $msr_onfault,PCB_ONFAULT(%r8)
845         movl    %edi,%ecx
846         movl    %esi,%eax
847         sarq    $32,%rsi
848         movl    %esi,%edx
849         wrmsr                   /* Write MSR pointed by %ecx. Accepts
850                                    hi byte in edx, lo in %eax. */
851         xorq    %rax,%rax
852         movq    %rax,PCB_ONFAULT(%r8)
853         POP_FRAME_POINTER
854         ret
855
856 /*
857  * MSR operations fault handler
858  */
859         ALIGN_TEXT
860 msr_onfault:
861         movq    $0,PCB_ONFAULT(%r8)
862         movl    $EFAULT,%eax
863         POP_FRAME_POINTER
864         ret
865
866 /*
867  * void pmap_pti_pcid_invalidate(uint64_t ucr3, uint64_t kcr3);
868  * Invalidates address space addressed by ucr3, then returns to kcr3.
869  * Done in assembler to ensure no other memory accesses happen while
870  * on ucr3.
871  */
872         ALIGN_TEXT
873 ENTRY(pmap_pti_pcid_invalidate)
874         pushfq
875         cli
876         movq    %rdi,%cr3       /* to user page table */
877         movq    %rsi,%cr3       /* back to kernel */
878         popfq
879         retq
880
881 /*
882  * void pmap_pti_pcid_invlpg(uint64_t ucr3, uint64_t kcr3, vm_offset_t va);
883  * Invalidates virtual address va in address space ucr3, then returns to kcr3.
884  */
885         ALIGN_TEXT
886 ENTRY(pmap_pti_pcid_invlpg)
887         pushfq
888         cli
889         movq    %rdi,%cr3       /* to user page table */
890         invlpg  (%rdx)
891         movq    %rsi,%cr3       /* back to kernel */
892         popfq
893         retq
894
895 /*
896  * void pmap_pti_pcid_invlrng(uint64_t ucr3, uint64_t kcr3, vm_offset_t sva,
897  *     vm_offset_t eva);
898  * Invalidates virtual addresses between sva and eva in address space ucr3,
899  * then returns to kcr3.
900  */
901         ALIGN_TEXT
902 ENTRY(pmap_pti_pcid_invlrng)
903         pushfq
904         cli
905         movq    %rdi,%cr3       /* to user page table */
906 1:      invlpg  (%rdx)
907         addq    $PAGE_SIZE,%rdx
908         cmpq    %rdx,%rcx
909         ja      1b
910         movq    %rsi,%cr3       /* back to kernel */
911         popfq
912         retq
913
914         .altmacro
915         .macro  ibrs_seq_label l
916 handle_ibrs_\l:
917         .endm
918         .macro  ibrs_call_label l
919         call    handle_ibrs_\l
920         .endm
921         .macro  ibrs_seq count
922         ll=1
923         .rept   \count
924         ibrs_call_label %(ll)
925         nop
926         ibrs_seq_label %(ll)
927         addq    $8,%rsp
928         ll=ll+1
929         .endr
930         .endm
931
932 /* all callers already saved %rax, %rdx, and %rcx */
933 ENTRY(handle_ibrs_entry)
934         cmpb    $0,hw_ibrs_active(%rip)
935         je      1f
936         movl    $MSR_IA32_SPEC_CTRL,%ecx
937         movl    $(IA32_SPEC_CTRL_IBRS|IA32_SPEC_CTRL_STIBP),%eax
938         movl    $(IA32_SPEC_CTRL_IBRS|IA32_SPEC_CTRL_STIBP)>>32,%edx
939         wrmsr
940         movb    $1,PCPU(IBPB_SET)
941         testl   $CPUID_STDEXT_SMEP,cpu_stdext_feature(%rip)
942         jne     1f
943         ibrs_seq 32
944 1:      ret
945 END(handle_ibrs_entry)
946
947 ENTRY(handle_ibrs_exit)
948         cmpb    $0,PCPU(IBPB_SET)
949         je      1f
950         movl    $MSR_IA32_SPEC_CTRL,%ecx
951         xorl    %eax,%eax
952         xorl    %edx,%edx
953         wrmsr
954         movb    $0,PCPU(IBPB_SET)
955 1:      ret
956 END(handle_ibrs_exit)
957
958 /* registers-neutral version, but needs stack */
959 ENTRY(handle_ibrs_exit_rs)
960         cmpb    $0,PCPU(IBPB_SET)
961         je      1f
962         pushq   %rax
963         pushq   %rdx
964         pushq   %rcx
965         movl    $MSR_IA32_SPEC_CTRL,%ecx
966         xorl    %eax,%eax
967         xorl    %edx,%edx
968         wrmsr
969         popq    %rcx
970         popq    %rdx
971         popq    %rax
972         movb    $0,PCPU(IBPB_SET)
973 1:      ret
974 END(handle_ibrs_exit_rs)
975
976         .noaltmacro