Skip to content

Instantly share code, notes, and snippets.

@pgaskin
Created September 27, 2022 21:28
Show Gist options
  • Save pgaskin/1f9a0097284bfcace6fa30a5c9c2ca70 to your computer and use it in GitHub Desktop.
Save pgaskin/1f9a0097284bfcace6fa30a5c9c2ca70 to your computer and use it in GitHub Desktop.
Limit qcom battery charging on Android. Written in assembly for fun.
// 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