DGND3700 V1 Transmission Firmware Reverse Decompile 2

From richud.com
Jump to navigation Jump to search


adsl_phy.bin swapping

This may have been a lot easier to someone good with C and assembler skills, unfortunately that isn't me. Without the old source this would have been impossible.

Interesting info here, http://bcm63xx.sipsolutions.net/AdslPhy

At the beginning in 2013

Unfortunately A2pv6C035m [obtained from the beta .20 firmware] is the last version that will load for some reason. (Several older ones I tried all seem to load ok.)

I have tried these three newer ones with same error, which comes from adsldd.ko, so presume they are only as swappable as this kernel module can manage to support.

  • A2pv6C037h
  • A2pv6C038h
  • A2pv6C038k1
BcmAdsl_Initialize=0xC012CA10, g_pFnNotifyCallback=0xC0160FD4
Clocks for QPROC and AFE are being aligned with step through ... 
AFE is aligned, i = 080, PhaseValue = -050, PhaseCntl = 0x3B6D3D9B
QPROC is aligned, i = 001, PhaseValue = 0050, PhaseCntl = 0x3B6D7D9C
Clocks for QPROC and AFE are aligned with syn_status AFE = 0x70, QPROC = 0x78
AFE  phase control reg @0xb0f570f8 default actual = 0x0021C38F, exp = 0x0021c38f
QPRC phase control reg @0xb0f5f0c0 default actual = 0x0421C38F, exp = 0x0421c38f
*** XfaceOffset: 0x5FF90 => 0x5FF90 ***
Failed to read SDRAM image from '/etc/adsl/adsl_phy.bin'.
AdslCoreHwReset:  Failed to load ADSL PHY image (0)
Clocks for QPROC and AFE are being aligned with step through ... 
AFE is aligned, i = 080, PhaseValue = -050, PhaseCntl = 0x3ACD3D4B
QPROC is aligned, i = 001, PhaseValue = 0050, PhaseCntl = 0x3ACD7D4C
Clocks for QPROC and AFE are aligned with syn_status AFE = 0x70, QPROC = 0x78
AFE  phase control reg @0xb0f570f8 default actual = 0x0021C38F, exp = 0x0021c38f
QPRC phase control reg @0xb0f5f0c0 default actual = 0x0421C38F, exp = 0x0421c38f
*** XfaceOffset: 0x5FF90 => 0x5FF90 ***
Failed to read SDRAM image from '/etc/adsl/adsl_phy.bin'.
AdslCoreHwReset:  Failed to load ADSL PHY image (0)
dgasp: kerSysRegisterDyingGaspHandler: dsl0 registered 

The key part should look like this (35m)

pSdramPHY=0xA7FFFFF8, 0x3B1 0xDEADBEEF
*** XfaceOffset: 0x5FF90 => 0x5FF90 ***
*** PhySdramSize got adjusted: 0xC8B38 => 0xFF230 ***
AdslCoreSharedMemInit: shareMemAvailable=3504

Update Feb 2015

I spent a few weeks reverse engineering this using the DG834G source code (DG834GBv4_V5.01.01_src) and looking at the kernel module (of the DGND3700 and HG612). Looking at the MIPS assembled and disassembled code , most of it was same/very similar as far as I could tell [assume really]. (Just to add to the confusion I initially was comparing with the hd612 'enlargened' code but then did screenshots with the enlargened 3800 code, although it was about the same I think)

Understanding the size definitions

Dump first 32 bye header of the PHY to see the sizes of the lmem and sdram parts.

hexdump -C -n 32 adsl_phy_A2pv6C035m.bin
00000000  41 20 00 00 00 00 00 20  00 00 5f 88 00 00 5f a8  |A ..... .._..._.|
00000010  00 0c 8b 38 00 00 00 00  00 00 00 00 00 00 00 00  |...8............|
0x04	32 bit	lmemImageOffset	[offset of the adsl mips part in the file]	00 00 00 20
0x08	32 bit	lmemImageLen	[length of the adsl mips part]			00 00 5f 88  <	0x5F88 (24456)
0x0c	32 bit	sdramImageOffset	[offset of the shared part in the file]	00 00 5f a8	
0x10	32 bit	sdramImageLength	[length of the shared part]		00 0c 8b 38  < aka, *** PhySdramSize got adjusted: 0xC8B38 (822072)


