Help with asm constraints

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi All,

I'm in a situation where i need to remotely update a bootloader (i know, it would have been nice to get it right the first time!) luckily, by accident the bootloader is not locked. However as some may know you can't just update it.

My project is for the atmega1284p.

I came across a method for bootjacking which seems nice and I'm trying to get it to compile by not having any luck.

Unfortunately it has been a long time since i was deep in c and assembly land on atmel processors and could really use some help.

I put the project up on github here: https://github.com/unstableunico...

The main part i'm having issues with is getting the asm constraints to work, it keeps throwing a compiler error at me and i can't figure it out.

It's based off the work by Snail (Fignition) i found: http://oneweekwonder.blogspot.co...

The SpmSequenceAddr is a unsigned long, and was a global, which i found may not work so assigned a local to the value.

I also split it up with a union & struct to address the individual parts.

typedef union
{
    struct
    {
        uint8_t res;
        uint8_t rz;
        uint8_t zh;
        uint8_t zl;
    };
    uint32_t add_32;
} ADD_T;

ADD_T SpmSequenceAddr;
void SpmLeapCmd( uint32_t addr, uint8_t spmCmd, uint16_t optValue)
{
	uint8_t cmdReg,tmp=0;
    ADD_T spmaddr;
    spmaddr.add_32 = SpmSequenceAddr.add_32;

	cmdReg=(uint8_t)((GetPgmWord(SpmSequenceAddr.add_32)>>4)&0x1f);
	/*PINC|=(1<<4);*/
	asm volatile(
				"push r0\n"
				"push r1\n"		// needed for opt command.
				"push %11\n"
				"push r30\n"
				"push r31\n"
				"SpmLeapCmdWaitSpm: in %11,%10\n"	//
				"sbrc %11,0\n"	//wait for spm operation complete.
				"rjmp SpmLeapCmdWaitSpm\n"
				"ldi %11,1\n"	// timer 0 start at fClk
				"out %2,%11\n"	// set TCCR0B so off we go. This is time 0c.
				"movw r0,%5\n"	// set the value to be written.

				"mov r30,%3\n"	// get the register used by the sequence's spm command.
				"ldi r31,0\n"	// z^reg to save.
				"ld %11,Z\n"	// get the reg
				"push %11\n"	// saved it, now we can overwrite with spm command.

				"ldi r30,lo8(pm(SpmLeapCmdRet))\n"
				"ldi r31,hi8(pm(SpmLeapCmdRet))\n"
				"push r30\n"
				"push r31\n"	// return address must be pushed big-endian.
				"lds r30,%7\n"	// lo uint8_t of Spm sequence address
				"lds r31,%8\n" // hi uint8_t of Spm sequence address. z^sequence in code.
                "lds RAMPZ,%9\n"
				"lsr r31\n"
				"ror r30\n"		// div 2 to get correct Spm program address.
				"push r30\n"
				"push r31\n"	// Spm sequence program address must be pushed big-endian.
                "push RAMPZ\n"

				"push %A6\n"	// before we overwrite reg used by sequence's spm command
								// we must first save the spm target address
				"push %B6\n"	// in case it would get overwritten by the st Z.

				"mov r30,%3\n"	// get the register used by the sequence's spm command.
				"ldi r31,0\n"	// z^reg to save.
				"st Z,%4\n"	// store the command in the reg.
				"pop r31\n"
				"pop r30\n"	// restore the spm target address into Z.
                "pop RAMPZ\n"
				"ret\n"			// return to bootloader.
				// sts (2c)
				// spm (1c). 42c in total, timer should be set to 40.
				"SpmLeapCmdRet:pop %11\n"		// restore command Reg.
				"mov r30,%3\n"
				"ldi r31,0\n"	// z^reg to save.
				"st Z,%11\n"	// pop the reg
				"pop r31\n"
				"pop r30\n"
				"pop %11\n"
				"pop r1\n"
				"pop r0\n" : "=d" (tmp), "=r" (addr) : "I" (TCCR0B), "r" (cmdReg), "r" (spmCmd),
								"r" (optValue), "0" (addr), "M" (spmaddr.zl), "M" (spmaddr.zh),"M" (spmaddr.rz) ,"I" (SpmCsr), "d" (tmp) );
}

Note I know the loading and popping of RAMPZ is probably wrong and some suggestion to get this piece of code working would be great!!

Tried for 4 days so far but no luck, looking at the original code will show what i have changed but too much messing around to revert so just showing what i currently have.

Hope someone can help.

"I'll tolerate assignments in Loops, but this kind of lewd behavior in IF's will not be tolerated"

