Created
September 27, 2022 21:28
-
-
Save pgaskin/1f9a0097284bfcace6fa30a5c9c2ca70 to your computer and use it in GitHub Desktop.
Limit qcom battery charging on Android. Written in assembly for fun.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// as qchlimit.s -o qchlimit.o | |
// ld qchlimit.o -o qchlimit | |
// llvm-strip --strip-all --strip-sections qchlimit | |
// adb root && adb shell 'killall qchlimit ; /path/to/qchlimit 35 75 &' | |
.global _start | |
.text | |
.equ SYS_openat, 56 | |
.equ SYS_pread64, 67 | |
.equ SYS_pwrite64, 68 | |
.equ SYS_close, 57 | |
.equ SYS_exit, 93 | |
.equ SYS_nanosleep, 101 | |
.equ SYS_chdir, 49 | |
.equ SYS_setsid, 157 | |
.equ AT_FDCWD, -100 | |
.equ O_RDONLY, 0x0000 | |
.equ O_RDWR, 0x0002 | |
_start: | |
// argc | |
ldr x0, [sp] | |
// argc == 3 || exit(2) | |
cmp x0, 3 | |
beq _start__argc_ok | |
mov x8, SYS_exit | |
mov x0, 2 | |
svc #0 | |
_start__argc_ok: | |
// x19 = parsepct(argv[1]) || exit(1) | |
mov x0, #4 | |
ldr x1, [sp, 16] | |
bl parsepct | |
bl checkerr | |
mov x19, x0 | |
// x20 = parsepct(argv[2]) || exit(1) | |
mov x0, #4 | |
ldr x1, [sp, 24] | |
bl parsepct | |
bl checkerr | |
mov x20, x0 | |
// if (x19 >= x20) exit(1) | |
cmp x19, x20 | |
blt _start__args_ok | |
_start__args_ok: | |
// x21 = openat(AT_FDCWD, BAT_CAPACITY, O_RDONLY) || exit(1) | |
mov x8, SYS_openat | |
mov x0, AT_FDCWD | |
adr x1, BAT_CAPACITY | |
mov x2, O_RDONLY | |
svc #0 | |
bl checkerr | |
mov x21, x0 | |
// x22 = openat(AT_FDCWD, BAT_SUSPEND, O_RDWR) || exit(1) | |
mov x8, SYS_openat | |
mov x0, AT_FDCWD | |
adr x1, BAT_SUSPEND | |
mov x2, O_RDWR | |
svc #0 | |
bl checkerr | |
mov x22, x0 | |
// x0 = pread64(x21, BAT_SUSPEND_VAL, 4) || exit(1) | |
mov x8, SYS_pread64 | |
mov x0, x22 | |
adr x1, BAT_SUSPEND_VAL | |
mov x2, BAT_SUSPEND_VAL_LEN | |
mov x3, #0 | |
svc #0 | |
bl checkerr | |
// x23 = parsebool(x0, BAT_SUSPEND_VAL) || exit 1 | |
bl parsebool | |
bl checkerr | |
mov x23, x0 | |
bl detach | |
_start_loop: | |
// x0 = pread64(x21, BAT_CAPACITY_VAL, 4) || exit(1) | |
mov x8, SYS_pread64 | |
mov x0, x21 | |
adr x1, BAT_CAPACITY_VAL | |
mov x2, BAT_CAPACITY_VAL_LEN | |
mov x3, #0 | |
svc #0 | |
bl checkerr | |
// x0 = parsepct(x0, BAT_CAPACITY_VAL) || exit(1) | |
bl parsepct | |
mov x2, x0 | |
bl checkerr | |
// x0 = suspended ? capacity >= min : capacity >= max | |
cbz x23, _start__ss_0 | |
cmp x0, x19 | |
cset x0, ge // suspended: stay until capacity < min | |
b _start__ss_1 | |
_start__ss_0: | |
cmp x0, x20 | |
cset x0, ge // !suspended: stay until capacity >= max | |
_start__ss_1: | |
// if (x23 != x0) | |
cmp x23, x0 | |
beq _start__keep | |
mov x23, x0 | |
// BAT_SUSPEND_VAL={x0+'0', '\n'} | |
adr x2, BAT_SUSPEND_VAL | |
add x0, x0, '0' | |
strb w0, [x2], 1 | |
mov x0, '\n' | |
strb w0, [x2], 1 | |
// x0 = pwrite64(x22, BAT_SUSPEND_VAL, 2, 0) | |
mov x8, SYS_pwrite64 | |
mov x0, x22 | |
adr x1, BAT_SUSPEND_VAL | |
mov x2, #2 | |
mov x3, #0 | |
svc #0 | |
// if (x0 < 0) x23 ^= 1 | |
cmp x0, #0 | |
bge _start__keep | |
eor x23, x23, #1 | |
_start__keep: | |
// x0 = nanosleep(POLL_INTERVAL) | |
mov x2, x0 | |
mov x8, SYS_nanosleep | |
adr x0, POLL_INTERVAL | |
mov x1, #0 | |
svc #0 | |
mov x0, x2 | |
// continue | |
b _start_loop | |
// x0 = close(x22) | |
mov x8, SYS_close | |
mov x0, x22 | |
svc #0 | |
// x0 = close(x21) | |
mov x8, SYS_close | |
mov x0, x21 | |
svc #0 | |
detach: | |
// x0 = close(0) | |
mov x8, SYS_close | |
mov x0, 0 | |
svc #0 | |
// x0 = close(1) | |
mov x8, SYS_close | |
mov x0, 1 | |
svc #0 | |
// x0 = close(2) | |
mov x8, SYS_close | |
mov x0, 2 | |
svc #0 | |
// x0 = chdir("/") | |
mov x8, SYS_chdir | |
mov x0, ROOT_DIR | |
svc #0 | |
// x0 = setsid() | |
mov x8, SYS_setsid | |
svc #0 | |
ret | |
checkerr: | |
cmp x0, #0 | |
bge checkerr__ok | |
mov x8, SYS_exit | |
neg x0, x0 | |
svc #0 | |
checkerr__ok: | |
ret | |
parsebool: // maxlen, buf | |
cbz x0, parsebool__err | |
cbz x1, parsebool__err | |
mov x8, #0 | |
ldrb w8, [x1], 1 | |
sub x8, x8, '0' | |
cmp x8, 1 | |
bgt parsebool__err | |
cmp x0, 2 | |
blt parsebool__ret | |
mov x9, #0 | |
ldrb w9, [x1] | |
cbz x9, parsebool__ret | |
cmp x9, ' ' | |
beq parsebool__ret | |
cmp x9, '\n' | |
beq parsebool__ret | |
parsebool__err: | |
mov x8, #-1 | |
parsebool__ret: | |
mov x0, x8 | |
ret | |
parsepct: // maxlen, buf | |
mov x8, x0 | |
mov x9, x1 | |
mov x0, #0 | |
mov x10, #0 | |
mov x11, #10 | |
cbz x9, parsepct__ret | |
parsepct__loop: | |
cbz x8, parsepct__ret | |
ldrb w10, [x9], 1 | |
sub x8, x8, 1 | |
cbz x10, parsepct__ret | |
cmp x10, ' ' | |
beq parsepct__ret | |
cmp x10, '\n' | |
beq parsepct__ret | |
sub x10, x10, '0' | |
bmi parsepct__err | |
cmp x10, #9 | |
bgt parsepct__err | |
madd x0, x0, x11, x10 | |
cmp x0, 100 | |
bgt parsepct__err | |
b parsepct__loop | |
parsepct__err: | |
mov x0, #-1 | |
parsepct__ret: | |
ret | |
.data | |
ROOT_DIR: | |
.asciz "/" | |
BAT_CAPACITY: | |
.asciz "/sys/class/power_supply/qcom_battery/capacity" | |
BAT_CAPACITY_VAL: | |
.zero 4 | |
.equ BAT_CAPACITY_VAL_LEN, . - BAT_CAPACITY_VAL | |
BAT_SUSPEND: | |
.asciz "/sys/class/power_supply/qcom_battery/input_suspend" | |
BAT_SUSPEND_VAL: | |
.zero 4 | |
.equ BAT_SUSPEND_VAL_LEN, . - BAT_SUSPEND_VAL | |
POLL_INTERVAL: // struct timespec | |
.xword 15 // .tv_sec | |
.xword 0 // .tv_nsec |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment