pxlycan

PixeLycan: devinfo viewer and modifier for Tensor-based Google Pixels
git clone git://git.luxferre.top/pxlycan.git
Log | Files | Refs

commit 27715e7e3269a7689752e3d9ba287c70e40d95cc
Author: Luxferre <lux@ferre>
Date:   Sun,  3 Nov 2024 20:49:52 +0200

ininial up

Diffstat:
Apxlycan.tcl | 244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 244 insertions(+), 0 deletions(-)

diff --git a/pxlycan.tcl b/pxlycan.tcl @@ -0,0 +1,244 @@ +#!/usr/bin/env tclsh +# PixeLycan: devinfo partition modification utility for Google Pixel phones +# (Tensor/Exynos-based, starting with Pixel 6) +# Usage: +# View info: pxlycan.tcl devinfofile +# Set info field: pxlycan.tcl devinfofile setinfofield fieldname hex_value +# Set PS tags: pxlycan.tcl devinfofile setps tag1 val1 tag2 val2 ... +# Set PSENV tags: pxlycan.tcl devinfofile setpsenv tag1 val1 tag2 val2 ... +# where tag1, tag2 etc must start with DIUS_ or DIFR_ based on their type +# Requires Tcl 8.6 and up with no external dependencies +# Created by Luxferre in 2024, released into public domain + +# parse the PS and PS env tags into the pstags and psenvtags list +# every tag is a dictionary with the following fields: +# offset type key value key_len full_len +# where type can be DIFR or DIUS +proc scantags {infodata} { + set pstags {} + set psenvtags {} + set offset 128 + while {$offset < 8192} { + set scanval [string range $infodata $offset end] + binary scan $scanval a4 tmagic + if {$tmagic eq {DIFR} || $tmagic eq {DIUS}} { + set scanval [string range $scanval 4 end] + binary scan $scanval iu full_len + if {$full_len > 0xf00} { # handle special case of the last tag + # the rule is: current offset + 12 + full_len = 8192 + incr offset 12 + set scanval [string range $infodata $offset end] + binary scan $scanval iu key_len + set scanval [string range $scanval 4 end] + set tagdata [string range $scanval 0 end] + + } else { # normal tag + set scanval [string range $scanval 4 end] + binary scan $scanval iu key_len + set scanval [string range $scanval 4 end] + set tagdata [string range $scanval 0 [expr {$full_len - 1}]] + } + set tagkey [string trim [string range $tagdata 0 [expr {$key_len - 1}]]] + if {$tagkey ne ""} { + set tagkey [string cat $tmagic _ $tagkey] + set tagvalue [string range $tagdata $key_len end] + if {$tagvalue ne ""} { + if {$offset >= 4096} { + dict set psenvtags $tagkey $tagvalue + } else { + dict set pstags $tagkey $tagvalue + } + } + } + incr offset 12 + incr offset $full_len + } else {incr offset 4} + } + return [list pstags $pstags psenvtags $psenvtags] +} + +# build the PS/PSENV tag blob from a Tcl dictionary-like list +# where key names are like DIUS_name or DIFR_name +# key names are stripped, values are passed as is +# pass 3968 as maxlen for PS tag zone, 4096 for PSENV tag zone +proc buildtagblob {inputdict maxlen} { + set tagblob {} + # iterate over the input dictionary + dict for {ckey val} $inputdict { + if {[string match DI??_* $ckey]} { + set key "[string trim [string range $ckey 5 end]]\0" + set type [string range $ckey 0 3] + set keylen [string length $key] + set fullen [expr {$keylen + [string length $val]}] + append tagblob [binary format a4iuiu $type $fullen $keylen] + append tagblob $key $val + } + } + # build the last padding tag of DIFR type + set fullen [expr {$maxlen - [string length $tagblob] - 12}] + append tagblob [binary format a4iuiux$fullen DIFR $fullen 0] + return $tagblob +} + + +# parse the argument +set devinfofile "" +if {[llength $argv] > 0} { + set devinfofile [lindex $argv 0] +} else { + puts "No devinfo file specified" + exit 1 +} +if {![file exists $devinfofile]} { + puts "Invalid devinfo file specified" + exit 1 +} + +# read the devinfo partition +set fp [open $devinfofile r] +fconfigure $fp -translation binary -buffering none +set infodata [read $fp] +close $fp + +# parse the basic info +if {[string length $infodata] != 8192} { + puts "Invalid devinfo file length (must be 8192 bytes)" + exit 1 +} +if {[string first "DEVI" $infodata] ne 0} { + puts "Invalid devinfo file magic (must be DEVI)" + exit 1 +} +# the infomap has the format {name offset:formattype name2 offset2:formattype2 ...} +set infomap { + magic 0:a4 + ver_major 4:su + ver_minor 6:su + board_project 40:su + board_stage_code 42:cu + board_rev_hi 43:cu + board_rev_lo 44:cu + board_variant 45:cu + slota_retrycount 48:cu + slota_flags 49:cu + slotb_retrycount 52:cu + slotb_flags 53:cu +} +array set info {} +dict for {key val} $infomap { + set vparts [split $val :] + set offset [lindex $vparts 0] + set fmt [lindex $vparts 1] + set scanval [string range $infodata $offset [expr {$offset + 4}]] + binary scan $scanval $fmt info($key) +} + +puts "===========================" +puts " PixeLycan by Luxferre" +puts "===========================" +puts "\nInput file: $devinfofile" +puts "\n------- Info fields -------\n" +puts "File magic: $info(magic)" +puts "Major format version: $info(ver_major)" +puts "Minor format version: $info(ver_minor)" +puts "Board project version: [format %04X $info(board_project)]" +puts "Board stage code: $info(board_stage_code)" +puts "Board revision: [format %X $info(board_rev_hi)].[format %02X $info(board_rev_lo)]" +puts "Board variant: $info(board_variant)" +puts "Slot A retries: $info(slota_retrycount)" +puts "Slot A flags: [format %02X $info(slota_flags)]" +puts "Slot B retries: $info(slotb_retrycount)" +puts "Slot B flags: [format %02X $info(slotb_flags)]" + +set taglist [scantags $infodata] + +puts "\n--------- PS tags ---------\n" +dict for {key val} [dict get $taglist pstags] { + if {$key ne ""} { + set hexval [binary encode hex $val] + puts "$key: $val (HEX:$hexval)" + } +} +puts "\n------- PSENV tags --------\n" +dict for {key val} [dict get $taglist psenvtags] { + if {$key ne ""} { + set hexval [binary encode hex $val] + puts "$key: $val (HEX:$hexval)" + } +} +puts "\n---------------------------\n" + +# read the additional arguments as key/value dictionary to set +if {[llength $argv] > 1} { + set mode [lindex $argv 1] + set stoffset 0 + set maxlen 0 + set activedict {} + # the mode can be setinfofield, setps or setpsenv, start offset depends on it + if {$mode eq "setinfofield"} { # only one infofield can be set at a time + if {[llength $argv] > 3} { + set targetname [lindex $argv 2] + if {![dict exists $infomap $targetname]} { + puts "Invalid info field name!" + puts "Valid field names: [dict keys $infomap]" + exit 1 + } + puts "Setting the $targetname info field" + set targetparams [split [dict get $infomap $targetname] :] + set targetoffset [lindex $targetparams 0] + set targetfmt [lindex $targetparams 1] + set fmtlen 1 + if {$targetfmt eq "su"} {set fmtlen 2} + binary scan [binary decode hex [string trim [lindex $argv 3]]] $targetfmt targetvalue + set finalvalue [binary format $targetfmt $targetvalue] + set infodata [string replace $infodata $targetoffset [expr {$fmtlen + int($targetoffset) - 1}] $finalvalue] + puts "Saving the devinfo image to $devinfofile..." + set fp [open $devinfofile w] + fconfigure $fp -translation binary -buffering none + puts -nonewline $fp $infodata + close $fp + puts "Devinfo file saved" + exit 0 + } else { + puts {Invalid setinfofield parameters!} + puts {Usage: pxlycan.tcl [devinfofile] setinfofield [name] [hexvalue]} + puts "Valid field names: [dict keys $infomap]" + } + } elseif {$mode eq "setps"} { + set stoffset 128 + set maxlen 3968 + set activedict [dict get $taglist pstags] + } elseif {$mode eq "setpsenv"} { + set stoffset 4096 + set maxlen 4096 + set activedict [dict get $taglist psenvtags] + } else { + puts "Invalid setting mode, please choose setinfofield, setps or setpsenv!" + exit 1 + } + if {[llength $argv] > 2} { + set params [lrange $argv 2 end] + dict for {ckey val} $params { + if {[string match DI??_* $ckey]} { + puts "($mode) Setting tag $ckey" + if {[string match HEX:* $val]} { # direct hex value + set val [string range $val 4 end] + dict set activedict $ckey [binary decode hex $val] + } else { # string value + dict set activedict $ckey [string cat $val \0] + } + } + } + puts "Building blob..." + set tagblob [buildtagblob $activedict $maxlen] + puts "Writing blob to devinfo..." + set infodata [string replace $infodata $stoffset [expr {$maxlen + $stoffset - 1}] $tagblob] + puts "Saving the devinfo image to $devinfofile..." + set fp [open $devinfofile w] + fconfigure $fp -translation binary -buffering none + puts -nonewline $fp $infodata + close $fp + puts "Devinfo file saved" + } +} +