798 lines
20 KiB
ArmAsm
798 lines
20 KiB
ArmAsm
/* sections */
|
|
|
|
|
|
#include <minix/config.h>
|
|
#include <minix/const.h>
|
|
#include <machine/asm.h>
|
|
#include <machine/interrupt.h>
|
|
#include <machine/vm.h>
|
|
#include "archconst.h"
|
|
#include "kernel/const.h"
|
|
#include "sconst.h"
|
|
#include <machine/multiboot.h>
|
|
|
|
|
|
/* Easy way to make functions */
|
|
|
|
/* Make a function of the form func(arg) */
|
|
|
|
#define STACKARG 8(%ebp)
|
|
|
|
#define ARG_EAX_ACTION(FUNCTION, ACTION) ;\
|
|
ENTRY(FUNCTION) ;\
|
|
push %ebp ;\
|
|
mov %esp, %ebp ;\
|
|
mov STACKARG, %eax ;\
|
|
ACTION ;\
|
|
pop %ebp ;\
|
|
ret
|
|
|
|
/* Make a function of the form ret = func() */
|
|
#define ARG_EAX_RETURN(FUNCTION, EXPR) ;\
|
|
ENTRY(FUNCTION) ;\
|
|
push %ebp ;\
|
|
mov %esp, %ebp ;\
|
|
mov EXPR, %eax ;\
|
|
pop %ebp ;\
|
|
ret
|
|
|
|
/* Make a function of the form ret = func() */
|
|
#define ARG_EAX_SET(FUNCTION, DEST) ;\
|
|
ENTRY(FUNCTION) ;\
|
|
push %ebp ;\
|
|
mov %esp, %ebp ;\
|
|
mov STACKARG, %eax ;\
|
|
mov %eax, DEST ;\
|
|
jmp 0f /* a jump is required for some sets */ ;\
|
|
0: pop %ebp ;\
|
|
ret
|
|
|
|
/* Make a function of the form ret = func() */
|
|
#define ARG_AX_SET(FUNCTION, DEST) ;\
|
|
ENTRY(FUNCTION) ;\
|
|
push %ebp ;\
|
|
mov %esp, %ebp ;\
|
|
mov STACKARG, %eax ;\
|
|
mov %ax, DEST ;\
|
|
jmp 0f /* a jump is required for some sets */ ;\
|
|
0: pop %ebp ;\
|
|
ret
|
|
|
|
/*
|
|
* This file contains a number of assembly code utility routines needed by the
|
|
* kernel.
|
|
*/
|
|
|
|
ENTRY(__main)
|
|
ret
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* phys_insw */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC void phys_insw(Port_t port, phys_bytes buf, size_t count);
|
|
* Input an array from an I/O port. Absolute address version of insw().
|
|
*/
|
|
/* transfer data from (disk controller) port to memory */
|
|
ENTRY(phys_insw)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
cld
|
|
push %edi
|
|
|
|
mov 8(%ebp), %edx /* port to read from */
|
|
mov 12(%ebp), %edi /* destination addr */
|
|
mov 16(%ebp), %ecx /* byte count */
|
|
shr $1, %ecx /* word count */
|
|
rep insw /* input many words */
|
|
pop %edi
|
|
pop %ebp
|
|
ret
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* phys_insb */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC void phys_insb(Port_t port, phys_bytes buf, size_t count);
|
|
* Input an array from an I/O port. Absolute address version of insb().
|
|
*/
|
|
/* transfer data from (disk controller) port to memory byte by byte */
|
|
ENTRY(phys_insb)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
cld
|
|
push %edi
|
|
|
|
mov 8(%ebp), %edx /* port to read from */
|
|
mov 12(%ebp), %edi /* destination addr */
|
|
mov 16(%ebp), %ecx /* byte count */
|
|
rep insb /* input many bytes */
|
|
pop %edi
|
|
pop %ebp
|
|
ret
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* phys_outsw */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC void phys_outsw(Port_t port, phys_bytes buf, size_t count);
|
|
* Output an array to an I/O port. Absolute address version of outsw().
|
|
*/
|
|
/* transfer data from memory to (disk controller) port */
|
|
ENTRY(phys_outsw)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
cld
|
|
push %esi
|
|
|
|
mov 8(%ebp), %edx /* port to write to */
|
|
mov 12(%ebp), %esi /* source addr */
|
|
mov 16(%ebp), %ecx /* byte count */
|
|
shr $1, %ecx /* word count */
|
|
rep outsw /* output many words */
|
|
pop %esi
|
|
pop %ebp
|
|
ret
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* phys_outsb */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC void phys_outsb(Port_t port, phys_bytes buf, size_t count);
|
|
* Output an array to an I/O port. Absolute address version of outsb().
|
|
*/
|
|
/* transfer data from memory to (disk controller) port byte by byte */
|
|
ENTRY(phys_outsb)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
cld
|
|
push %esi
|
|
|
|
mov 8(%ebp), %edx /* port to write to */
|
|
mov 12(%ebp), %esi /* source addr */
|
|
mov 16(%ebp), %ecx /* byte count */
|
|
rep outsb /* output many bytes */
|
|
pop %esi
|
|
pop %ebp
|
|
ret
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* phys_copy */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC phys_bytes phys_copy(phys_bytes source, phys_bytes destination,
|
|
* phys_bytes bytecount);
|
|
* Copy a block of data from anywhere to anywhere in physical memory.
|
|
*/
|
|
/* es edi esi eip src dst len */
|
|
ENTRY(phys_copy)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
|
|
cld
|
|
push %esi
|
|
push %edi
|
|
|
|
mov 8(%ebp), %esi
|
|
mov 12(%ebp), %edi
|
|
mov 16(%ebp), %eax
|
|
|
|
cmp $10, %eax /* avoid align overhead for small counts */
|
|
jb pc_small
|
|
mov %esi, %ecx /* align source, hope target is too */
|
|
neg %ecx
|
|
and $3, %ecx /* count for alignment */
|
|
sub %ecx, %eax
|
|
|
|
rep movsb (%esi), (%edi)
|
|
mov %eax, %ecx
|
|
shr $2, %ecx /* count of dwords */
|
|
|
|
rep movsl (%esi), (%edi)
|
|
and $3, %eax
|
|
pc_small:
|
|
xchg %eax, %ecx /* remainder */
|
|
|
|
rep movsb (%esi), (%edi)
|
|
|
|
mov $0, %eax /* 0 means: no fault */
|
|
LABEL(phys_copy_fault) /* kernel can send us here */
|
|
pop %edi
|
|
pop %esi
|
|
pop %ebp
|
|
ret
|
|
|
|
LABEL(phys_copy_fault_in_kernel) /* kernel can send us here */
|
|
pop %edi
|
|
pop %esi
|
|
pop %ebp
|
|
mov %cr2, %eax
|
|
ret
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* copy_msg_from_user */
|
|
/*===========================================================================*/
|
|
/*
|
|
* int copy_msg_from_user(message * user_mbuf, message * dst);
|
|
*
|
|
* Copies a message of 36 bytes from user process space to a kernel buffer. This
|
|
* function assumes that the process address space is installed (cr3 loaded).
|
|
*
|
|
* This function from the callers point of view either succeeds or returns an
|
|
* error which gives the caller a chance to respond accordingly. In fact it
|
|
* either succeeds or if it generates a pagefault, general protection or other
|
|
* exception, the trap handler has to redirect the execution to
|
|
* __user_copy_msg_pointer_failure where the error is reported to the caller
|
|
* without resolving the pagefault. It is not kernel's problem to deal with
|
|
* wrong pointers from userspace and the caller should return an error to
|
|
* userspace as if wrong values or request were passed to the kernel
|
|
*/
|
|
ENTRY(copy_msg_from_user)
|
|
/* load the source pointer */
|
|
mov 4(%esp), %ecx
|
|
/* load the destination pointer */
|
|
mov 8(%esp), %edx
|
|
|
|
/* mov 0*4(%ecx), %eax
|
|
mov %eax, 0*4(%edx) */
|
|
mov 1*4(%ecx), %eax
|
|
mov %eax, 1*4(%edx)
|
|
mov 2*4(%ecx), %eax
|
|
mov %eax, 2*4(%edx)
|
|
mov 3*4(%ecx), %eax
|
|
mov %eax, 3*4(%edx)
|
|
mov 4*4(%ecx), %eax
|
|
mov %eax, 4*4(%edx)
|
|
mov 5*4(%ecx), %eax
|
|
mov %eax, 5*4(%edx)
|
|
mov 6*4(%ecx), %eax
|
|
mov %eax, 6*4(%edx)
|
|
mov 7*4(%ecx), %eax
|
|
mov %eax, 7*4(%edx)
|
|
mov 8*4(%ecx), %eax
|
|
mov %eax, 8*4(%edx)
|
|
|
|
LABEL(__copy_msg_from_user_end)
|
|
movl $0, %eax
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* copy_msg_to_user */
|
|
/*===========================================================================*/
|
|
/*
|
|
* void copy_msg_to_user(message * src, message * user_mbuf);
|
|
*
|
|
* Copies a message of 36 bytes to user process space from a kernel buffer.
|
|
*
|
|
* All the other copy_msg_from_user() comments apply here as well!
|
|
*/
|
|
ENTRY(copy_msg_to_user)
|
|
/* load the source pointer */
|
|
mov 4(%esp), %ecx
|
|
/* load the destination pointer */
|
|
mov 8(%esp), %edx
|
|
|
|
mov 0*4(%ecx), %eax
|
|
mov %eax, 0*4(%edx)
|
|
mov 1*4(%ecx), %eax
|
|
mov %eax, 1*4(%edx)
|
|
mov 2*4(%ecx), %eax
|
|
mov %eax, 2*4(%edx)
|
|
mov 3*4(%ecx), %eax
|
|
mov %eax, 3*4(%edx)
|
|
mov 4*4(%ecx), %eax
|
|
mov %eax, 4*4(%edx)
|
|
mov 5*4(%ecx), %eax
|
|
mov %eax, 5*4(%edx)
|
|
mov 6*4(%ecx), %eax
|
|
mov %eax, 6*4(%edx)
|
|
mov 7*4(%ecx), %eax
|
|
mov %eax, 7*4(%edx)
|
|
mov 8*4(%ecx), %eax
|
|
mov %eax, 8*4(%edx)
|
|
|
|
LABEL(__copy_msg_to_user_end)
|
|
movl $0, %eax
|
|
ret
|
|
|
|
/*
|
|
* if a function from a selected set of copies from or to userspace fails, it is
|
|
* because of a wrong pointer supplied by the userspace. We have to clean up and
|
|
* and return -1 to indicated that something wrong has happend. The place it was
|
|
* called from has to handle this situation. The exception handler redirect us
|
|
* here to continue, clean up and report the error
|
|
*/
|
|
ENTRY(__user_copy_msg_pointer_failure)
|
|
movl $-1, %eax
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* phys_memset */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC void phys_memset(phys_bytes dst, unsigned long pattern,
|
|
* phys_bytes bytecount);
|
|
* Fill a block of physical memory with pattern.
|
|
*/
|
|
ENTRY(phys_memset)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
push %edi
|
|
cld
|
|
|
|
mov 8(%ebp), %edi
|
|
mov 16(%ebp), %ecx
|
|
mov 12(%ebp), %eax
|
|
shr $2, %ecx
|
|
rep stosl
|
|
|
|
/* Any remaining bytes? */
|
|
mov 16(%ebp), %ecx
|
|
and $3, %ecx
|
|
rep stosb
|
|
|
|
LABEL(memset_fault) /* kernel can send us here */
|
|
mov $0, %eax /* 0 means: no fault */
|
|
pop %edi
|
|
pop %ebp
|
|
ret
|
|
|
|
LABEL(memset_fault_in_kernel) /* kernel can send us here */
|
|
pop %edi
|
|
pop %ebp
|
|
mov %cr2, %eax
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* x86_triplefault */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC void x86_triplefault();
|
|
* Reset the system by loading IDT with offset 0 and interrupting.
|
|
*/
|
|
ENTRY(x86_triplefault)
|
|
lidt idt_zero
|
|
int $3 /* anything goes, the 386 will not like it */
|
|
.data
|
|
idt_zero:
|
|
.long 0, 0
|
|
.text
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* halt_cpu */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC void halt_cpu(void);
|
|
* reanables interrupts and puts the cpu in the halts state. Once an interrupt
|
|
* is handled the execution resumes by disabling interrupts and continues
|
|
*/
|
|
ENTRY(halt_cpu)
|
|
sti
|
|
hlt /* interrupts enabled only after this instruction is executed! */
|
|
/*
|
|
* interrupt handlers make sure that the interrupts are disabled when we
|
|
* get here so we take only _one_ interrupt after halting the CPU
|
|
*/
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* poweroff_vmware_clihlt */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC void poweroff_vmware_clihlt(void);
|
|
* VMware detects this peculiar sequence and forces the virtual machine off
|
|
* when the parameter gui.exitOnCLIHLT is set to TRUE.
|
|
* Otherwise this sequence just hangs the CPU, requiring a power down action.
|
|
*/
|
|
ENTRY(poweroff_vmware_clihlt)
|
|
#ifndef NO_VMWARE_DETECTION
|
|
mov $1, %eax
|
|
cpuid
|
|
test $[1<<31], %ecx /* "virtualized" */
|
|
jz 1f /* always 0 on real hardware */
|
|
mov $0x40000000, %eax /* select hypervisor-use leaf */
|
|
cpuid
|
|
cmp $0x61774D56, %ebx /* ASCII "VMwa" */
|
|
jne 1f
|
|
cmp $0x4D566572, %ecx /* ASCII "reVM" */
|
|
jne 1f
|
|
cmp $0x65726177, %edx /* ASCII "ware" */
|
|
jne 1f
|
|
/* we are virtualized by some VMware product! */
|
|
#endif
|
|
cli
|
|
hlt
|
|
1: ret
|
|
|
|
/*===========================================================================*/
|
|
/* read_flags */
|
|
/*===========================================================================*/
|
|
/*
|
|
* PUBLIC unsigned long read_cpu_flags(void);
|
|
* Read CPU status flags from C.
|
|
*/
|
|
ENTRY(read_cpu_flags)
|
|
pushf
|
|
mov (%esp), %eax
|
|
add $4, %esp
|
|
ret
|
|
|
|
ENTRY(read_ds)
|
|
mov $0, %eax
|
|
mov %ds, %ax
|
|
ret
|
|
|
|
ENTRY(read_cs)
|
|
mov $0, %eax
|
|
mov %cs, %ax
|
|
ret
|
|
|
|
ENTRY(read_ss)
|
|
mov $0, %eax
|
|
mov %ss, %ax
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* fpu_routines */
|
|
/*===========================================================================*/
|
|
|
|
/* non-waiting FPU initialization */
|
|
ENTRY(fninit)
|
|
fninit
|
|
ret
|
|
|
|
ENTRY(clts)
|
|
clts
|
|
ret
|
|
|
|
/* store status word (non-waiting) */
|
|
ENTRY(fnstsw)
|
|
xor %eax, %eax
|
|
|
|
/* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
|
|
fnstsw %ax
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* fxrstor */
|
|
/*===========================================================================*/
|
|
ENTRY(fxrstor)
|
|
mov 4(%esp), %eax
|
|
fxrstor (%eax)
|
|
ENTRY(__fxrstor_end)
|
|
xor %eax, %eax
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* frstor */
|
|
/*===========================================================================*/
|
|
ENTRY(frstor)
|
|
mov 4(%esp), %eax
|
|
frstor (%eax)
|
|
ENTRY(__frstor_end)
|
|
xor %eax, %eax
|
|
ret
|
|
|
|
/* Shared exception handler for both fxrstor and frstor. */
|
|
ENTRY(__frstor_failure)
|
|
mov $1, %eax
|
|
ret
|
|
|
|
/* Read/write control registers */
|
|
ARG_EAX_RETURN(read_cr0, %cr0);
|
|
ARG_EAX_RETURN(read_cr2, %cr2);
|
|
ARG_EAX_RETURN(read_cr3, %cr3);
|
|
ARG_EAX_RETURN(read_cr4, %cr4);
|
|
ARG_EAX_SET(write_cr4, %cr4);
|
|
ARG_EAX_SET(write_cr0, %cr0);
|
|
ARG_EAX_SET(write_cr3, %cr3);
|
|
|
|
/* Read/write various descriptor tables */
|
|
ARG_EAX_ACTION(x86_ltr, ltr STACKARG );
|
|
ARG_EAX_ACTION(x86_lidt, lidtl (%eax));
|
|
ARG_EAX_ACTION(x86_lgdt, lgdt (%eax));
|
|
ARG_EAX_ACTION(x86_lldt, lldt STACKARG);
|
|
ARG_EAX_ACTION(x86_sgdt, sgdt (%eax));
|
|
ARG_EAX_ACTION(x86_sidt, sidt (%eax));
|
|
|
|
/* Load segments */
|
|
ARG_AX_SET(x86_load_ds, %ds)
|
|
ARG_AX_SET(x86_load_es, %es)
|
|
ARG_AX_SET(x86_load_fs, %fs)
|
|
ARG_AX_SET(x86_load_gs, %gs)
|
|
ARG_AX_SET(x86_load_ss, %ss)
|
|
|
|
/* FPU */
|
|
ARG_EAX_ACTION(fnsave, fnsave (%eax) ; fwait);
|
|
ARG_EAX_ACTION(fxsave, fxsave (%eax));
|
|
ARG_EAX_ACTION(fnstcw, fnstcw (%eax));
|
|
|
|
/* invlpg */
|
|
ARG_EAX_ACTION(i386_invlpg, invlpg (%eax));
|
|
|
|
ENTRY(x86_load_kerncs)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
mov 8(%ebp), %eax
|
|
jmp $KERN_CS_SELECTOR, $newcs
|
|
newcs:
|
|
pop %ebp
|
|
ret
|
|
|
|
/*
|
|
* Read the Model Specific Register (MSR) of IA32 architecture
|
|
*
|
|
* void ia32_msr_read(u32_t reg, u32_t * hi, u32_t * lo)
|
|
*/
|
|
ENTRY(ia32_msr_read)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
|
|
mov 8(%ebp), %ecx
|
|
rdmsr
|
|
mov 12(%ebp), %ecx
|
|
mov %edx, (%ecx)
|
|
mov 16(%ebp), %ecx
|
|
mov %eax, (%ecx)
|
|
|
|
pop %ebp
|
|
ret
|
|
|
|
/*
|
|
* Write the Model Specific Register (MSR) of IA32 architecture
|
|
*
|
|
* void ia32_msr_write(u32_t reg, u32_t hi, u32_t lo)
|
|
*/
|
|
ENTRY(ia32_msr_write)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
|
|
mov 12(%ebp), %edx
|
|
mov 16(%ebp), %eax
|
|
mov 8(%ebp), %ecx
|
|
wrmsr
|
|
|
|
pop %ebp
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* __switch_address_space */
|
|
/*===========================================================================*/
|
|
/* PUBLIC void __switch_address_space(struct proc *p, struct ** ptproc)
|
|
*
|
|
* sets the %cr3 register to the supplied value if it is not already set to the
|
|
* same value in which case it would only result in an extra TLB flush which is
|
|
* not desirable
|
|
*/
|
|
ENTRY(__switch_address_space)
|
|
/* read the process pointer */
|
|
mov 4(%esp), %edx
|
|
/* get the new cr3 value */
|
|
movl P_CR3(%edx), %eax
|
|
/* test if the new cr3 != NULL */
|
|
cmpl $0, %eax
|
|
je 0f
|
|
|
|
/*
|
|
* test if the cr3 is loaded with the current value to avoid unnecessary
|
|
* TLB flushes
|
|
*/
|
|
mov %cr3, %ecx
|
|
cmp %ecx, %eax
|
|
je 0f
|
|
mov %eax, %cr3
|
|
/* get ptproc */
|
|
mov 8(%esp), %eax
|
|
mov %edx, (%eax)
|
|
0:
|
|
ret
|
|
|
|
/* acknowledge just the master PIC */
|
|
ENTRY(eoi_8259_master)
|
|
movb $END_OF_INT, %al
|
|
outb $INT_CTL
|
|
ret
|
|
|
|
/* we have to acknowledge both PICs */
|
|
ENTRY(eoi_8259_slave)
|
|
movb $END_OF_INT, %al
|
|
outb $INT_CTL
|
|
outb $INT2_CTL
|
|
ret
|
|
|
|
/* in some cases we need to force TLB update, reloading cr3 does the trick */
|
|
ENTRY(refresh_tlb)
|
|
mov %cr3, %eax
|
|
mov %eax, %cr3
|
|
ret
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
/*===========================================================================*/
|
|
/* smp_get_htt */
|
|
/*===========================================================================*/
|
|
/* PUBLIC int smp_get_htt(void); */
|
|
/* return true if the processor is hyper-threaded. */
|
|
ENTRY(smp_get_htt)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
pushf
|
|
pop %eax
|
|
mov %eax, %ebx
|
|
and $0x200000, %eax
|
|
je 0f
|
|
mov $0x1, %eax
|
|
/* FIXME don't use the byte code */
|
|
.byte 0x0f, 0xa2 /* opcode for cpuid */
|
|
mov %edx, %eax
|
|
pop %ebp
|
|
ret
|
|
0:
|
|
xor %eax, %eax
|
|
pop %ebp
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* smp_get_num_htt */
|
|
/*===========================================================================*/
|
|
/* PUBLIC int smp_get_num_htt(void); */
|
|
/* Get the number of hyper-threaded processor cores */
|
|
ENTRY(smp_get_num_htt)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
pushf
|
|
pop %eax
|
|
mov %eax, %ebx
|
|
and $0x200000, %eax
|
|
je 0f
|
|
mov $0x1, %eax
|
|
/* FIXME don't use the byte code */
|
|
.byte 0x0f, 0xa2 /* opcode for cpuid */
|
|
mov %ebx, %eax
|
|
pop %ebp
|
|
ret
|
|
0:
|
|
xor %eax, %eax
|
|
pop %ebp
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* smp_get_cores */
|
|
/*===========================================================================*/
|
|
/* PUBLIC int smp_get_cores(void); */
|
|
/* Get the number of cores. */
|
|
ENTRY(smp_get_cores)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
pushf
|
|
pop %eax
|
|
mov %eax, %ebx
|
|
and $0x200000, %eax
|
|
je 0f
|
|
push %ecx
|
|
xor %ecx, %ecx
|
|
mov $0x4, %eax
|
|
/* FIXME don't use the byte code */
|
|
.byte 0x0f, 0xa2 /* opcode for cpuid */
|
|
pop %ebp
|
|
ret
|
|
0:
|
|
xor %eax, %eax
|
|
pop %ebp
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* arch_spinlock_lock */
|
|
/*===========================================================================*/
|
|
/* void arch_spinlock_lock (u32_t *lock_data)
|
|
* {
|
|
* while (test_and_set(lock_data) == 1)
|
|
* while (*lock_data == 1)
|
|
* ;
|
|
* }
|
|
* eax register is clobbered.
|
|
*/
|
|
ENTRY(arch_spinlock_lock)
|
|
mov 4(%esp), %eax
|
|
mov $1, %edx
|
|
2:
|
|
mov $1, %ecx
|
|
xchg %ecx, (%eax)
|
|
test %ecx, %ecx
|
|
je 0f
|
|
|
|
cmp $(1<< 16), %edx
|
|
je 1f
|
|
shl %edx
|
|
1:
|
|
mov %edx, %ecx
|
|
3:
|
|
pause
|
|
sub $1, %ecx
|
|
test %ecx, %ecx
|
|
jz 2b
|
|
jmp 3b
|
|
0:
|
|
mfence
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* arch_spinlock_unlock */
|
|
/*===========================================================================*/
|
|
/* * void arch_spinlock_unlock (unsigned int *lockp) */
|
|
/* spin lock release routine. */
|
|
ENTRY(arch_spinlock_unlock)
|
|
mov 4(%esp), %eax
|
|
mov $0, %ecx
|
|
xchg %ecx, (%eax)
|
|
mfence
|
|
ret
|
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
/*===========================================================================*/
|
|
/* mfence */
|
|
/*===========================================================================*/
|
|
/* PUBLIC void mfence (void); */
|
|
/* architecture specific memory barrier routine. */
|
|
ENTRY(mfence)
|
|
mfence
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* arch_pause */
|
|
/*===========================================================================*/
|
|
/* PUBLIC void arch_pause (void); */
|
|
/* architecture specific pause routine. */
|
|
ENTRY(arch_pause)
|
|
pause
|
|
ret
|
|
|
|
/*===========================================================================*/
|
|
/* read_ebp */
|
|
/*===========================================================================*/
|
|
/* PUBLIC u16_t cpuid(void) */
|
|
ENTRY(read_ebp)
|
|
mov %ebp, %eax
|
|
ret
|
|
|
|
ENTRY(interrupts_enable)
|
|
sti
|
|
ret
|
|
|
|
ENTRY(interrupts_disable)
|
|
cli
|
|
ret
|
|
|
|
|
|
/*
|
|
* void switch_k_stack(void * esp, void (* continuation)(void));
|
|
*
|
|
* sets the current stack pointer to the given value and continues execution at
|
|
* the given address
|
|
*/
|
|
ENTRY(switch_k_stack)
|
|
/* get the arguments from the stack */
|
|
mov 8(%esp), %eax
|
|
mov 4(%esp), %ecx
|
|
mov $0, %ebp /* reset %ebp for stack trace */
|
|
mov %ecx, %esp /* set the new stack */
|
|
jmp *%eax /* and jump to the continuation */
|
|
|
|
/* NOT_REACHABLE */
|
|
0: jmp 0b
|
|
|
|
.data
|
|
idt_ptr:
|
|
.short 0x3ff
|
|
.long 0x0
|
|
|
|
ldtsel:
|
|
.long LDT_SELECTOR
|