commit 27715e7e3269a7689752e3d9ba287c70e40d95cc
Author: Luxferre <lux@ferre>
Date: Sun, 3 Nov 2024 20:49:52 +0200
ininial up
Diffstat:
A | pxlycan.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"
+ }
+}
+