4 #include <minix/config.h>
5 #include <minix/const.h>
6 #include <machine/interrupt.h>
8 #include "kernel/const.h"
12 * This file contains a number of assembly code utility routines needed by the
16 .globl _monitor/* exit Minix and return to the monitor */
17 .globl _int86 /* let the monitor make an 8086 interrupt call */
19 .globl _exit /* dummy for library routines */
20 .globl __exit /* dummy for library routines */
21 .globl ___exit /* dummy for library routines */
23 .globl ___main /* dummy for GCC */
24 .globl _phys_insw /* transfer data from (disk controller) port to memory */
25 .globl _phys_insb /* likewise byte by byte */
26 .globl _phys_outsw /* transfer data from memory to (disk controller) port */
27 .globl _phys_outsb /* likewise byte by byte */
28 .globl _phys_copy /* copy data from anywhere to anywhere in memory */
29 .globl _phys_copy_fault /* phys_copy pagefault */
30 .globl _phys_copy_fault_in_kernel /* phys_copy pagefault in kernel */
31 .globl _phys_memset /* write pattern anywhere in memory */
32 .globl _mem_rdw /* copy one word from [segment:offset] */
33 .globl _reset /* reset the system */
34 .globl _halt_cpu/* halts the current cpu when idle */
35 .globl _read_cpu_flags /* read the cpu flags */
36 .globl _read_cr0 /* read cr0 */
37 .globl _read_cr2 /* read cr2 */
39 .globl _write_cr0 /* write a value in cr0 */
44 .globl _catch_pagefaults
48 .globl _idt_reload /* reload idt when returning to monitor. */
50 .globl _fninit /* non-waiting FPU initialization */
51 .globl _fnstsw /* store status word (non-waiting) */
52 .globl _fnstcw /* store control word (non-waiting) */
60 * The routines only guarantee to preserve the registers the C compiler
61 * expects to be preserved (ebx, esi, edi, ebp, esp, segment registers, and
62 * direction bit in the flags).
66 /*===========================================================================*/
68 /*===========================================================================*/
69 /* PUBLIC void monitor(); */
70 /* Return to the monitor. */
73 movl _mon_sp, %esp /* restore monitor stack pointer */
74 movw $SS_SELECTOR, %dx /* monitor data segment */
83 lretw /* return to the monitor */
86 /*===========================================================================*/
88 /*===========================================================================*/
89 /* PUBLIC void int86(); */
91 cmpb $0, _mon_return /* is the monitor there? */
93 movb $0x01, %ah /* an int 13 error seems appropriate */
94 movb %ah, _reg86+0 /* reg86.w.f = 1 (set carry flag) */
95 movb %ah, _reg86+13 /* reg86.b.ah = 0x01 = "invalid command" */
98 push %ebp /* save C registers */
102 pushf /* save flags */
103 cli /* no interruptions */
108 push %eax /* save interrupt masks */
109 movl _irq_use, %eax /* map of in-use IRQ's */
110 and $~(1<<CLOCK_IRQ), %eax /* keep the clock ticking */
111 outb $INT_CTLMASK /* enable all unused IRQ's and vv. */
115 mov $SS_SELECTOR, %eax /* monitor data segment */
117 xchgl _mon_sp, %esp /* switch stacks */
118 push _reg86+36 /* parameters used in INT call */
128 mov %ax, %ds /* remaining data selectors */
133 push $return /* kernel return address and selector */
134 ljmpw *20+2*4+10*4+2*4(%esp)
146 lgdt _gdt+GDT_SELECTOR /* reload global descriptor table */
147 ljmp $CS_SELECTOR, $csinit
149 mov $DS_SELECTOR, %eax
155 xchgl _mon_sp, %esp /* unswitch stacks */
156 lidt _gdt+IDT_SELECTOR /* reload interrupt descriptor table */
159 cmpl $0x0, lapic_addr
165 mov $FLAT_DS_SELECTOR, %ebx
167 movl lapic_addr, %eax
169 .byte 0x64; mov (%eax), %ebx
170 and $0xFF000000, %ebx
175 add $apicid2cpuid, %ebx
179 add $TSS_SELECTOR, %eax
180 addl _gdt+DESC_ACCESS, %eax
182 ltr %bx /* set TSS register */
184 mov $DS_SELECTOR, %eax
187 #endif /* CONFIG_APIC */
190 outb $INT_CTLMASK /* restore interrupt masks */
195 addl %ecx, _lost_ticks /* record lost clock ticks */
197 popf /* restore flags */
198 pop %ebx /* restore C registers */
204 /*===========================================================================*/
206 /*===========================================================================*/
208 * PUBLIC void exit();
209 * Some library routines use exit, so provide a dummy version.
210 * Actual calls to exit cannot occur in the kernel.
211 * GNU CC likes to call ___main from main() for nonobvious reasons.
225 /*===========================================================================*/
227 /*===========================================================================*/
229 * PUBLIC void phys_insw(Port_t port, phys_bytes buf, size_t count);
230 * Input an array from an I/O port. Absolute address version of insw().
240 mov $FLAT_DS_SELECTOR, %ecx
242 mov 8(%ebp), %edx /* port to read from */
243 mov 12(%ebp), %edi /* destination addr */
244 mov 16(%ebp), %ecx /* byte count */
245 shr $1, %ecx /* word count */
246 rep insw /* input many words */
253 /*===========================================================================*/
255 /*===========================================================================*/
257 * PUBLIC void phys_insb(Port_t port, phys_bytes buf, size_t count);
258 * Input an array from an I/O port. Absolute address version of insb().
268 mov $FLAT_DS_SELECTOR, %ecx
270 mov 8(%ebp), %edx /* port to read from */
271 mov 12(%ebp), %edi /* destination addr */
272 mov 16(%ebp), %ecx /* byte count */
273 rep insb /* input many bytes */
280 /*===========================================================================*/
282 /*===========================================================================*/
284 * PUBLIC void phys_outsw(Port_t port, phys_bytes buf, size_t count);
285 * Output an array to an I/O port. Absolute address version of outsw().
296 mov $FLAT_DS_SELECTOR, %ecx
298 mov 8(%ebp), %edx /* port to write to */
299 mov 12(%ebp), %esi /* source addr */
300 mov 16(%ebp), %ecx /* byte count */
301 shr $1, %ecx /* word count */
302 rep outsw /* output many words */
309 /*===========================================================================*/
311 /*===========================================================================*/
313 * PUBLIC void phys_outsb(Port_t port, phys_bytes buf, size_t count);
314 * Output an array to an I/O port. Absolute address version of outsb().
325 mov $FLAT_DS_SELECTOR, %ecx
327 mov 8(%ebp), %edx /* port to write to */
328 mov 12(%ebp), %esi /* source addr */
329 mov 16(%ebp), %ecx /* byte count */
330 rep outsb /* output many bytes */
337 /*===========================================================================*/
339 /*===========================================================================*/
341 * PUBLIC phys_bytes phys_copy(phys_bytes source, phys_bytes destination,
342 * phys_bytes bytecount);
343 * Copy a block of physical memory.
346 PC_ARGS = 4+4+4+4 /* 4 + 4 + 4 */
347 /* es edi esi eip src dst len */
356 mov $FLAT_DS_SELECTOR, %eax
359 mov PC_ARGS(%esp), %esi
360 mov PC_ARGS+4(%esp), %edi
361 mov PC_ARGS+4+4(%esp), %eax
363 cmp $10, %eax /* avoid align overhead for small counts */
365 mov %esi, %ecx /* align source, hope target is too */
367 and $3, %ecx /* count for alignment */
370 rep movsb %es:(%esi), %es:(%edi)
372 shr $2, %ecx /* count of dwords */
374 rep movsl %es:(%esi), %es:(%edi)
377 xchg %eax, %ecx /* remainder */
379 rep movsb %es:(%esi), %es:(%edi)
381 mov $0, %eax /* 0 means: no fault */
382 _phys_copy_fault: /* kernel can send us here */
388 _phys_copy_fault_in_kernel: /* kernel can send us here */
395 /*===========================================================================*/
396 /* copy_msg_from_user */
397 /*===========================================================================*/
399 * int copy_msg_from_user(struct proc * p, message * user_mbuf, message * dst);
401 * Copies a message of 36 bytes from user process space to a kernel buffer. This
402 * function assumes that the process address space is installed (cr3 loaded) and
403 * the local descriptor table of this process is loaded too.
405 * The %gs segment register is used to access the userspace memory. We load the
406 * process' data segment in this register.
408 * This function from the callers point of view either succeeds or returns an
409 * error which gives the caller a chance to respond accordingly. In fact it
410 * either succeeds or if it generates a pagefault, general protection or other
411 * exception, the trap handler has to redirect the execution to
412 * __user_copy_msg_pointer_failure where the error is reported to the caller
413 * without resolving the pagefault. It is not kernel's problem to deal with
414 * wrong pointers from userspace and the caller should return an error to
415 * userspace as if wrong values or request were passed to the kernel
419 .globl _copy_msg_from_user
424 movw DSREG(%eax), %gs
426 /* load the source pointer */
428 /* load the destination pointer */
431 mov %gs:0*4(%ecx), %eax
433 mov %gs:1*4(%ecx), %eax
435 mov %gs:2*4(%ecx), %eax
437 mov %gs:3*4(%ecx), %eax
439 mov %gs:4*4(%ecx), %eax
441 mov %gs:5*4(%ecx), %eax
443 mov %gs:6*4(%ecx), %eax
445 mov %gs:7*4(%ecx), %eax
447 mov %gs:8*4(%ecx), %eax
450 .globl ___copy_msg_from_user_end
451 ___copy_msg_from_user_end:
458 /*===========================================================================*/
459 /* copy_msg_to_user */
460 /*===========================================================================*/
462 * void copy_msg_to_user(struct proc * p, message * src, message * user_mbuf);
464 * Copies a message of 36 bytes to user process space from a kernel buffer. This
465 * function assumes that the process address space is installed (cr3 loaded) and
466 * the local descriptor table of this process is loaded too.
468 * All the other copy_msg_from_user() comments apply here as well!
472 .globl _copy_msg_to_user
477 movw DSREG(%eax), %gs
479 /* load the source pointer */
481 /* load the destination pointer */
485 mov %eax, %gs:0*4(%edx)
487 mov %eax, %gs:1*4(%edx)
489 mov %eax, %gs:2*4(%edx)
491 mov %eax, %gs:3*4(%edx)
493 mov %eax, %gs:4*4(%edx)
495 mov %eax, %gs:5*4(%edx)
497 mov %eax, %gs:6*4(%edx)
499 mov %eax, %gs:7*4(%edx)
501 mov %eax, %gs:8*4(%edx)
503 .globl ___copy_msg_to_user_end
504 ___copy_msg_to_user_end:
512 * if a function from a selected set of copies from or to userspace fails, it is
513 * because of a wrong pointer supplied by the userspace. We have to clean up and
514 * and return -1 to indicated that something wrong has happend. The place it was
515 * called from has to handle this situation. The exception handler redirect us
516 * here to continue, clean up and report the error
519 .globl ___user_copy_msg_pointer_failure
520 ___user_copy_msg_pointer_failure:
526 /*===========================================================================*/
528 /*===========================================================================*/
530 * PUBLIC void phys_memset(phys_bytes source, unsigned long pattern,
531 * phys_bytes bytecount);
532 * Fill a block of physical memory with pattern.
545 mov $FLAT_DS_SELECTOR, %ebx
554 /* Any remaining bytes? */
574 /*===========================================================================*/
576 /*===========================================================================*/
578 * PUBLIC u16_t mem_rdw(U16_t segment, u16_t *offset);
579 * Load and return word at far pointer segment:offset.
586 mov 4+4(%esp), %eax /* offset */
587 movzwl (%eax), %eax /* word to return */
592 /*===========================================================================*/
594 /*===========================================================================*/
596 * PUBLIC void reset();
597 * Reset the system by loading IDT with offset 0 and interrupting.
602 int $3 /* anything goes, the 386 will not like it */
609 /*===========================================================================*/
611 /*===========================================================================*/
613 * PUBLIC void halt_cpu(void);
614 * reanables interrupts and puts the cpu in the halts state. Once an interrupt
615 * is handled the execution resumes by disabling interrupts and continues
619 hlt /* interrupts enabled only after this instruction is executed! */
621 * interrupt handlers make sure that the interrupts are disabled when we
622 * get here so we take only _one_ interrupt after halting the CPU
626 /*===========================================================================*/
628 /*===========================================================================*/
630 * PUBLIC unsigned long read_cpu_flags(void);
631 * Read CPU status flags from C.
656 /*===========================================================================*/
658 /*===========================================================================*/
670 /* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
678 /* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
683 /*===========================================================================*/
685 /*===========================================================================*/
688 fxsave (%eax) /* Do not change the operand! (gas2ack) */
691 /*===========================================================================*/
693 /*===========================================================================*/
696 fnsave (%eax) /* Do not change the operand! (gas2ack) */
697 fwait /* required for compatibility with processors prior pentium */
700 /*===========================================================================*/
702 /*===========================================================================*/
705 fxrstor (%eax) /* Do not change the operand! (gas2ack) */
708 /*===========================================================================*/
710 /*===========================================================================*/
713 frstor (%eax) /* Do not change the operand! (gas2ack) */
717 /*===========================================================================*/
719 /*===========================================================================*/
720 /* PUBLIC unsigned long read_cr0(void); */
728 /*===========================================================================*/
730 /*===========================================================================*/
731 /* PUBLIC void write_cr0(unsigned long value); */
737 jmp 0f /* A jump is required for some flags */
742 /*===========================================================================*/
744 /*===========================================================================*/
745 /* PUBLIC reg_t read_cr2(void); */
750 /*===========================================================================*/
752 /*===========================================================================*/
753 /* PUBLIC unsigned long read_cr3(void); */
758 /* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
763 /*===========================================================================*/
765 /*===========================================================================*/
766 /* PUBLIC unsigned long read_cr4(void); */
771 /* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
776 /*===========================================================================*/
778 /*===========================================================================*/
779 /* PUBLIC void write_cr4(unsigned long value); */
785 /* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
791 /*===========================================================================*/
793 /*===========================================================================*/
794 /* PUBLIC unsigned long getcr3val(void); */
800 * Read the Model Specific Register (MSR) of IA32 architecture
802 * void ia32_msr_read(u32_t reg, u32_t * hi, u32_t * lo)
804 .globl _ia32_msr_read
820 * Write the Model Specific Register (MSR) of IA32 architecture
822 * void ia32_msr_write(u32_t reg, u32_t hi, u32_t lo)
824 .globl _ia32_msr_write
837 /*===========================================================================*/
839 /*===========================================================================*/
840 /* PUBLIC void idt_reload (void); */
843 lidt _gdt+IDT_SELECTOR /* reload interrupt descriptor table */
847 * void reload_segment_regs(void)
850 #define RELOAD_SEG_REG(reg) \
859 /*===========================================================================*/
860 /* switch_address_space */
861 /*===========================================================================*/
862 /* PUBLIC void switch_address_space(struct proc *p)
864 * sets the %cr3 register to the supplied value if it is not already set to the
865 * same value in which case it would only result in an extra TLB flush which is
869 .globl _switch_address_space
870 _switch_address_space:
872 /* read the process pointer */
874 /* enable process' segment descriptors */
876 /* get the new cr3 value */
877 movl P_CR3(%edx), %eax
878 /* test if the new cr3 != NULL */
883 * test if the cr3 is loaded with the current value to avoid unnecessary