/*
* asmloader.c
*
* Linux Loader for the Palm OS.
*
*
* Copyright (C) 2004 Vinayak R. Borkar
* Author: Vinayak R. Borkar [vinayakb@users.sourceforge.net]
*
* Original version by Romain Goyet [r.goyet@gmail.com]
*
* 11-Sep-2005, Alex Osborne [bobofdoom@gmail.com]
* Updated to work with multiple handhelds.
*
* Based on the work of:
* Nikolay Petukhov - Palm Tungsten T2
* Vladimir Pouzanov - Palm Tungsten T3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <PceNativeCall.h>
#include <Standalone.h>
//#include <ByteOrderUtils.h>
#include "garux.h"
#include "pxa-regs.h"
#define SET_TAG_WORD(off, value) *((unsigned long *)((RAM_BASE_ADDRESS + 0x100)+((off)<<2)))=value
#define SET_TAG_WORD_BYTE(off, byte, value) *((char *)((RAM_BASE_ADDRESS + 0x100)+((off)<<2)+(byte)))=value
STANDALONE_CODE_RESOURCE_ID (0);
void startKernel();
void write_atags(char *cmdline);
unsigned long bootLinux(const void *emulStateP, char *userData68KP, Call68KFuncType *call68KFuncP) {
unsigned long register i;
unsigned long register j;
unsigned long register krnlSrc;
char *cmdline;
cmdline = (char*)(userData68KP);
krnlSrc = (unsigned long)userData68KP+256;
// Disable FIQ, IRQ
asm volatile ("mrs r0, cpsr");
asm volatile ("orr r0, r0, #0xc0");
asm volatile ("msr cpsr, r0");
// Machine-specific initialization
#if MACHINE_CPU=='OMAP'
// OMAP-based PDA
#if MACHINE_NAME=='ptt2'
// Palm Tungsten T2
//io ports settings for uart3
*((unsigned long*)0xfffe1044)=0xc9fbffff;
*((unsigned long*)0xfffce004)=0x00003a0e;
//Reset for disable dma LCD
//irq reset
*((unsigned long*)0xfffecb00)=0x0;
*((unsigned long*)0xfffecb04)=0xffffffff;
//dmaGlobal reset
*((unsigned short*)0xfffedb00)=0x0;
*((unsigned short*)0xfffedc00)=0x8;
// Set Page table entries, On palm tt2 page table place in sram.
*((unsigned long*)0x20010400)=0x10000c1a;
*((unsigned long*)0x20010440)=0x11000c1a;
#elif MACHINE_NAME=='pte'
// Palm Tungsten E
// Set Page table entries for 0x10000000 and 0x11000000 to identity.
*((unsigned long *)0x4400)=0x10000c1a;
*((unsigned long *)0x4440)=0x11000c1a;
#endif
#elif MACHINE_CPU=='XSCL'
// XScale-based PDA
// shut off devices to prevent side-effects of DMA corruption
// these will be re-enabled by the kernel drivers.
#if MACH_NAME != 'ptc' //With palmtc, shut off LDC crashes garux
// This needs to be fixed
LCCR0 &= ~LCCR0_ENB; // shut off LCD controller
#endif
GCR |= ~GCR_ACLINK_OFF; // shut off the AC97 audio controlller
// reset IRQs in the hope of fixing the bitops.h crash.
// from kernel26/arch/arm/mach-pxa/irq.c
/* disable all IRQs */
ICMR = 0;
/* all IRQs are IRQ, not FIQ */
ICLR = 0;
/* clear all GPIO edge detects */
GFER0 = 0;
GFER1 = 0;
GFER2 = 0;
GRER0 = 0;
GRER1 = 0;
GRER2 = 0;
GEDR0 = GEDR0;
GEDR1 = GEDR1;
GEDR2 = GEDR2;
// this does not seem to be required, but if problems occur
// add it in.
// Set Page table entries for 0xA0000000 and 0xA1000000 to identity.
/* *((unsigned long *)0x6800)=0xA0000c1a;
// *((unsigned long *)0x6804)=0xA0100c1a;
// *((unsigned long *)0x6802)=0xA0080c1a;
*((unsigned long *)0x6820)=0xA0800c1a;*/
// *((unsigned long *)0x6824)=0xA0900c1a;
/* *((unsigned long *)0x6804)=0xA0100C1A;
*((unsigned long *)0x6808)=0xA0200C1A;
*((unsigned long *)0x680C)=0xA0300C1A;*/
/* *((unsigned long *)0x6814)=0xA0500C1A;
*((unsigned long *)0x6818)=0xA0600C1A;
*((unsigned long *)0x681C)=0xA0700C1A;*/
// *((unsigned long *)0x6820)=0xA0C00C1A;
/* *((unsigned long *)0x6824)=0xA0900C1A;
*((unsigned long *)0x6828)=0xA0A00C1A;
*((unsigned long *)0x682C)=0xA0B00C1A; */
// *((unsigned long *)0x7400)=0xA0E00C1A;
/*
*((unsigned long *)0x6834)=0xA0D00C1A;
*((unsigned long *)0x6838)=0xA0E00C1A;
*((unsigned long *)0x683C)=0xA0F00C1A;*/
/* *((unsigned long *)0x6840)=0xA1000C1A;
*((unsigned long *)0x6844)=0xA1100C1A;
*((unsigned long *)0x6848)=0xA1200C1A;
*((unsigned long *)0x684C)=0xA1300C1A;
*((unsigned long *)0x6850)=0xA1400C1A;
*((unsigned long *)0x6854)=0xA1500C1A;
*((unsigned long *)0x6858)=0xA1600C1A;
*((unsigned long *)0x685C)=0xA1700C1A;
*((unsigned long *)0x6860)=0xA1800C1A;
*((unsigned long *)0x6864)=0xA1900C1A;
*((unsigned long *)0x6868)=0xA1A00C1A;
*((unsigned long *)0x686C)=0xA1B00C1A;
*((unsigned long *)0x6870)=0xA1C00C1A;
*((unsigned long *)0x6874)=0xA1D00C1A;
*((unsigned long *)0x6878)=0xA1E00C1A;
*((unsigned long *)0x687C)=0xA1F00C1A;*/
*((unsigned long *)0x6800)=0xa0000c1a;
*((unsigned long *)0x6804)=0xa0100c1a;
*((unsigned long *)0x6834)=0xa0d00c1a;
*((unsigned long *)0x6838)=0xa0e00c1a;
*((unsigned long*)(0x92000000)) &= ~LCCR0_ENB;
// *((unsigned long *)0x6800)=0xA0000C02;
/* *((unsigned long *)0x6800)=0xA0800C1A;
*((unsigned long *)0x6800)=0xA0000C1A; */
//*((unsigned long*)(0x92000000)) &= ~LCCR0_ENB;
// *((unsigned long *)0x92000000) &= ~LCCR0_ENB; // shut off LCD controller
#if MACHINE_NAME=='pld'
// Palm LifeDrive
/* And similarly for the extra regs on the PXA27x */
ICMR2 = 0;
//ICLR2 = 0;
//GFER3 = 0;
//GRER3 = 0;
//GEDR3 = GEDR3;
#endif
#endif
// Let's flush Instruction and Data cache
asm volatile ("mov r0, #0");
asm volatile ("mcr p15, 0, r0, c7, c7, 0"); // flush v3/v4 cache
asm volatile ("mcr p15, 0, r0, c8, c7, 0"); // flush v4 TLB
// Wait a bit.
for(i=0 ; i<100000 ; i++);
// Copy the kernel to memory starting at MEM_BASE_ADDRESS
// In reality we want to copy the image to MEM_BASE_ADDRESS + 0x8000, but
// that location holds a second level page table. So if we try to copy directly
// to the final destination, the system crashes due to a corrupted second level
// page table. So we temporarily copy it way out into MEM_BASE_ADDRESS + 0x1000000
// first.
//
// Assumption: 0x1000000 is way out and does not trample on either
// the code nor kernel data.
for(i=0 ; i<NUM_KERNEL_PAGES*PAGE_SIZE ; i+=4) {
*((unsigned long *)(RAM_BASE_ADDRESS+0x01000000+i))=*((unsigned long *)(krnlSrc+i));
}
write_atags(cmdline);
// FIXME: We arbitrarily add 0x60 to pc and copy 512 bytes starting
// from there. So if any code is modified between <CRITICAL>
// and </CRITICAL>, the offset needs to change.
// <CRITICAL>
asm volatile ("add %0, pc, #0x60" : "=r" (j));
for(i=0 ; i<512 ; i++) {
*((char *)(RAM_BASE_ADDRESS+0x00001000+i))=*((char *)(i+j));
}
asm volatile ("mov lr, %0;"
"add pc, lr, #0x00001000;" : : "i"(RAM_BASE_ADDRESS));
// </CRITICAL>
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
// Let's flush Instruction and Data cache
asm volatile ("mov r0, #0");
asm volatile ("mcr p15, 0, r0, c7, c7, 0"); // flush v3/v4 cache
asm volatile ("mcr p15, 0, r0, c8, c7, 0"); // flush v4 TLB
// Wait a bit.
for(i=0 ; i<100000 ; i++);
// Let's disable the MMU and disable data cache.
asm volatile ("mrc p15, 0, r0, c1, c0, 0");
asm volatile ("bic r0, r0, #0x00002300"); // clear bits 13, 9:8 (--V- --RS)
asm volatile ("bic r0, r0, #0x00000087"); // clear bits 7, 2:0 (B--- -CAM)
asm volatile ("orr r0, r0, #0x00001000"); // set bit 12 (I) I-Cache
asm volatile ("mcr p15, 0, r0, c1, c0, 0");
// invalidate TLB
asm volatile ("mov r0, #0");
asm volatile ("mcr p15, 0, r0, c8, c7, 0");
// Wait a bit.
for(i=0 ; i<100000 ; i++);
// Actually copy the kernel
for(i=0 ; i<NUM_KERNEL_PAGES*PAGE_SIZE ; i+=4) {
*((unsigned long *)(RAM_BASE_ADDRESS + 0x0008000+i))=*((unsigned long*)(RAM_BASE_ADDRESS+0x01000000+i));
}
// Copy initrd
for(i=0 ; i<SIZE_INITRD ; i+=4) {
*((unsigned long *)(RAM_BASE_ADDRESS + 0x0400000+i))=*((unsigned long*)(RAM_BASE_ADDRESS+0x01000000+SIZE_KERNEL+i));
}
// Wait a bit.
for(i=0 ; i<10000000 ; i++);
// Initializing before boot
asm volatile ("mov r0, #0");
asm volatile ("mov r1, %0" : : "i"(MACHINE_ID_LINUX & 0xFF));
asm volatile ("orr r1, r1, %0" : : "i"(MACHINE_ID_LINUX & 0xF00));
// ATAGs location
asm volatile ("mov r2, %0" : : "i"(RAM_BASE_ADDRESS));
asm volatile ("add r2, r2, #0x00000100");
// Jump to the kernel.
asm volatile ("mov r3, #0x00008000");
asm volatile ("orr r3, r3, %0": : "i"(RAM_BASE_ADDRESS));
asm volatile ("mov pc, r3");
// We should never get here... Just to make the compiler happy.
return 0;
}
/**
* Set the ATAGs.
*
* <Alex> I have moved this into a seperate function so as to keep the number of
* local variables in bootLinux to a minimum, meaning the compile can use
* registers for all of them.
*
* <Romain> It's OK, but thanks to my patch we shall not be relying on GCC using
* only registers now.
*/
void write_atags(char *cmdline) {
int i=0;
int byte;
char c;
// ATAG_CORE
SET_TAG_WORD(i++,0x00000002);
SET_TAG_WORD(i++,0x54410001);
// ATAG_MEM
SET_TAG_WORD(i++,0x00000004);
SET_TAG_WORD(i++,0x54410002);
SET_TAG_WORD(i++,PHYSICAL_RAM_SIZE); // size
SET_TAG_WORD(i++,RAM_BASE_ADDRESS); // start
// ATAG_CMDLINE
for (byte=0; cmdline[byte]; byte++);
byte = (byte+3) & ~3;
SET_TAG_WORD(i++,0x00000002+byte/4);
SET_TAG_WORD(i++,0x54410009);
byte = 0;
do {
c = *cmdline++;
SET_TAG_WORD_BYTE(i,byte,c);
if (++byte==4) { byte=0; i++; }
} while (c);
while (byte>0) {
SET_TAG_WORD_BYTE(i,byte,0);
if (++byte==4) { byte=0; i++; }
}
#if MACHINE_CPU=='OMAP'
// ATAG_OMAP
SET_TAG_WORD(i++,0x00000005);
SET_TAG_WORD(i++,0x414f4d50);
SET_TAG_WORD(i++,0x4f030005);
SET_TAG_WORD(i++,0x030001c2);
SET_TAG_WORD(i++,0x00000000);
#endif
// ATAG_NONE
SET_TAG_WORD(i++,0x00000000);
SET_TAG_WORD(i++,0x00000000);
}