282d23fff7
FUN_0000d10c @ 0xd10c (49 insts) contains poll site 11. Semantically decoded as a PHY-side prologue for frequency-change handshake: saves current state of one PHY CTL + four secondary-table entries, waits for PHY firmware to reach state 1 (idle). Matching-decomp iteration deferred vs the clean first lift (d328) — d10c's two-base-pointer csel pattern plus parity-dependent offset chain gives GCC too much register-allocation freedom. Getting to >=90% byte-match would be an afternoon of iteration; time better spent expanding pre-UART coverage breadth. Poll-site coverage so far: d328: sites 12, 13, 14, 15 (C candidate at 89.7% size match) d10c: site 11 (reference C only, no matching iteration) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
107 lines
4.6 KiB
C
107 lines
4.6 KiB
C
/* Best-effort ground truth for FUN_0000d10c @ 0xd10c (196 bytes / 49 insts).
|
|
* Contains poll site 11 (the sole site in this function).
|
|
*
|
|
* Harder to lift than FUN_0000d328 — uses two base pointers (channel-switched
|
|
* via csel) and several save-and-modify loops on PHY registers indexed
|
|
* through a secondary table pointer. Represents the "prepare-for-freq-change"
|
|
* family of functions.
|
|
*
|
|
* Signature: void prep_freq_change(struct ctx *ctx, uint32_t ch, void *save_area);
|
|
* X0 = ctx (opaque)
|
|
* W1 = channel index (0..3)
|
|
* X2 = save_area where original register values are cached for later restore
|
|
*
|
|
* Structure of ctx (guessed from offsets):
|
|
* ctx[0..7] : array of channel base pointers? (ctx[ch*32] = channel base)
|
|
* ctx[0xc8], ctx[0xd0] : two "secondary register table" base pointers
|
|
* (one picked based on ch > 1)
|
|
*
|
|
* Behaviour:
|
|
* 1. Look up the PHY base pointer for channel `ch` from ctx[ch*32].
|
|
* 2. Select one of two secondary tables based on whether `ch > 1`.
|
|
* 3. Read+save PHY @ +0x10180; clear bits 0xFFFBFD00, write back.
|
|
* 4. Poll site 11: wait for PHY +0x10014 [2:0] == 1.
|
|
* 5. For a fixed set of secondary-table offsets (depend on ch parity):
|
|
* save old value (masked), write a "disable" value.
|
|
* 6. Return. The restore step happens in a sibling function.
|
|
*
|
|
* This is the PHY-side half of a frequency-scaling handshake: pause
|
|
* training, record current state, clear active masks, wait for the
|
|
* PHY firmware to reach "state 1" (idle?), then prime the hardware
|
|
* for the new clock.
|
|
*
|
|
* NOTE: matching-decomp iteration on this function is deferred — it
|
|
* will take several hours to reach ≥90% byte-match because GCC's
|
|
* register allocation for the two-base-pointer csel pattern differs
|
|
* substantially from the vendor's. The AI-Ghidra rename pass
|
|
* (GhidrAssist + Hermes-2-Pro on dirac) may help identify the
|
|
* semantic names for the offset constants (0x4c, 0x30, 0x1F0000,
|
|
* 0x2FFF0FFF, etc.) before we continue here.
|
|
*/
|
|
#include <stdint.h>
|
|
|
|
struct ctx; /* opaque; array+pointer layout inferred from offsets */
|
|
|
|
#define PHY_OFF_BASE 0x10000
|
|
#define PHY_CTL_180 0x180 /* absolute: +0x10180 */
|
|
#define PHY_STATE_014 0x14 /* absolute: +0x10014 */
|
|
|
|
#define PHY_CTL_CLR_MASK 0xFFFBFD00U
|
|
#define PHY_STATE_IDLE 1U
|
|
|
|
static inline uint32_t rd(volatile uint8_t *b, unsigned o) {
|
|
return *(volatile uint32_t *)(b + o);
|
|
}
|
|
static inline void wr(volatile uint8_t *b, unsigned o, uint32_t v) {
|
|
*(volatile uint32_t *)(b + o) = v;
|
|
}
|
|
|
|
void prep_freq_change(struct ctx *ctx, uint32_t ch, uint8_t *save_area)
|
|
{
|
|
uint64_t *ch_bases = (uint64_t *)ctx;
|
|
uint64_t *sec_table_a = *(uint64_t **)((uint8_t *)ctx + 0xC8);
|
|
uint64_t *sec_table_b = *(uint64_t **)((uint8_t *)ctx + 0xD0);
|
|
uint64_t *sec_table = (ch > 1) ? sec_table_b : sec_table_a;
|
|
|
|
volatile uint8_t *phy_ch = (volatile uint8_t *)(ch_bases[ch] + PHY_OFF_BASE);
|
|
uint8_t *ch_save = save_area + ch * 32;
|
|
|
|
/* Read + cache + mask-modify PHY CTL register */
|
|
wr((uint8_t *)ch_save, 0x238, rd(phy_ch, PHY_CTL_180));
|
|
uint32_t v = rd(phy_ch, PHY_CTL_180) & PHY_CTL_CLR_MASK;
|
|
wr(phy_ch, PHY_CTL_180, v);
|
|
|
|
/* Poll site 11 — wait for PHY state machine to report idle */
|
|
while ((rd(phy_ch, PHY_STATE_014) & 7U) != PHY_STATE_IDLE)
|
|
;
|
|
|
|
/* The rest: save + clear secondary-table entries indexed by a
|
|
* parity-dependent pattern. Offsets 0x30, 0x4c, 0x18, 0x24 into
|
|
* the secondary table; value masks 0xffff, 0x1f, 0x2fff, 0x4000;
|
|
* replacement writes 0x1f0000, 0x2fff0fff, 0x40000000.
|
|
*
|
|
* This section is semi-decoded — names are the analyst's best guesses.
|
|
* Further AI-Ghidra rename pass needed before attempting byte-match. */
|
|
uint32_t parity = ch & 1;
|
|
unsigned o0 = parity * 0x30;
|
|
unsigned o1 = (parity + 0x4c) << 2; /* ubfiz x1, x1, #2, #8 == (x1 & 0xff) << 2 */
|
|
|
|
/* Block A: save low 16 bits at sec[o0+0x18], no write-back */
|
|
wr(ch_save, 0x244, (uint32_t)(sec_table[(o0 + 0x18) / 8] & 0xFFFF));
|
|
|
|
/* Block B: save low 5 bits at sec[o0+4], write 0x1f0000 */
|
|
uint32_t *b_ptr = (uint32_t *)((uint8_t *)sec_table + o0 + 4);
|
|
wr(ch_save, 0x240, *b_ptr & 0x1F);
|
|
*b_ptr = 0x1F0000U;
|
|
|
|
/* Block C: save low 12 bits at sec[o0+0x24], write 0x2fff0fff */
|
|
uint32_t *c_ptr = (uint32_t *)((uint8_t *)sec_table + o0 + 0x24);
|
|
wr(ch_save, 0x248, *c_ptr & 0x2FFFU);
|
|
*c_ptr = 0x2FFF0FFFU;
|
|
|
|
/* Block D: save bit 14 at sec[o1], write 0x40000000 */
|
|
uint32_t *d_ptr = (uint32_t *)((uint8_t *)sec_table + o1);
|
|
wr(ch_save, 0x24C, *d_ptr & 0x4000U);
|
|
*d_ptr = 0x40000000U;
|
|
}
|