This is the flow in adsldd.ko that controls the PHY loading, as per DG834GBv4_V5.01.01_src source code. Most of the function names are the same.

adsl_init (adsldrv.c) [module entrant]
 BcmAdsl_Initialize (adsl.c)
  BcmAdslCoreInit (BcmAdslcore.c) [from source comment : called by ADSL driver to init core, ADSL PHY]
   ADSLCoreInit (Adslcore.c)
    AdslCoreHwReset (Adslcore.c) [If true returns *** PhySdramSize got adjusted: 0xXXXXX => 0xXXXXXX ***, this not present in source.]
     __AdslCoreHwReset (Adslcore.c) [called 3 times in a loop, why failed PHY loading happens thrice, function seems merged in new adsldd.ko]
      AdslCoreLoadImage (Adslcore.c) [If this returns false it gives error "AdslCoreHwReset:  Failed to load ADSL PHY image (0)"
       AdslFileLoadImage (AdslFile.c) [loads PHY into buffer] [If AdslCoreSetSdramImageAddr returns false, errors "Failed to read SDRAM image from '/etc/adsl/adsl_phy.bin'."]
        AdslCoreSetSdramImageAddr (Adslcore.c) [Prints 'pSdramPHY=0xA7FFFFF8, 0xXXXX 0xDEADBEEF' if successful.]
     If above returns true to __AdslCoreHwReset, it then calls
     AdslCoreSharedMemInit [does calloc] [gived AdslCoreSharedMemInit: shareMemAvailable=xxxxxxxxx]
  • Note Also while testing something later (by removing the phy altogether when it tried to load) I got this stacktrace of the real thing which is helpful
Call Trace:
[<c012d084>] AdslFileLoadImage+0x44/0x210 [adsldd]
[<c011ec1c>] AdslCoreHwReset+0x518/0xb80 [adsldd]
[<c011f55c>] AdslCoreInit+0x2d8/0x2ec [adsldd]
[<c01250c8>] BcmAdslCoreInit+0x12c/0x1d0 [adsldd]
[<c011ad50>] BcmAdsl_Initialize+0x12c/0x17c [adsldd]
[<c011956c>] DoInitialize+0xe0/0x150 [adsldd]
[<c01180f0>] adsl_ioctl+0x9c/0xac [adsldd]
[<80080f10>] vfs_ioctl+0x2d8/0x308
[<80080f90>] sys_ioctl+0x50/0x90
[<80019760>] stack_done+0x20/0x3c


The nub of the problem is that the space tested for an allocatable is 0xFFFFF, which will load a ~ 850kb PHY, anything newer than 35m is bigger. (you can see only 3504 bytes are free after loading 35m). I never worked out the math behind this. There are various macro defines in the source this could relate to, the most likely seemed to be ADSL_SDRAM_TOTAL_SIZE. Sadly there are also lots of random values in the source that aren't defined as macros relating to god knows what too. This is most obvious function this played a part in is AdslCoreSetSdramImageAddr (as adslCorePhyDesc.sdramPageAddr)

This led to

3 things to patch

Three 32 bit values need to be changed. In MIPS these are done in two steps, high 16 (lui) then low 16 (ori)

(These opcode patch offsets below are relative to the default adsldd.ko [adslddDGND3700.o_save] in the DGND3700)

AdslCoreSetSdramImageAddr

Success: pSdramPHY=0xA7FFFFF8, 0xXXXX 0xDEADBEEF, error causes 'Failed to read SDRAM image from /etc/adsl/adsl_file.bin' in AdslFileLoadImage

New PHY: Reduced threshold value of logic branch from 0x0fffff to 0x0cdfff = 0x3200 = 204800

Error stems from this in Adslfile.c (this is the old DG834g source)

AdslFile.c , Function AdslFileLoadImage

	pAdslSDRAM = AdslCoreSetSdramImageAddr(((ulong*)pAdslLMem)[2], phyHdr.sdramSize);
	adslf_lseek(fd, phyHdr.sdramOffset, 0);
	if (adslf_read(fd, pAdslSDRAM, phyHdr.sdramSize) != phyHdr.sdramSize) {
		printk("Failed to read SDRAM image from '%s'.\n", fname);
		adslf_close(fd);
		return 0;
	}
	adslf_close(fd);
	set_fs(fs);
	return phyHdr.lmemSize + phyHdr.sdramSize;
}

If pAdslSDRAM is returned too small the read will fail to load everything, [phyHdr.sdramSize is read from the adls_phy.bin file header size.]

