GP2X Rebooting Gp2x


Robster

Dodgy hardware mod maker
Joined
Jul 8, 2003
Messages
439
Location
New Zealand
Website
www.cobbleware.com
Hey all,

This will reboot the GP2X (rather than just resetting it):
Code:
	/* Mask ALL interrupts */
MSP_INTMASK = 0xFF8FFFE7;

/* Disable the clock into the timer module */
MSP_SYSCLKENREG &= (~0x80);

/* Set reboot behaviour when watchdog reset occurs */
MSP_BOOTCTRLREG = 4;

/* Clear any timer interrupts that may be pending, and enable the
 * interrupt in the timer module */
MSP_TSTATUS = 8;
MSP_TINTEN = 8;
MSP_SRCPEND = 32;
MSP_INTPEND = 32;

/* Enable the watchdog reset */
MSP_RSTCTRLREG = 0x100;

/* Enable the Timer module interrupt */
MSP_INTMASK &= (~0x20);

/* Set the watchdog compare to fire in 50 counts, or 6.78us at 7.3728MHz */
MSP_TMATCH3 = MSP_TCOUNT + 50;

/* Set match 3 to be watchdog reset rather than an interrupt */
MSP_TCONTROL = 3;

/* Enable the clock into the timer module */
MSP_SYSCLKENREG |= 0x80;

/* Idle, waiting for reset */
while (1);
Seems OK to me, using the watchdog timer is a bit crusty but I couldn't see any better ways. And I think it beats keeping some part of U-Boot in memory so an app can return to it.

Thoughts?

Damn, I've got to start putting this stuff up on cobbleware :/
 
tbh, from my experience with arm cores - a watchdog reset is the only reset that actually pulls down the reset line to the processor, so is the only guaranteed way.

I like how you disable interrupts - I'm not sure about the mmsp2, but I think normally the processor has a watchdog interrupt that gets fired before the reset, so that would be the only way to get out.

Nice work :)
 
As a little extension to this, I thought we could "escape" from Linux by altering the boot vector and then resetting, rather than rebooting. The following program shows what I mean, and I think it works but it will need a full CPU setup before it will show any life. At the moment, it certainly kills the unit (LCD shuts down etc) but I don't know 100% if it is actually working.
Code:
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <errno.h>
#include "mmsp2.h"

unsigned short *screendw1;
unsigned int *screendw2;
int Gfxfd;

char buf[100];

void *trymmap (void *start, size_t length, int prot, int flags, int fd, off_t offset)
{
   char *p;
   int aa;

   sprintf (buf,"mmap(%X, %X, %X, %X, %X, %X) ... ", (unsigned int)start, length, prot, flags, fd, (unsigned int)offset);
   printf (buf);
   p = mmap (start, length, prot, flags, fd, offset);
   if (p == (char *)0xFFFFFFFF)
   {
      aa = errno;
      sprintf (buf,"failed. errno = %d\n", aa);
   }
   else
      sprintf (buf,"OK! (%X)\n", (unsigned int)p);

   printf (buf);

   return p;
}

void reentry (void)
{
   int i;

   while (1)
   {
      MSP_THB0 = '1';
      for (i=0;i<1000000;i++);
   }
}

#define OFF_SRCPEND  	(0x0400)
#define OFF_INTMASK  	(0x0404)
#define OFF_INTPEND  	(0x0408)
#define OFF_SYSCLKENREG  (0x0482)
#define OFF_TCOUNT  	(0x0500)
#define OFF_TMATCH3  	(0x0508)
#define OFF_TCONTROL  	(0x050A)
#define OFF_TSTATUS  	(0x050B)
#define OFF_TINTEN  	(0x050C)
#define OFF_RSTCTRLREG  (0x060A)
#define OFF_BOOTCTRLREG  (0x060C)


int main(int argc, char **argv)
{
   int i;

   /* Big-ass initial delay, to let everything settle */
   for (i=0;i<10000000;i++);

   Gfxfd = open("/dev/mem", O_RDWR);
   if (Gfxfd == -1)
   {
      printf ("Open failed\n");
      return 0;
   }

   sprintf (buf,"/dev/mem opened successfully - fd = %d\n", Gfxfd);
   printf (buf);

   screendw1 = (unsigned short *)trymmap(0, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, Gfxfd, 0xc0000000);
   if (screendw1 == (unsigned short *)0xFFFFFFFF)
      return 1;

   screendw2 = (unsigned int *)trymmap(0, 4, PROT_READ | PROT_WRITE, MAP_SHARED, Gfxfd, 0);
   if (screendw2 == (unsigned int *)0xFFFFFFFF)
      return 1;

   for (i=0;i<100000000;i++);

   *screendw2 = (unsigned int)reentry;

   /* Mask ALL interrupts */
   *((unsigned int *)(&screendw1[OFF_INTMASK])) = 0xFF8FFFE7;
	
   /* Disable the clock into the timer module */
   screendw1[OFF_SYSCLKENREG] &= (~0x80);
	
   /* Set reboot behaviour when watchdog reset occurs */
   screendw1[OFF_BOOTCTRLREG] = 7;
	
   /* Clear any timer interrupts that may be pending, and enable the
    * interrupt in the timer module */
   screendw1[OFF_TSTATUS] = 8;
   screendw1[OFF_TINTEN] = 8;
   screendw1[OFF_SRCPEND] = 32;
   screendw1[OFF_INTPEND] = 32;
	
   /* Enable the watchdog reset */
   screendw1[OFF_RSTCTRLREG] = 0x100;

   /* Enable the Timer module interrupt */
   screendw1[OFF_INTMASK] &= (~0x20);
	
   /* Set the watchdog compare to fire in 50 counts, or 6.78us at 7.3728MHz */
   *((unsigned int *)(&screendw1[OFF_TMATCH3])) = (*((unsigned int *)(&screendw1[OFF_TCOUNT]))) + 50;
	
   /* Set match 3 to be watchdog reset rather than an interrupt */
   screendw1[OFF_TCONTROL] = 3;
	
   /* Enable the clock into the timer module */
   screendw1[OFF_SYSCLKENREG] |= 0x80;
	
   /* Idle, waiting for reset */
   for (i=0;i<100000000;i++);

   printf ("Well that was a flop... :(");
}
 
The problem is that when we are in Linux, we are running in user-mode with the MMU enabled, so how to disable the MMU and re-enter svc mode?

Actually, it seems you are performing a full reset via watchdog, but wouldn't this just go and fetch uboot from the nand again?

Or am I barking up completely the wrong tree?
 
Back
Top