GP2X Dangerous Linux Hacking Code

Discussion in 'General Discussions' started by Squidge, Jun 20, 2006.

  1. Squidge

    Squidge Certified Guru

    Joined:
    Nov 16, 2003
    Messages:
    8,495
    Location:
    UK
    The following is my MMU hacking code for changing the cachable, bufferable bits and everything else about the MMU page tables.

    Feel free to use in your own code, but you should give credit where its due, and if used in something commercial, I should get a free copy :)

    Code:
    #include <stdio.h>
    #include <signal.h>
    #include <setjmp.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    #include <linux/fb.h>
    #include <unistd.h>
    #include <stropts.h> 
    #include <string.h>
    
    extern int errno;
    
    int memfd;
    
    void *trymmap (void *start, size_t length, int prot, int flags, int fd, off_t offset)
    {
    	char *p;
    	int aa;
    
    	//printf ("mmap(%X, %X, %X, %X, %X, %X) ... ", (unsigned int)start, length, prot, flags, fd, (unsigned int)offset);
    	p = mmap (start, length, prot, flags, fd, offset);
    	if (p == (char *)0xFFFFFFFF)
    	{
    		aa = errno;
    		printf ("failed mmap(%X, %X, %X, %X, %X, %X) errno = %d\n", (unsigned int)start, length, prot, flags, fd, (unsigned int)offset, aa);
    	}
    	else
    	{
    		//printf ("OK! (%X)\n", (unsigned int)p);
    	}
    
    	return p;
    }
     
    unsigned char initphys (void)
    {
    	memfd = open("/dev/mem", O_RDWR);
    	if (memfd == -1)
    	{
    		printf ("Open failed\n");
    		return 0;
    	}
    
    	printf ("/dev/mem opened successfully - fd = %d\n", memfd);
    
    	return 1;
    }
    
    void closephys (void)
    {
    	close (memfd);
    }
    
    int myuname(char *buffer)
    {
    	asm volatile ("swi #0x90007a");
    }
    
    void DecodeCoarse (unsigned int indx, unsigned int sa)
    {
    	unsigned int cpt[256];
    	unsigned int dom = (sa >> 5) & 15;
    	unsigned int temp;
    	unsigned int i = 0;
    	unsigned int wb = 0;
    	
    	sa &= 0xfffffc00;
    	indx *= 1048576;
    	
    	//printf ("  > %08X\n", sa);
    	//printf ("%d\n", 
    	lseek (memfd, sa, SEEK_SET);
    	memset (cpt, 0, 256*4);
    	temp = read (memfd, cpt, 256*4);
    	//printf ("%d\n", temp);
    	if (temp != 256*4)
    	{
    		printf ("  # Bad read\n");
    		return;
    	}
    
    	//printf ("%08X %08X %08X %08X\n", cpt[0], cpt[4], cpt[8], cpt[12]);
    	
    	for (i = 0; i < 256; i ++)
    	{
    		if (cpt[i])
    		{
    			switch (cpt[i] & 3)
    			{
    				case 0:
    					//printf ("  -- [%08X] Invalid --\n", cpt[i]);
    					break;
    				case 1:
    					printf ("  VA: %08X PA: %08X - %08X A: %d %d %d %d D: %d C: %d B: %d\n", indx,
    							cpt[i] & 0xFFFF0000, (cpt[i] & 0xFFFF0000) | 0xFFFF,
    							(cpt[i] >> 10) & 3, (cpt[i] >> 8) & 3, (cpt[i] >> 6) & 3, 
    							(cpt[i] >> 4) & 3, dom, (cpt[i] >> 3) & 1, (cpt[i] >> 2) & 1); 
    					break;
    				case 2:
    					printf ("  VA: %08X PA: %08X - %08X A: %d %d %d %d D: %d C: %d B: %d\n", indx,
    							cpt[i] & 0xfffff000, (cpt[i] & 0xfffff000) | 0xFFF,
    							(cpt[i] >> 10) & 3, (cpt[i] >> 8) & 3, (cpt[i] >> 6) & 3, 
    							(cpt[i] >> 4) & 3, dom, (cpt[i] >> 3) & 1, (cpt[i] >> 2) & 1); 
    					// This is where we look for any virtual addresses that map to physical address 0x03000000 and
    					// alter the cachable and bufferable bits...
    					if (((cpt[i] & 0xffff0000) == 0x03000000) && ((cpt[i] & 12)==0))
    					{
    						//printf("c and b bits not set, overwriting\n");
    						cpt[i] |= 0xFFC;
    						wb = 1;
    					}
    					break;
    				case 3:
    					//printf ("  -- [%08X/%d] Unsupported --\n", cpt[i],cpt[i] & 3);
    					break;
    				default:
    					//printf ("  -- [%08X/%d] Unknown --\n", cpt[i], cpt[i] & 3);
    					break;
    			}
    		}
    		indx += 4096;
    	}
    	//printf ("%08X %08X %08X %08X\n", cpt[0], cpt[4], cpt[8], cpt[12]);
    	if (wb) 
    	{
    		//printf("Hacking cpt\n");
    		lseek (memfd, sa, SEEK_SET);
    		temp = write (memfd, cpt, 256*4);
    		printf("%d bytes written, %s\n", temp, temp == 1024 ? "yay!" : "oh fooble :(!");
    	}
    }
    
    void dumppgtable (unsigned int ttb)
    {
    	unsigned int pgtable[4096];
    	char *desctypes[] = {"Invalid", "Coarse", "Section", "Fine"}; 
    
    	memset (pgtable, 0, 4096*4);
    	lseek (memfd, ttb, SEEK_SET);
    	read (memfd, pgtable, 4096*4);
    
    	int i;
    	for (i = 0; i < 4096; i ++)
    	{
    		int temp;
    		
    		if (pgtable[i])
    		{
    			printf ("Indx: %d VA: %08X Type: %d [%s] \n", i, i * 1048576, pgtable[i] & 3, desctypes[pgtable[i]&3]);
    			switch (pgtable[i]&3)
    			{
    				case 0:
    					//printf (" -- Invalid --\n");
    					break;
    				case 1:
    					DecodeCoarse(i, pgtable[i]);
    					break;
    				case 2:
    					temp = pgtable[i] & 0xFFF00000;
    					//printf ("  PA: %08X - %08X A: %d D: %d C: %d B: %d\n", temp, temp | 0xFFFFF, 
    					//		(pgtable[i] >> 10) & 3, (pgtable[i] >> 5) & 15, (pgtable[i] >> 3) & 1, 
    					//		(pgtable[i] >> 2) & 1);
    							
    					break;
    				case 3:
    					printf ("  -- Unsupported! --\n");
    					break;
    			}
    		}
    	}
    }
    
    void benchmark (void *memptr)
    {
    	int starttime = time (NULL);
    	int a,b,c,d;
    	volatile unsigned int *pp = (unsigned int *) memptr;
    
    	while (starttime == time (NULL));
    
    	printf ("\n\nmemory benchmark of volatile VA: %08X\n\nread test\n", memptr);
    	for (d = 0; d < 3; d ++) 
    	{
    		starttime = time (NULL);
    		b = 0;
    		c = 0;
    		while (starttime == time (NULL))
    		{
    			for (a = 0; a < 20000; a ++) 
    			{
    				b += pp[a];
    			}
    			c ++;
    		}
    		printf ("Count is %d. %dMB/sec\n",  c, (c * 20000)/1024/1024);
    	}
    
    	printf ("write test\n");
    	for (d = 0; d < 3; d ++) 
    	{
    		starttime = time (NULL);
    		b = 0;
    		c = 0;
    		while (starttime == time (NULL))
    		{
    			for (a = 0; a < 20000; a ++) 
    			{
    				pp[a] = 0x37014206;
    			}
    			c ++;
    		}
    		printf ("Count is %d. %dMB/sec\n",  c, (c * 20000)/1024/1024);
    	}
    
    	printf  ("combined test (read, write back)\n");
    	for (d = 0; d < 3; d ++) 
    	{
    		starttime = time (NULL);
    		b = 0;
    		c = 0;
    		while (starttime == time (NULL))
    		{
    			for (a = 0; a < 20000; a ++) 
    			{
    				pp[a] += 0x55017601;
    			}
    			c ++;
    		}
    		printf ("Count is %d. %dMB/sec\n",  c, (c * 20000)/1024/1024);
    	}
    
    	printf ("test complete\n");
    }
    
    void hackpgtable (void)
    {
    	unsigned int oldc1, oldc2, oldc3, oldc4;
    	unsigned int newc1 = 0xee120f10, newc2 = 0xe12fff1e;
    	unsigned int ttb, ttx;
    	char name[256];
    
    	// We need to execute a "MRC p15, 0, r0, c2, c0, 0", to get the pointer to the translation table base, but we can't 
    	// do this in user mode, so we have to patch the kernel to get it to run it for us in supervisor mode. We do this 
    	// at the moment by overwriting the sys_newuname function and then calling it.
    
    	lseek (memfd, 0x6ec00, SEEK_SET); // fixme: We should ask the kernel for this address rather than assuming it... 
    	read (memfd, &oldc1, 4);
    	read (memfd, &oldc2, 4);
    	read (memfd, &oldc3, 4);
    	read (memfd, &oldc4, 4);
    
    	printf ("0:%08X %08X\n", oldc1, oldc2);
    
    	lseek (memfd, 0x6ec00, SEEK_SET); 
    	write (memfd, &newc1, 4);
    	write (memfd, &newc2, 4);	
    	
    	ttb = myuname(name);
    	
    	lseek (memfd, 0x6ec00, SEEK_SET); 
    	write (memfd, &oldc1, 4);
    	write (memfd, &oldc2, 4);	
    
    	printf ("1:%08X\n", ttb);
    
    	//printf ("Restored contents\n");
    	
    	// We now have the translation table base ! Walk the table looking for our allocation and hack it :)
    	dumppgtable(ttb);	
    
    	// Now drain the write buffer and flush the tlb caches. Something else we can't do in user mode...
    	unsigned int tlbc1 = 0xe3a00000; // mov	r0, #0
    	unsigned int tlbc2 = 0xee070f9a; // mcr	15, 0, r0, cr7, cr10, 4
    	unsigned int tlbc3 = 0xee080f17; // mcr	15, 0, r0, cr8, cr7, 0
    	unsigned int tlbc4 = 0xe1a0f00e; // mov	pc, lr
    
    	lseek (memfd, 0x6ec00, SEEK_SET); 
    	write (memfd, &tlbc1, 4);
    	write (memfd, &tlbc2, 4);	
    	write (memfd, &tlbc3, 4);	
    	write (memfd, &tlbc4, 4);	
    	
    	ttx = myuname(name);
    	
    	//printf ("Return from uname: %08X\n", ttx);
    	
    	lseek (memfd, 0x6ec00, SEEK_SET); 
    	write (memfd, &oldc1, 4);
    	write (memfd, &oldc2, 4);	
    	write (memfd, &oldc3, 4);	
    	write (memfd, &oldc4, 4);	
    
    	//printf ("Restored contents\n");
    
    	//printf ("Pagetable after modification!\n");
    	//printf ("-------------------------------\n");
    	//dumppgtable(ttb);
    }
    
    int
    main( int argc, char* argv[] )
    {	
    	if (!initphys())
    		return 0;
    
    	volatile unsigned int *myBuf = trymmap((void *)0, 65536, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0x03000000);
    	volatile unsigned int *secbuf = (unsigned int *)malloc (204800);
    
    	//memset ((void *)myBuf, 0x55, 65536);
    	//memset ((void *)secbuf, 0x55, 65536);
    
    	printf("mmaped 0x03000000 buffer @ VA: %08X malloc'd buffer @ VA: %08X\n", myBuf, secbuf);
    
    	hackpgtable();
    
    	//benchmark ((void*)myBuf);
    	//benchmark ((void*)secbuf);
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////
    	printf ("\n\nCleaning up...\n");	
    	printf ("Closing files...\n");
    	close (memfd);
    	printf ("Exiting...\n");
    	
    	return 0;		
    } 
    
    If you require documentation and/or support, then please don't hesitate to fill up my paypal balance :D
     
    Tags:
  2. deadlychicken22

    deadlychicken22 Man is a reasoning rather than a reasonable animal

    Joined:
    Mar 18, 2004
    Messages:
    1,501
    Location:
    MN, USA
    I don't know what this does, but it sounds important . . .

    Good job, Squidge!
     
  3. Tobriand

    Tobriand Very Active Member

    Joined:
    Dec 27, 2002
    Messages:
    4,071
    Location:
    Croydon (UK)
    I think, if I'm right, it makes Linux aware that the machine actually has 64mb of ram and that it can fiddle with it. Presumably breaking video playback and default 940t operations in the process, given that that's currently how people seem to be able to talk to the second processor. Not from a programmers point of view, incidentally, so if you're looking to use this do not go on my assumptions, but that's what it sounds like in terms of changing which bits are cacheable.

    In any event, it's very cool, from the sound of things, and, if it does what I think it might be for, eliminates (potentially) one more of DaveC's (absolutely warrented, but sometimes a little overstated) complaints about the system :)
     
  4. Dzz

    Dzz stmia r0!, {r2-r9}

    Joined:
    Jan 30, 2006
    Messages:
    1,098
    That's a pretty fun hack, nice job! I wouldn't have thought of overwriting the code of a system call like that; it could have other uses too! For the system call you chose, do you know how much room is available?

    The 920 could share memory with the 940 just fine before this, but that memory would always be uncached on the 920, making access to it slow. With this hack that memory can become cached which should be helpful for complex programs looking to share memory areas heavily accessed by both processors.
     
  5. Purist

    Purist I'm stupid. Nothing more!

    Joined:
    Jan 17, 2006
    Messages:
    238
    Location:
    California
    So, what does this do? Make both processors equally usable with all the RAM?
     
  6. Dzz

    Dzz stmia r0!, {r2-r9}

    Joined:
    Jan 30, 2006
    Messages:
    1,098
    No, it has nothing directly to do with the second processor. It allows the first processor to make some changes to the way it accesses memory. That memory could be shared with the second processor. Using this technique a program that needs a LOT of fast memory (more than the 25ish megabytes normally available) could get fast access to the upper 32mb which would normally be slow.
     
    Last edited by a moderator: Dec 17, 2015
  7. BradN

    BradN Member

    Joined:
    Jun 22, 2003
    Messages:
    577
    Squidge, have you tested if this actually breaks the video player, or actually speeds it up? Or is it not applicable because the video player ends up resetting the settings anyway?
     
  8. Squidge

    Squidge Certified Guru

    Joined:
    Nov 16, 2003
    Messages:
    8,495
    Location:
    UK
    Ok, some infos:

    Each application runs in it's own memory space with it's own mmu tables, so this code must be included with any program that wants to modify it's mmu tables (for eg. fast access to memory >32mb). Programs running without this hack included will run as normal.

    No, it doesn't break the video player unless you happen to overwrite memory that is reserved by the second processor (ie. exactly the same as before), as the changes to the mmu tables only apply to the currently running program. Any program not including this code will run as normal, with slow access to >32mb, even if another program using this hack is running in the background.

    The kernel code that is overwritten (sys_newuname) isn't very big in size (it's basically just copies a string into the user provided buffer and returns), so if you want to run a lot of code as supervisor, it would be best if you used kmalloc to allocate kernel memory; copied your code there and branched to that. Only problem is that kmalloc is only available to the kernel, so you need to use a similar technique to what is used here to use that function. You could branch to a function in user land instead, but don't forget that if linux does a task switch on you whilst your in that code, and another task calls sys_newuname, expect a nice big kernel panic. One way of stopping this is disabling interrupts in your new code, but that will screw up timers/etc if you take too long.

    The reason I chose sys_newuname for overwriting is that it seems to be the least used system call on Linux - there's little chance of a task switch happening and another function calling that routine, as most programs simply don't care about the os they are running on.
     
  9. MadDog

    MadDog Member

    Joined:
    Mar 4, 2006
    Messages:
    262
    Location:
    UK
    Am I correct in saying this has no effect to the amount of memory avalible on the heap?
    All it does is change the cache mode to the upper 32megs?
    So to use this mem in an emulator your still need to manage it your self, but at least its at full speed now? :)

    Very cleaver mate!

    But is it not a bug in gp2x Linux? Should not the kernal code should be protected as its memory is?
     
  10. Squidge

    Squidge Certified Guru

    Joined:
    Nov 16, 2003
    Messages:
    8,495
    Location:
    UK
    Yes.

    Yes.

    Yes :)

    No, The kernel code is protected as per usual. I use /dev/mem to access the physical memory, and this only works if the code is running as the superuser (root), which is the default on the gp2x. You can't mmap the kernel code either, which is why I use lseek, write, etc instead.

    Also, for completeness, the address of sys_newuname was found by looking through /proc/ksym, and there are kernel functions available if you wish to do this automatically - I'm just idle :)
     
    Last edited by a moderator: Dec 17, 2015
  11. MadDog

    MadDog Member

    Joined:
    Mar 4, 2006
    Messages:
    262
    Location:
    UK
    Ah, I think I get it, still a bit new at this Linux lark. Sorry, a bit offtopic, but if the gp2x was using Linux in the normal way I would be logged in as a normal user and this 'hack' would fail?

    (Guess this is why Windows has problems as most login as administrator letting naughty progs to install stuff as they see fit.)
     
    Last edited by a moderator: Dec 17, 2015
  12. Squidge

    Squidge Certified Guru

    Joined:
    Nov 16, 2003
    Messages:
    8,495
    Location:
    UK
    Yup, if you create a non-superuser, login with that, and then run this hack, it'll fail to open /dev/mem and bail out.
     
  13. DavidBeoulve

    DavidBeoulve GP2X by EvilDragon OC's to 280-300MHz

    Joined:
    May 10, 2006
    Messages:
    427
    Location:
    USA
    Awesome work, Squidge! You're one of my GP2X heroes.
     
    Last edited by a moderator: Dec 17, 2015
  14. Franxis

    Franxis MAME 4 ALL

    Joined:
    Aug 22, 2004
    Messages:
    788
    Location:
    Spain
    Yuhuuuu! Thx Squidge! :D Let's play with upper cached memory and hardware blitter! :lol:
     
  15. Parkydr

    Parkydr Developing for the GP2X since 2006

    Joined:
    Dec 3, 2005
    Messages:
    1,360
    Unless you set the setuid bit on your program
     
    Last edited by a moderator: Dec 17, 2015
  16. Franxis

    Franxis MAME 4 ALL

    Joined:
    Aug 22, 2004
    Messages:
    788
    Location:
    Spain
    I have changed this line in your code to make it to run:
    Code:
    if (((cpt[i] & 0xff000000) == 0x03000000) && ((cpt[i] & 12)==0))
    
    The interesting part of the terminal output is here:
    Code:
    0:E1A0C00D E92DD810
    1:0062D3BC
      VA: CAFD7000 PA: 03070000 - 03070FFF A: 0 0 0 0 D: 7 C: 0 B: 0
    1024 bytes written, yay!
    
    I have used the hardware blitter in this way:
    Code:
    static void update_display (void)
    {
    	gp2x_2dregs[0x4 >> 2] = gp2x_physvram[gp2x_nflip]+gp2x_xscreen+(320*gp2x_yscreen); /* Destination */
    	gp2x_2dregs[0x0] = (0<<5); /* Destination is 8 bpp */
    	gp2x_2dregs[0x8 >> 2] = 320; /* Destination stride size in bytes */
    		gp2x_2dregs[0xC >> 2] = (1 << 8) | (1 << 7) | (0 << 5); /* if this works/doesnt, try with bit 7 clear. */
    	gp2x_2dregs[0x10 >> 2] = gp2x_video_buffer_physical + (scrbitmap->line[gp2x_yoffset]+gp2x_xoffset)-((unsigned char *)scrbitmap->_private); /* Source address */
    	gp2x_2dregs[0x14 >> 2] = scrbitmap->line[1] - scrbitmap->line[0]; /* Source stride */
    	gp2x_2dregs[0x2c >> 2] = ((gp2x_height) << 16) | (gp2x_width); /* Height and width to blit */
    	gp2x_2dregs[0x30 >> 2] = (1<<11) | (1<<9) | (1<<8 ) | 0xcc; /* Fill from top left to bottom right */
    	__asm__ __volatile__ ("" : : :"memory");
    	gp2x_2dregs[0x34 >> 2] = 1; 
    }	
    
    
    Everything is working fine, but... i have compared at 200 MHz the old MAME GP2X 2.3 (without cached upper memory and without hardware blitter) and this test (with both cached upper memory and hardware blitter) and unfortunately i don't see speed improvements :( :( :( .
     
  17. Franxis

    Franxis MAME 4 ALL

    Joined:
    Aug 22, 2004
    Messages:
    788
    Location:
    Spain
    OK!!!!!. I have it working now :lol:

    1) I opened "/dev/mem" twice in MAME (one with minimal lib and one with the MMU hack functions). I have solved it.

    2) I have done new tests with this modification in the code:
    Code:
    					if (((cpt[i] & 0xff000000) == 0x03000000) && ((cpt[i] & 12)==0))
    					{
    						cpt[i] |= 0xFFC;
    						wb = 1;
    					}
    					else if (((cpt[i] & 0xff000000) == 0x02000000) && ((cpt[i] & 12)==0))
    					{
    						cpt[i] |= 0xFFC;
    						wb = 1;
    					}
    
    The memory areas i had in my tests were in 0x3000000, but they were listed as 0x2000000 in MMU caching tables (?). I have run Metal Slug in MAME (it uses a lot of high memory) and i have notice the speed improvement thanks to the MMU caching :D :D :D ...

    Regards.
     
  18. Squidge

    Squidge Certified Guru

    Joined:
    Nov 16, 2003
    Messages:
    8,495
    Location:
    UK
  19. washo

    washo Washo 4 evr

    Joined:
    Oct 3, 2003
    Messages:
    1,126
    Location:
    France (Marseille)
    YAY...speed...more speed....YAY
    You are reallly amazing
    Thanks

    see you :lol:
     
  20. Vimacs

    Vimacs Don't be evil!

    Joined:
    Oct 23, 2003
    Messages:
    5,211
    Location:
    Germany
    how much of a speed improvement are we talking about?
     

Share This Page

Loading...