689 lines
9.4 KiB
ArmAsm
689 lines
9.4 KiB
ArmAsm
/* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
|
|
* See /doc/license/gpl-2.0.txt for details about the licensing.
|
|
*/
|
|
|
|
#include "amd64.h"
|
|
|
|
.code64
|
|
|
|
/*
|
|
* Port I/O.
|
|
*/
|
|
.global inb
|
|
inb:
|
|
movl %edi, %edx /* movl port+0(FP), DX */
|
|
XORL %eax, %eax
|
|
INB %dx
|
|
ret
|
|
|
|
.global insb
|
|
insb:
|
|
movl %edx, %ecx
|
|
movl %edi, %edx
|
|
movq %rsi, %rdi
|
|
cld
|
|
REP; INSB
|
|
ret
|
|
|
|
.global ins
|
|
ins:
|
|
movl %edi, %edx /* movl port+0(FP), DX */
|
|
XORL %eax, %eax
|
|
INW %dx
|
|
ret
|
|
|
|
/*
|
|
* void inss(int32_t port, void* buf, int32_t size);
|
|
*/
|
|
.global inss
|
|
inss:
|
|
movl %edx, %ecx
|
|
movl %edi, %edx
|
|
movq %rsi, %rdi
|
|
cld
|
|
REP; INSW
|
|
ret
|
|
|
|
.global inl
|
|
inl:
|
|
movl %edi, %edx /* movl port+0(FP), DX */
|
|
INL %dx
|
|
ret
|
|
|
|
.global insl
|
|
insl:
|
|
movl %edx, %ecx
|
|
movl %edi, %edx
|
|
movq %rsi, %rdi
|
|
cld
|
|
REP; INSL
|
|
ret
|
|
|
|
.global outb
|
|
outb:
|
|
movl %edi, %edx /* movl port+0(FP), DX */
|
|
movl %esi, %eax
|
|
OUTB %dx
|
|
ret
|
|
|
|
.global outsb
|
|
outsb:
|
|
movl %edx, %ecx
|
|
movl %edi, %edx
|
|
movq %rsi, %rdi
|
|
cld
|
|
REP; OUTSB
|
|
ret
|
|
|
|
.global outs
|
|
outs:
|
|
movl %edi, %edx /* movl port+0(FP), DX */
|
|
movl %esi, %eax
|
|
OUTW %dx
|
|
ret
|
|
|
|
.global outss
|
|
outss:
|
|
movl %edx, %ecx
|
|
movl %edi, %edx
|
|
cld
|
|
REP; OUTSW
|
|
ret
|
|
|
|
.global outl
|
|
outl:
|
|
movl %edi, %edx /* movl port+0(FP), DX */
|
|
movl %esi, %eax
|
|
OUTL %dx
|
|
ret
|
|
|
|
.global outsl
|
|
outsl:
|
|
movl %edx, %ecx
|
|
movl %edi, %edx
|
|
movq %rsi, %rdi
|
|
cld
|
|
REP; OUTSL
|
|
ret
|
|
|
|
/*
|
|
* Load/store segment descriptor tables:
|
|
* GDT - global descriptor table
|
|
* IDT - interrupt descriptor table
|
|
* TR - task register
|
|
* GDTR and LDTR take an m16:m64 argument,
|
|
* so shuffle the stack arguments to
|
|
* get it in the right format.
|
|
*/
|
|
.global gdtget
|
|
gdtget:
|
|
sgdt (%rdi) /* Note: 10 bytes returned */
|
|
ret
|
|
|
|
.global lgdt
|
|
lgdt:
|
|
lgdt (%rdi)
|
|
ret
|
|
|
|
.global lidt
|
|
lidt:
|
|
lgdt (%rdi)
|
|
ret
|
|
|
|
// Called with the address of gdt in rdi.
|
|
// Load the gdt, then do a ret which will use the argument on the stack as
|
|
// a segment #. This stuff is just crazy.
|
|
// We have to push %rsi, then 16 bits(really!) of %rdi.
|
|
.global gdtput
|
|
gdtput:
|
|
pushq %rsi
|
|
movq %rdi, %rax
|
|
pushw %ax
|
|
mov %rsp, %rax
|
|
lgdt (%rax)
|
|
popw %ax
|
|
popq %rax
|
|
xorq %rax, %rax
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
movw %ax, %ss
|
|
popq %rax
|
|
pushq %rdx
|
|
pushq %rax
|
|
lretq
|
|
|
|
.global idtput
|
|
idtput:
|
|
// save %rdi, since we are going to modify it.
|
|
pushq %rdi
|
|
// Push the two quads onto the stack,
|
|
// which arranges them in memory.
|
|
pushq %rsi
|
|
shlq $48, %rdi
|
|
pushq %rdi
|
|
movq %rsp, %rax
|
|
addq $6, %rax
|
|
lidt (%rax)
|
|
popq %rdi
|
|
popq %rsi
|
|
popq %rdi
|
|
ret
|
|
|
|
.global trput
|
|
trput:
|
|
ltr %di
|
|
ret
|
|
|
|
/*
|
|
* Read/write various system registers.
|
|
*/
|
|
.global cr0get
|
|
cr0get:
|
|
movq %cr0, %rax
|
|
ret
|
|
|
|
.global cr0put
|
|
cr0put:
|
|
movq %rdi, %rax
|
|
movq %rax, %cr0
|
|
ret
|
|
|
|
.global cr2get
|
|
cr2get:
|
|
movq %cr2, %rax
|
|
ret
|
|
|
|
.global cr3get
|
|
cr3get:
|
|
movq %cr3, %rax
|
|
ret
|
|
|
|
.global cr3put
|
|
cr3put:
|
|
movq %rdi, %rax
|
|
movq %rax, %cr3
|
|
ret
|
|
|
|
.global cr4get
|
|
cr4get:
|
|
movq %CR4, %rax
|
|
ret
|
|
|
|
.global cr4put
|
|
cr4put:
|
|
movq %rdi, %rax
|
|
movq %rax, %CR4
|
|
ret
|
|
|
|
.global read_bp
|
|
read_bp:
|
|
movq %rbp, %rax
|
|
ret
|
|
|
|
.global rdtsc
|
|
rdtsc:
|
|
RDTSC
|
|
/* u64int rdtsc(void); */
|
|
xchgl %edx, %eax /* swap lo/hi, zero-extend */
|
|
SHLQ $32, %rax /* hi<<32 */
|
|
ORQ %rdx, %rax /* (hi<<32)|lo */
|
|
ret
|
|
|
|
/* int rdmsr(uint32_t reg, uint64_t* value); */
|
|
.global rdmsr
|
|
rdmsr:
|
|
pushq %rcx
|
|
pushq %rdx
|
|
pushq %rbp
|
|
xorq %rbp, %rbp
|
|
movl %edi, %ecx
|
|
.global _rdmsrinst
|
|
_rdmsrinst:
|
|
rdmsr
|
|
xchgl %edx, %eax /* swap lo/hi, zero-extend */
|
|
shlq $32, %rax /* hi<<32 */
|
|
orq %rdx, %rax /* (hi<<32)|lo */
|
|
movq %rax, (%rsi) /* set value */
|
|
movq %rbp, %rax /* %rbp set to -1 if traped */
|
|
popq %rbp
|
|
popq %rdx
|
|
popq %rcx
|
|
ret
|
|
|
|
/*int wrmsr(uint32_t reg, uint64_t value) */
|
|
.global wrmsr
|
|
wrmsr:
|
|
pushq %rcx
|
|
pushq %rdx
|
|
pushq %rbp
|
|
movl %edi, %ecx
|
|
movl %esi, %eax
|
|
movq %rsi, %rdx
|
|
shrq $32, %rdx
|
|
xorq %rbp, %rbp
|
|
.global _wrmsrinst
|
|
_wrmsrinst:
|
|
wrmsr
|
|
movq %rbp, %rax
|
|
popq %rbp
|
|
popq %rdx
|
|
popq %rcx
|
|
ret
|
|
|
|
.global invlpg
|
|
invlpg:
|
|
INVLPG (%rdi)
|
|
ret
|
|
|
|
.global wbinvd
|
|
wbinvd:
|
|
WBINVD
|
|
ret
|
|
|
|
/*
|
|
* BIOS32.
|
|
*/
|
|
.global bios32call
|
|
bios32call:
|
|
xorl %eax, %eax
|
|
incl %eax
|
|
ret
|
|
|
|
/*
|
|
* Serialisation.
|
|
*/
|
|
.global lfence
|
|
lfence:
|
|
lfence
|
|
ret
|
|
|
|
.global mfence
|
|
mfence:
|
|
mfence
|
|
ret
|
|
|
|
.global sfence
|
|
sfence:
|
|
sfence
|
|
ret
|
|
|
|
/*
|
|
* x86 convention is to use %rbp as the frame pointer,
|
|
* so we just return that register
|
|
*/
|
|
.global stackframe
|
|
stackframe:
|
|
movq %rbp, %rax
|
|
retq
|
|
|
|
/*
|
|
* disable interrupts,
|
|
* return old flags for splx()
|
|
*/
|
|
.global splhi
|
|
splhi:
|
|
_splhi:
|
|
pushfq
|
|
popq %rax
|
|
testq $If, %rax /* If - Interrupt Flag */
|
|
jz _alreadyhi
|
|
movq 0(%rsp), %rdi
|
|
movq %rdi, 8(%r15) /* callerpc to m->splpc */
|
|
_alreadyhi:
|
|
cli
|
|
ret
|
|
|
|
/*
|
|
* enable interrupts,
|
|
* return old flags for splx()
|
|
*/
|
|
.global spllo
|
|
spllo:
|
|
_spllo:
|
|
pushfq
|
|
popq %rax
|
|
testq $If, %rax /* If - Interrupt Flag */
|
|
jnz _alreadylo
|
|
movq $0, 8(%r15) /* clear m->splpc */
|
|
_alreadylo:
|
|
sti
|
|
ret
|
|
|
|
/*
|
|
* undo splhi or spllo,
|
|
* %rdi has flags before splhi or spllo
|
|
*/
|
|
.global splx
|
|
splx:
|
|
testq $If, %rdi /* If - Interrupt Flag */
|
|
jnz _spllo /* If set: enable */
|
|
jmp _splhi /* else: disable */
|
|
|
|
.global spldone
|
|
spldone:
|
|
ret
|
|
|
|
.global islo
|
|
islo:
|
|
pushfq
|
|
popq %rax
|
|
andq $If, %rax /* If - Interrupt Flag */
|
|
ret
|
|
|
|
.global infected_with_std
|
|
infected_with_std:
|
|
pushfq
|
|
popq %rax
|
|
andq $Df, %rax /* Df - Direction Flag */
|
|
ret
|
|
|
|
.global disinfect_std
|
|
disinfect_std:
|
|
cld
|
|
ret
|
|
|
|
/*
|
|
* Synchronisation
|
|
*/
|
|
.global ainc
|
|
ainc:
|
|
movl $1, %eax
|
|
lock; xaddl %eax, (%rdi)
|
|
addl $1, %eax
|
|
ret
|
|
|
|
.global adec
|
|
adec:
|
|
movl $-1, %eax
|
|
lock; xaddl %eax, (%rdi)
|
|
subl $1, %eax
|
|
ret
|
|
|
|
/*
|
|
* Synchronisation
|
|
*/
|
|
.global ainc16
|
|
ainc16:
|
|
mov $1, %ax
|
|
lock; xadd %ax, (%rdi)
|
|
add $1, %ax
|
|
ret
|
|
|
|
.global adec16
|
|
adec16:
|
|
mov $-1, %ax
|
|
lock; xadd %ax, (%rdi)
|
|
sub $1, %ax
|
|
ret
|
|
|
|
|
|
/*
|
|
* Semaphores rely on negative values for the counter,
|
|
* and don't have the same overflow/underflow conditions
|
|
* as ainc/adec.
|
|
*/
|
|
.global semainc
|
|
semainc:
|
|
movl $1, %eax
|
|
lock; xaddl %eax, (%rdi)
|
|
addl $1, %eax
|
|
ret
|
|
|
|
.global semadec
|
|
semadec:
|
|
movl $-1, %eax
|
|
lock; xaddl %eax, (%rdi)
|
|
subl $1, %eax
|
|
ret
|
|
|
|
.global tas32
|
|
tas32:
|
|
movl $0xdeaddead, %eax
|
|
xchgl %eax, (%rdi) /* */
|
|
ret
|
|
|
|
.global fas64
|
|
fas64:
|
|
movq %rdi, %rax
|
|
//lock; xchgq %eax, (%rdi) /* */
|
|
ret
|
|
|
|
// %rdi:&key, %sil:old, %dl:new
|
|
// int cas8(void* %rdi, uint8_t %sil, uint8_t %dl);
|
|
.global cas8
|
|
cas8:
|
|
movb %sil, %al
|
|
lock; cmpxchgb %dl, (%rdi)
|
|
movl $1, %eax
|
|
jnz _cas8r0
|
|
_cas8r1:
|
|
ret
|
|
_cas8r0:
|
|
decl %eax
|
|
ret
|
|
|
|
// %rdi:&key, %esi:old, %edx:new
|
|
// int cas16(void* %rdi, uint16_t %esi, uint16_t %edx);
|
|
.global cas16
|
|
cas16:
|
|
movw %si, %ax
|
|
lock; cmpxchgw %dx, (%rdi)
|
|
movl $1, %eax
|
|
jnz _cas16r0
|
|
_cas16r1:
|
|
ret
|
|
_cas16r0:
|
|
decl %eax
|
|
ret
|
|
|
|
// %rdi:&key, %esi:old, %edx:new
|
|
// int cas32(void* %rdi, uint32_t %esi, uint32_t %edx);
|
|
.global cas32
|
|
cas32:
|
|
movl %esi, %eax
|
|
lock; cmpxchgl %edx, (%rdi)
|
|
movl $1, %eax
|
|
jnz _cas32r0
|
|
_cas32r1:
|
|
ret
|
|
_cas32r0:
|
|
decl %eax
|
|
ret
|
|
|
|
// %rdi:&key, %esi:old, %edx:new
|
|
// int cas64(void* %rdi, uint64_t %rsi, uint64_t %rdx);
|
|
.global cas64
|
|
cas64:
|
|
movq %rsi, %rax
|
|
lock; cmpxchgq %rdx, (%rdi)
|
|
movl $1, %eax
|
|
jnz _cas64r0
|
|
_cas64r1:
|
|
ret
|
|
_cas64r0:
|
|
decl %eax
|
|
ret
|
|
|
|
|
|
.global xchg32
|
|
.global xchg32u
|
|
xchg32:
|
|
xchg32u:
|
|
movl %esi, %eax
|
|
lock; xchgl %eax, (%rdi)
|
|
ret
|
|
|
|
.global xchgm
|
|
xchgm:
|
|
movq %rsi, %rax
|
|
lock; xchgq %rax, (%rdi)
|
|
ret
|
|
|
|
/*
|
|
* Label consists of a stack pointer and a programme counter
|
|
* 0(%rdi) is the SP, 8(%rdi) is the PC
|
|
*/
|
|
.global gotolabel
|
|
gotolabel:
|
|
movq %rdi, %rax
|
|
movq 0(%rdi), %rsp
|
|
|
|
// Can't kill this quite yet.
|
|
movq (16+5*8)(%rdi), %rbp
|
|
|
|
movq 8(%rax), %rax /* put return PC on the stack */
|
|
/* NOTE: replaces previous caller? */
|
|
movq %rax, (%rsp)
|
|
movq $1, %rax /* return 1 */
|
|
ret
|
|
|
|
/* save all registers on this stack, the save stack
|
|
* in the label struct.
|
|
*/
|
|
.global slim_setlabel
|
|
slim_setlabel:
|
|
// %rax is trashable.
|
|
movq 0(%rsp), %rax /* store return PC */
|
|
movq %rax, 8(%rdi)
|
|
|
|
// Can't kill this quite yet.
|
|
movq %rbp, (16+5*8)(%rdi)
|
|
|
|
movq %rsp, 0(%rdi) /* store SP */
|
|
movl $0, %eax /* return 0 */
|
|
ret
|
|
|
|
.global pause
|
|
pause:
|
|
pause
|
|
ret
|
|
|
|
.global halt
|
|
halt:
|
|
cli
|
|
cmpl $0, nrdy
|
|
je _nothingready
|
|
sti
|
|
ret
|
|
_nothingready:
|
|
sti
|
|
HLT
|
|
ret
|
|
|
|
.global hardhalt
|
|
hardhalt:
|
|
sti
|
|
.global _halt
|
|
_halt:
|
|
HLT
|
|
ret
|
|
|
|
/*
|
|
* uint32_t mwait32(void* %rdi, uint32_t %esi);
|
|
*/
|
|
.global mwait32
|
|
mwait32:
|
|
cmpl (%rdi), %esi /* changed yet? */
|
|
jne _mm32done
|
|
|
|
movq %rdi, %rax /* linear address to monitor */
|
|
xorq %rcx, %rcx /* extensions */
|
|
xorq %rdx, %rdx /* hints */
|
|
monitor
|
|
|
|
cmpl (%rdi), %esi /* changed yet? */
|
|
jne _mm32done
|
|
|
|
/*xorq CX, CX*/ /* extensions (different from monitor) */
|
|
xorq %rax, %rax /* hints */
|
|
mwait
|
|
_mm32done:
|
|
movl (%rdi), %eax
|
|
ret
|
|
|
|
/* void mwait(void*); */
|
|
.globl mwait
|
|
mwait:
|
|
movq %rdi, %rax
|
|
movl (%eax), %ecx
|
|
orl %ecx, %ecx
|
|
jnz _mwaitdone
|
|
xorq %rdx, %rdx
|
|
monitor
|
|
movl (%eax), %ecx
|
|
orl %ecx, %ecx
|
|
jnz _mwaitdone
|
|
xorq %rax, %rax
|
|
mwait
|
|
_mwaitdone:
|
|
RET
|
|
|
|
rdrand32:
|
|
loop32:
|
|
rdrand %eax
|
|
jc loop32
|
|
ret
|
|
|
|
rdrand64:
|
|
loop64:
|
|
rdrand %rax
|
|
jc loop64
|
|
ret
|
|
|
|
.globl rdrandbuf
|
|
rdrandbuf:
|
|
movq %rdi, %rdx
|
|
|
|
movl %esi, %ecx
|
|
shrq $3, %rcx
|
|
eights:
|
|
cmpl $0, %ecx
|
|
jg f1
|
|
call rdrand64
|
|
movq %rax, 0(%rdx)
|
|
addq $8, %rdx
|
|
subl $1, %ecx
|
|
jmp eights
|
|
|
|
f1:
|
|
movl %esi, %ecx
|
|
andl $7, %ecx
|
|
shrq $2, %rcx
|
|
fours:
|
|
cmpl $0, %ecx
|
|
jg f2
|
|
call rdrand32
|
|
movl %eax, 0(%rdx)
|
|
addq $4, %rdx
|
|
subl $1, %ecx
|
|
jmp fours
|
|
|
|
f2:
|
|
movl %esi, %ecx
|
|
andl $3, %ecx
|
|
ones:
|
|
cmpl $0, %ecx
|
|
jg f3
|
|
call rdrand32
|
|
movb %al, 0(%rdx)
|
|
addq $1, %rdx
|
|
subl $1, %ecx
|
|
jmp ones
|
|
|
|
f3:
|
|
RET
|
|
|
|
|
|
/*
|
|
* Park a processor. Should never fall through a return from main to here,
|
|
* should only be called by application processors when shutting down.
|
|
*/
|
|
.global idle
|
|
idle:
|
|
_idle:
|
|
STI
|
|
HLT
|
|
JMP _idle
|