Last Edited: Mon. Oct 23, 2017 - 07:14 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Why do this as inline asm anyway? Why not just a function in a .S file? Also why so much Asm anyway? Things like address mannipulation in there can be done in C before passing to the Asm just to do the actual SPM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the OP is using inline assembly because that is what the original author of the code used, and he thinks converting the code to either C or to an external assembly routine will be more difficult than messing with the quirks of inline assembly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

El Tangas wrote:
will be more difficult than messing with the quirks of inline assembly
Wonder how that's working out for him? cheeky

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lol, You are right, not working very  well.

But unfortunately I'm well out of practice and haven't been in avr c/asm land for close to 7 years (last time i was active on this forum was 2009! was surprised my login was still good!) and am way to rusty to understand all but the basics anymore.

I'll love some help and if someone can help me re write in c and and asm or just get this working i'd be super grateful as I also don't have much time to get this working. I'll send flowers or beer :)

Even some quick re writes here for the bits i need which is currently the above and this below to pop the large address jump back in.

ISR(__vector_15, ISR_NAKED) // OCR0B
{
	asm volatile(
		"ldi r30,0\n"
		"out %0,r30\n"	// stop timer 0
		"out %1,r30\n"	// reset timer 0.
		"ldi r30,7\n"
		"out %2,r30\n"	// clear interrupts on timer 0.
		"pop r31\n"	// don't care about overwiting Z because SpmLeap doesn't need it.
		"pop r30\n"
		"reti\n" : : "I" (TCCR0B), "I" (TCNT0), "I" (TIFR0));
}

I can test and modify the cycle count after this is working.

"I'll tolerate assignments in Loops, but this kind of lewd behavior in IF's will not be tolerated"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This one is shorter, so I had the patience to test it. Turns out you have to pass the I/O addresses of the SFRs, not the SFRs themselves. That is, for example, (&TCCR0B-0x20) instead of (TCCR0B). Fortunatelly, there is a macro to do it in a more readable way:

 

ISR(__vector_15, ISR_NAKED) // OCR0B
{
    asm volatile(
    "push r30\n"
    "ldi r30,0\n"
    "out %0,r30\n"	/* stop timer 0 */
    "out %1,r30\n"	/* reset timer 0.*/
    "ldi r30,7\n"
    "out %2,r30\n"	/* clear interrupts on timer 0.*/
    "pop r30\n"
    "reti\n" : : "I" (_SFR_IO_ADDR(TCCR0B)), "I" (_SFR_IO_ADDR(TCNT0)), "I" (&TIFR0-0x20));
}

I didn't use the macro on %2 to illustrate it's the same thing.

Also, you need to balance push and pop in the ISR, or you will soon be in trouble. Since the ISR only uses r30, I saved and restored only that register.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks El Tangas!

That got that part compiling but unfortunately didn't help the first part yet.

Also, this interrupts specifically to pop the addresses off the stack and disregard the last one so it returns to the bootjack code just after the spm is called in the boot sector...or something like that :/, still a little confused as to the specifics of this. I know i don't want to push any pc on the stack here though, just pop what was placed before the timer interrupts.

Any ideas by just glancing on the first bit of posted code though?

Once i get it compiling i can start testing and cross by fingers for this to work.

"I'll tolerate assignments in Loops, but this kind of lewd behavior in IF's will not be tolerated"

Last Edited: Tue. Oct 24, 2017 - 02:35 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I'm testing but I need the definition of SpmCsr to be able to compile it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi El Tangas,

Thank you!!!!

It is:

#define SpmCsr 0x37

The full source is linked in the OP on github if required : https://github.com/unstableunico...

"I'll tolerate assignments in Loops, but this kind of lewd behavior in IF's will not be tolerated"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, problem is the constraints with capital "M", which means immediate 8bit constant, need to be replaced by "m", memory.

 

This allows compilation, but there are still assembly errors. This is because RAMPZ is not an actual general purpose register, but an I/O register, so it can't be used in instructions like push and pop.

 

edit: correct constrains - >

: "=d" (tmp), "=r" (addr) : "I" (_SFR_IO_ADDR(TCCR0B)), "r" (cmdReg), "r" (spmCmd),
	"r" (optValue), "0" (addr), "m" (spmaddr.zl), "m" (spmaddr.zh),"m" (spmaddr.rz) ,"I" (SpmCsr), "d" (tmp) );

 

Last Edited: Tue. Oct 24, 2017 - 11:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Big thanks to El Tangas! the help here and help from a local colleague we got this working! We can now overwrite the bootloader woot!

I have updated my repo linked in the OP to write some fixed data to a bootloader section.

Next step is to clean it up and make is a little more generic to work with other setups. This one is particular to larger addressing, when I get some time and a few other devices I'll will wrap it up so can be used on most avr's.

 

"I'll tolerate assignments in Loops, but this kind of lewd behavior in IF's will not be tolerated"

Last Edited: Mon. Oct 30, 2017 - 06:49 AM