flat assembler
Message board for the users of flat assembler.
Index
> Linux > [fasmg] Battery limit manager for Linux made with fasm2 |
| Author |
|
|
Jessé 27 Dec 2025, 16:35
Greetings fasm people!
After some time, I got myself caught by a specific need: a system that I'm using is not properly limitting battery charge. And, after researching a little, I've come up with an idea, and made this simple application, of course, in assembly and using fasm2! Perhaps, it can be useful for you, too! It works by reading a configuration file, where it finds limits and paths to sysfs battery access, so, one can tweak and adjust it without even touch the .asm code. The code for it is at my GitHub repository, also I've provided a ready tarball there to ease the installation process. But, for your convenience, I let the main code below: Code: include 'batt-ctl.inc' ; for instance: ; ext proto geteuid, none _bss tempbuff rb 128 app_limit rq 1 fd_batt rq 1 cur_time rq 1 conffileprops STAT cur_limit rb 6 _data conffile db '/etc/batt-ctl/searchpaths.conf',0 helpmsg db 'Version: %s',10 db 'Usage:',10,10 db 9,'%s -ol XX%%',10 db 9,'%s -svc',10,10 db 9,'-ol',9,'-> Overrides limit to provided percentage (1%% to 100%%)',10 db 9,9,' to all batteries found within config file.',10 db 9,'-svc',9,'-> Invokes service mode, which reads and applies',10 db 9,9,' to batteries as declared within configuration file.',10 db 9,9,' Can be used to reload configuration file parameters.',10,10,0 readmode db 'r',0 writemode db 'r+',0 version db '0.1',0 iterations dd 0 comments dd 0 limits dd 0 searches dd 0 writecnt dd 0 flags: db 0 ; FLAGS map: ; ; BIT0: set if !VERSION directive has been validated; ; BIT1: set if any valid limit has been configured; ; BIT2: set if in application mode (-ol xx% command line option). ; _code align 64 Start: endbr64 __libc_start_main(&@f, [rsp+8], &rsp+16, NULL, NULL, rdx, rsp); align 16 @@ endbr64 push rbx push rbp push r15 mov rdx, [stdout] mov rcx, [stderr] mov rdx, [rdx] mov rcx, [rcx] mov [stdout], rdx mov [stderr], rcx mov r15, rsi ; r15 = saved argv[] push rsi push rdi geteuid(); pop rdi pop rsi test eax, eax jnz .err0 mov rdx, [rsi+8] cmp edi, 2 jb .help jne @f cmp [rdx], dword '-svc' ; service mode jne @f ; cmp [rdx+4], byte 0 je .service jmp .help @@ cmp edi, 3 jne .help cmp [rdx], dword '-ol' ; override level jne @f ; (application) mode .application: mov rdi, [rsi+16] mov [app_limit], rdi ; Save pointer to status message or [flags], byte 100b ; set application mode flag call HandleLimit jc .err9 jmp .app_resume @@ nop .help: GetExecName(*r15); fprintf(*stderr, &helpmsg, &version, rdi, rdi); mov eax, 127 jmp .enderr .service: time(&cur_time); localtime(&cur_time); strftime(&tempbuff, 127, \ "Starting %%s in service mode at: %Y-%m-%d %H:%M:%S%n", rax); GetExecName(*r15); fprintf(*stderr, &tempbuff, rdi); .app_resume: stat(&conffile, &conffileprops); test eax, eax jnz .err1 malloc(*conffileprops.st_size); test rax, rax jz .err2 ; mov [fd_confptr], rax mov rbp, rax ; rbp = buffer for config file data fopen(&conffile, &readmode); test rax, rax jz .err1 mov rbx, rax ; rbx = .conf file ; TEST fprintf(*stderr, <"Config file size '%s' is: %u bytes",\ 10,0>, &conffile, *conffileprops.st_size); @@@ xor r15, r15 mov [rbp], r15 fgets(rbp, *conffileprops.st_size, rbx); test rax, rax jz @@f mov ecx, [conffileprops.st_size] mov edx, [conffileprops.st_size] inc ecx mov rdi, rbp xor al, al repne scasb sub edx, ecx mov r15d, edx ; r15 = line length ; fprintf(*stdout, "(%u) -> ", edx); ; fputs(rbp, *stdout); inc [iterations] cmp [rbp], byte '#' ; check if it is a commented line jne @f inc [comments] jmp @@b @@ cmp [rbp], word 0Ah ; check and skip next if empty line je @@b @@ mov r10, 'LIMIT=' mov r11, 'SEARCH=' mov rax, '!VERSION' mov rdx, [rbp] mov r8, 0#0000FFFFFFFFFFFFh mov r9, 0#00FFFFFFFFFFFFFFh and r8, rdx and r9, rdx @@ cmp rdx, rax ; check for !VERSION directive jne @f cmp [rbp+8], byte '=' jne @f test [flags], byte 1 ; duplicated VERSION statement = error jnz .err4 ; ### !VERSION procedure lea rsi, [version] ; only support version 0.1 so far lea rdi, [rbp+9] lea ecx, [r15d-9] mov [rdi+rcx-1], byte 0 repe cmpsb jne .err3 or [flags], byte 1 ; set version flag if OK. jmp @@b @@ cmp r8, r10 jne @f ; ### LIMIT procedure test [flags], byte 1 jz .err5 inc [limits] ; keep counting for cosmetic reason at ¹ test [flags], byte 100b ; check application mode jnz @@b ; ignore if AM=1 call HandleLimit jc .err6 fprintf(*stderr, "Line %u: Limit at: %s", *iterations, &cur_limit); ; debug line jmp @@b @@ cmp r9, r11 jne @f2 test [flags], byte 1 ; check valid version jz .err5 inc [searches] ; ### SEARCH procedure test [flags], byte 10b ; Check valid limit set flag jz .err7 call HandleSearch jc .err8 test eax, eax jz @f mov r15d, eax ; recycling r15 register for error point errno(); fprintf(*stderr, " Write unsuccessful(%u:%u): ", r15d, [rax]); perror(NULL); @@ nop jmp @@b @@ nop ; ### unrecognized configuration directive procedure mov [rbp+r15-2], byte 0 fprintf(*stderr, <"Configuration error at line <%u>: unrecognized: '%s'", \ ": ignoring",10,0>, *iterations, rbp); jmp @@b @@@ fflush(*stdout); test [flags], byte 100b jnz @f fprintf(*stdout,<"Total iterations: %u; ", \ "limits found: %u; ", \ "search paths: %u; ", \ "commented lines: %u", \ 10, "Successful writes: %u",10,0>, \ *iterations, *limits, *searches, *comments, *writecnt); jmp @f2 .batt1 db 'y',0 .battn db 'ie' .mt1 db 's' ; ¹ .nullstr db 0 ; ¹ @@ lea r8, [.batt1] lea r9, [.nullstr] lea r10, [.battn] lea r11, [.mt1] cmp [writecnt], 1 cmova r8, r10 cmp [limits], 1 ; ¹ issue 'limit' or 'limits' depending cmova r9, r11 ; on number of active lines at .conf file fprintf(*stdout, <"Limit set manually at %s to %u batter%s ", \ "during this session.",10,"To make it persistent, edit ", \ "the 'LIMIT=' line%s at the aforementioned config file.",10,0>, \ *app_limit, *writecnt, r8, r9); @@ nop fclose(rbx); free(rbp); test [writecnt], -1 jnz .end fputs(<"No changes have been made to battery charge limits. ",10, \ "Check your configuration file for valid search paths, or ",10, \ "if your system support battery control methods provided ",10, \ "by this application.",10,0>, *stdout); mov eax, 126 jmp .enderr .end: xor eax, eax .enderr: pop r15 pop rbp pop rbx ret .err9: fputs(<"Invalid parameter format or limit out of range.",10,0>, \ *stderr); mov eax, 10 jmp .enderr .err8: mov [rbp+r15-1], byte 0 fprintf(*stderr, <"Invalid path at line <%u>: '%s'",10,0>, \ *iterations, rbp); fclose(rbx); free(rbp); mov eax, 9 jmp .enderr .err7: fputs(<"Configuration error: ", \ "search statement before limit set. Aborted.",10,0>, *stderr); fclose(rbx); free(rbp); mov eax, 8 jmp .enderr .err6: mov [rbp+r15-1], byte 0 fprintf(*stderr, <"Invalid limit at line <%u>: '%s'",10,0>, \ *iterations, rbp); fclose(rbx); free(rbp); mov eax, 7 jmp .enderr .err5: fputs(<"Cannot find which version to process. Aborted.",10,0>, *stderr); fclose(rbx); free(rbp); mov eax, 6 jmp .enderr .err4: fputs(<"Duplicated version statement. Aborted.",10,0>, *stderr); fclose(rbx); free(rbp); mov eax, 5 jmp .enderr .err3: fputs(<"Configuration file version mismatch. Aborted.",10,0>, *stderr); fclose(rbx); free(rbp); mov eax, 4 jmp .enderr .err2: fputs(<"Could not allocate memory.",10,0>, *stderr); mov eax, 3 jmp .enderr .err1: fputs(<"Configuration file corrupted or not found.",10,0>, *stderr); mov eax, 2 jmp .enderr .err0: fputs(<"Must be run as root!",10,0>, *stderr); ; mov eax, 1 jmp .help HandleLimit: push r14 lea r10, [rdi-6] test [flags], byte 100b ; check application mode cmovnz rbp, r10 lea rdi, [rbp+6] mov al, '%' ; checking percent char mov ecx, 4 repne scasb jne .err errno(); mov r14, rax mov [r14], dword 0 strtoul(&rbp+6, NULL, 10); test [r14], dword -1 jnz .err cmp eax, 100 ja .err test eax, eax jle .err lea rsi, [rbp+6] lea rdi, [cur_limit] @@ lodsb cmp al, '%' je @f stosb jmp @b @@ mov [rdi], word 0Ah pop r14 or [flags], byte 10b ; set valid limit flag clc ret .err: pop r14 stc ret HandleSearch: push r13 cmp [rbp+7], dword '/sys' jne .err cmp [rbp+11], byte '/' jne .err mov [rbp+r15-1], byte 0 push r12 push r14 fprintf(*stderr, <"Line %u: Path: '%s'",10,0>, *iterations, &rbp+7); errno(); xor r13, r13 mov [rax], r13d fopen(&rbp+7, &writemode); test rax, rax mov edx, 1 ; error opening file cmovz r13d, edx jz .end mov r12, rax ; r12 = searched file fgets(&tempbuff, 127, r12); test rax, rax mov edx, 2 ; error reading file cmovz r13d, edx jnz @f fclose(r12); jmp .end @@ fprintf(*stderr, " Current value: %s", &tempbuff); fseek(r12, 0, SEEK_SET); test eax, eax mov edx, 3 ; error seeking start of file cmovnz r13d, edx jz @f fclose(r12); jmp .end @@ fputs(&cur_limit, r12); test eax, eax mov edx, 4 ; error writing to file cmovs r13d, edx jns @f fclose(r12); jmp .end @@ fclose(r12); fprintf(*stderr, " Write successful: new value: %s", &cur_limit); inc [writecnt] .end: pop r14 pop r12 mov eax, r13d ; return status pop r13 clc ret .err: pop r13 stc ret ; Assuming: rdi = pointer to cmdline argv[0] _GetExecName: push r11 mov r11, rdi xor al, al mov ecx, 65535 ; max cmdline length to search for repne scasb not cx sub rdi, 2 mov al, '/' std repne scasb jne @f add rdi, 2 jmp @f2 @@ mov rdi, r11 @@ cld pop r11 ret Header file: Code: format ELF64 executable 3 at 100000h entry Start use AMD64, CET_IBT include 'fastcall_v1.inc' include 'anon_label.inc' include 'stdio.inc' macro struct? name define __ACTUAL__ name esc struc name label . : .size - . end macro macro ends?! .size: esc end struc virtual at 0 match a, __ACTUAL__ a a repeat 1, sz:.size display "'", `a, "'", " size: ", `sz, 10 end repeat end match end virtual end macro struct STAT .st_dev dq ? .st_ino dq ? .st_nlink dq ? .st_mode dd ? .st_uid dd ? .st_gid dd ? dd ? .st_rdev dq ? .st_size: dq ? .st_blksize dq ? .st_blocks dq ? .st_atim TIMESPEC .st_mtim TIMESPEC .st_ctim TIMESPEC .__glibc_reserved: rq 3 ends struct TIMESPEC .tv_sec dq ? .tv_nsec dq ? ends struct TIMEVAL .tv_sec dq ? .tv_usec dq ? ends proto GetExecName, qword It is a little premature, but it is already working, at least at my Vivobook laptop. I must alert that this needs my fastcall macro toolkit to be compiled as is being shown. You can find it on my repos, too. Thanks, |
|||
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.