This is from AdslCore.c

void * AdslCoreSetSdramImageAddr(ulong lmem2, ulong sdramSize)
{
	if (0 == lmem2) {
		lmem2 = (sdramSize > 0x40000) ? 0x20000 : 0x40000;
		adslCorePhyDesc.sdramPhyImageAddr = ADSL_PHY_SDRAM_START + lmem2;
	}
	else
		adslCorePhyDesc.sdramPhyImageAddr = lmem2;
	lmem2 &= (ADSL_PHY_SDRAM_PAGE_SIZE-1);
	adslCorePhyDesc.sdramImageAddr = adslCorePhyDesc.sdramPageAddr + lmem2;
#if (ADSL_PHY_SDRAM_PAGE_SIZE == 0x200000)
	if ((lmem2 & 0x00FFFFFF) < 0x100000)	/* old 256K PHY over orig. 2M */
		adslCorePhyDesc.sdramImageAddr += (0x200000 - 0x80000);
#endif
	adslCorePhyDesc.sdramImageSize = (sdramSize+0xF) & ~0xF;
	pSdramReserved = (void *) (adslCorePhyDesc.sdramPageAddr + ADSL_PHY_SDRAM_PAGE_SIZE - sizeof(sdramReservedAreaStruct));
	AdslDrvPrintf(TEXT("pSdramPHY=0x%X, 0x%X 0x%X\n"), (int) pSdramReserved, pSdramReserved->timeCnt, pSdramReserved->initMark);
	adslCoreEcUpdateMask = 0;


softdsl/AdslCoreDefs.h

#define ADSL_SDRAM_TOTAL_SIZE			0x00800000

adslCorePhyDesc.sdramPageAddr = macro ADSL_SDRAM_TOTAL_SIZE

Looking at the MIPS I can sort of see the change is before and relating to what goes on in the second branch (bne) i.e. "if ((lmem2 & 0x00FFFFFF) < 0x100000)". I presume it revolves around either how lmem2 is being defined above it or altering what equates to 0x100000 in the old source? I cant get my head round what is actually happening. Is it perhaps ADSL_SDRAM_TOTAL_SIZE being altered which is changing lmem2?


Adsl phy AdslCoreSetSdramImageAddr.png

Adsl phy AdslCoreSetSdramImageAddr decompile.png

Opcode change

#from
00003ca4  3c 02 00 0f 34 42 ff ff  
#to
00003ca4  3c 02 00 0c 34 42 df ff  

Patch

echo 'Patch adsldd.ko AdslCoreSetSdramImageAddr() lets it load the SDRAM phy in the firstplace, else error Failed to read SDRAM image from..'
printf '\x00\x0c' | dd of="$ROOT/DGND3700/bcmdrivers/broadcom/char/adsl/impl1/adslddDGND3700.o_save" bs=1 seek=$((0x3ca6)) count=2 conv=notrunc
printf '\xdf\xff' | dd of="$ROOT/DGND3700/bcmdrivers/broadcom/char/adsl/impl1/adslddDGND3700.o_save" bs=1 seek=$((0x3caa)) count=2 conv=notrunc

AdslCoreHWReset

Performs: *** PhySdramSize got adjusted: 0xXXXXX => 0xXXXXXX ***

New PHY: Space to load in is approx doubled up from 0xfffff to 0x1fffff = 0x100000 = 1048576

The source code for this looks different (all this stuff is in __AdslCoreHwReset in the source in fact) and I found this initialy by looking for the opcodes as changed in the AdslCoreSharedMemInit alteration, then double checking by comparing the original and HG612 PHY. This is the vaguest of the changes.

Adsl phy AdslCoreHWReset.png

Adsl phy AdslCoreHWReset decompile.png

Note the new code adds 0x200000 into the mix over the old code, not sure what this is doing.

Opcode change

#from
00006818  3c 02 00 0f 34 42 ff ff
#to
00006818  3c 02 00 1f 34 42 ff ff

Patch

echo 'Patch adsldd.ko AdslCoreHWReset() - responsible for allowing PhySdramSize got adjusted.'
printf '\x00\x1f' | dd of="$ROOT/DGND3700/bcmdrivers/broadcom/char/adsl/impl1/adslddDGND3700.o_save" bs=1 seek=$((0x681a)) count=2 conv=notrunc

AdslCoreSharedMemInit

Performs: AdslCoreSharedMemInit: shareMemAvailable=xxxxxxxxx

Without this you will get negative values like AdslCoreSharedMemInit: shareMemAvailable=-119168

New PHY: Space to load in is approx doubled up from 0xfffe0 to 0x1fffff = 0x10001f = 1048607


AdslCore.c

#define ADSL_PHY_SDRAM_SHARED_START		(adslCorePhyDesc.sdramPageAddr + ADSL_PHY_SDRAM_PAGE_SIZE - ADSL_SDRAM_RESERVED)

#define SHARE_MEM_REQUIRE 			2048	/* 936 bytes worst case, but will use 2KB */
int	adslPhyShareMemIsCalloc		= 0;
int	adslPhyShareMemSizeAllow		= 0;
void * adslPhyShareMemStart			= NULL;

void AdslCoreSharedMemInit(void)
{
	int shareMemAvailable = ADSL_SDRAM_IMAGE_SIZE - ADSL_SDRAM_RESERVED - AdslCoreGetSdramImageSize();
	
	if( adslPhyShareMemIsCalloc == 0 ) { /* If calloc() earlier, then will continure to use it; might be from Diags download */
		if(  shareMemAvailable < SHARE_MEM_REQUIRE ) {
			adslPhyShareMemStart = calloc(1,SHARE_MEM_REQUIRE);
			adslPhyShareMemStart = (void *)(0xA0000000 | ((ulong)adslPhyShareMemStart  + SHARE_MEM_REQUIRE));
			adslPhyShareMemIsCalloc = 1;
			adslPhyShareMemSizeAllow = SHARE_MEM_REQUIRE;
		}
		else {
			adslPhyShareMemStart = (void *) ADSL_PHY_SDRAM_SHARED_START;
			adslPhyShareMemSizeAllow = shareMemAvailable;
		}		
	}

	pAdslSharedMemAlloc = adslPhyShareMemStart;
	
}

This allocates the memory it loads the phy into, I couldn't follow this either but it seems likely if enough isn't allocated it cant load! You would think ADSL_SDRAM_RESERVED is the likely thing altered but seems defined as '32' which seems very odd? I assume the value of 'int shareMemAvailable' is what is being edited here.

AdslCore.c

#define			ADSL_SDRAM_RESERVED			32

The other possibility maybe from ADSL_SDRAM_IMAGE_SIZE, but the page size and bias I think are the same. Odd.

softdsl/AdslCoreDefs.h:#define ADSL_SDRAM_IMAGE_SIZE			(ADSL_PHY_SDRAM_PAGE_SIZE - ADSL_PHY_SDRAM_BIAS)
softdsl/AdslCoreDefs.h:#define ADSL_SDRAM_IMAGE_SIZE			(256*1024)
AdslCore.c:	int shareMemAvailable = ADSL_SDRAM_IMAGE_SIZE - ADSL_SDRAM_RESERVED - AdslCoreGetSdramImageSize();

The change seemed the only obvious difference in the function comparing the mips code.

Adsl phy AdslCoreSharedMemInit.png

Adsl phy AdslCoreSharedMemInit decompile.png

Note the new code adds 0x200000 into the mix over the old code, not sure what this is doing.

Opcode change

#from
00003e24  3c 02 00 0f 34 42 ff e0 
#to
00003e24  3c 02 00 1f 34 42 ff ff 

Patch

echo 'Patch adsldd.ko AdslCoreSharedMemInit() does AdslCoreSharedMemInit: shareMemAvailable increase.'
printf '\x00\x1f' | dd of="$ROOT/DGND3700/bcmdrivers/broadcom/char/adsl/impl1/adslddDGND3700.o_save" bs=1 seek=$((0x3e26)) count=2 conv=notrunc
printf '\xff\xff' | dd of="$ROOT/DGND3700/bcmdrivers/broadcom/char/adsl/impl1/adslddDGND3700.o_save" bs=1 seek=$((0x3e2a)) count=2 conv=notrunc

Outcome

Although the PHY would then load, it caused kernel to crash when xdslctl starts, DOH!

I sort of gave up on that and took another approach after finding the new DGND3800 kernel moule was already 'enlargened'. I had to 'fix' (hexed it to point at vprintf instead, cough) an unamed symbol error and then this promptly did exactly the same thing qhwn xdslctl started!...so maybe my fixes had worked afterall.

Comments

blog comments powered by Disqus