/* * 2nd - OMAP "second stage" tt-loader * * Copyright (C) 2008 Guillaume Bougard * Copyright (C) 2005 Luis Recuerda * * ARM kernel loader. Adapt from qemu-neo1973 * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "config.h" #include "usb.h" extern u32 crc32( u32 crc, char* buf, u32 len, crc_cb_fnc_t* cb); /* * Length of memory dump done by 2nd.bin, must be a multiple of DUMP_LINE_SIZE from host main.c * Must also be sufficient to output lines of text, let's say 128 bytes */ #define MEM_READ_SIZE 128*32 #define CHUNK_SIZE 8192 static char *usb_hdr = "TIS" ; static char usb_outbuffer[MEM_READ_SIZE+4]; static u32 strcpy ( char *dest, char *src ) { u32 count = 0 ; char *tmp = dest, *s = src; while ( ++count < MEM_READ_SIZE && *s != '\0' ) *tmp++ = *s++ ; *tmp = '\0' ; return count ; } static u32 strlen ( char *src ) { u32 len = 0 ; while ( *src != '\0' ) { src++ ; len ++ ; } return len ; } static u32 memcpy ( char *dest, char *src, u32 count ) { char *tmp = dest, *s = src; u32 step = count ; while (step--) *tmp++ = *s++; return count ; } static void usb_msg (const char cmd, const char *msg) { u32 len = strcpy(usb_outbuffer,usb_hdr); usb_outbuffer[len-1] = cmd ; len += strcpy(&usb_outbuffer[len], msg ? (char *)msg : "" ); usb_send ( usb_outbuffer, len); while (!usb_sent ()); } static u32 usb_code (char cmd, u32 code) { u32 len = strcpy(usb_outbuffer,usb_hdr); usb_outbuffer[len-1] = cmd ; (*(u32 *)(usb_outbuffer+len)) = code ; len += sizeof(u32); usb_send ( usb_outbuffer, len ); while (!usb_sent ()); return code ; } static void usb_blk (const char cmd, char *mem, u32 size) { u32 len = strcpy(usb_outbuffer,usb_hdr); usb_outbuffer[len-1] = cmd ; len += memcpy(usb_outbuffer+len,mem,size); usb_send ( usb_outbuffer, len ); while (!usb_sent ()); } // Code from linux-2.6.24.3/arch/arm/mach-omap1/id.c source static u16 omap_get_jtag_id(void) { u32 prod_id, jtag_id; prod_id = omap_readl(OMAP_PRODUCTION_ID_1); jtag_id = omap_readl(OMAP32_ID_1); /* Check for unusable OMAP_PRODUCTION_ID_1 on 1611B/5912 and 730 */ if (((prod_id >> 20) == 0) || (prod_id == jtag_id)) prod_id = 0; else prod_id &= 0xffff; if (prod_id) return prod_id; /* Use OMAP32_ID_1 as fallback */ prod_id = ((jtag_id >> 12) & 0xffff); return prod_id; } // stl_raw is qemu related #define stl_raw(x,v) *(u32 *)(x) = v static void set_kernel_args(u32 ram_size, int initrd_size, const char *kernel_cmdline, u32 ram_start) { u32 *p; // Set pointer to kernel params p = (u32 *)(CFG_PARAMADDR); /* ATAG_CORE */ stl_raw(p++, 5); stl_raw(p++, 0x54410001); stl_raw(p++, 0); // 0 = ro ; 1= rw stl_raw(p++, 0x1000); // Page size stl_raw(p++, 0); // root device overrided by cmdline /* ATAG_MEM */ stl_raw(p++, 4); stl_raw(p++, 0x54410002); stl_raw(p++, ram_size); stl_raw(p++, ram_start); if (initrd_size) { /* ATAG_INITRD2 */ stl_raw(p++, 4); stl_raw(p++, 0x54420005); stl_raw(p++, ram_start + INITRD_LOAD_ADDR); stl_raw(p++, initrd_size); } if (kernel_cmdline && *kernel_cmdline) { /* ATAG_CMDLINE */ int cmdline_size; cmdline_size = strlen((char *)kernel_cmdline); memcpy ((char *)p + 2, (char *)kernel_cmdline, cmdline_size + 1); cmdline_size = (cmdline_size >> 2) + 1; stl_raw(p++, cmdline_size + 2); stl_raw(p++, 0x54410009); p += cmdline_size; } /* ATAG_END */ stl_raw(p++, 0); stl_raw(p++, 0); } #define C1_DC (1<<2) /* dcache off/on */ #define C1_IC (1<<12) /* icache off/on */ void cleanup_before_linux (void) { /* * this function is called just before we call linux * it prepares the processor for linux * * we turn off caches etc ... */ unsigned long i; /* turn off I/D-cache */ asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); i &= ~(C1_DC | C1_IC); asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); /* flush I/D-cache */ i = 0; asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (i)); } void usb_crc32_pos( u32 pos, u32 total, u32 crc ) { u32 len = strcpy(usb_outbuffer,usb_hdr); usb_outbuffer[len-1] = 'x' ; (*(u32 *)(usb_outbuffer+len)) = pos ; len += sizeof(u32); (*(u32 *)(usb_outbuffer+len)) = total ; len += sizeof(u32); (*(u32 *)(usb_outbuffer+len)) = crc ; len += sizeof(u32); usb_send ( usb_outbuffer, len ); while (!usb_sent ()); } u32 _main (void) { u32 params = CFG_PARAMADDR ; u32 address = CFG_LOADADDR ; u32 crc = 0 ; usb_msg ('m', "In omap plateform"); u16 prod_id = omap_get_jtag_id(); switch (prod_id) { case 0xb55f: usb_msg ('m', "Found supported omap730"); break; default: usb_msg ('m', "Unsupported plateform found"); usb_code ('v', (u32) prod_id); break; } // Ready to manage requests usb_msg ('r', "Waiting first request"); for (;;) { u32 total, cmd, index = 0 ; u8 usb_inbuffer[CHUNK_SIZE]; usb_recv (usb_inbuffer, sizeof (usb_inbuffer)); while (!usb_rcvd ()); // Check we are knowing the provided header while ( usb_inbuffer[index] == (u8) usb_hdr[index] && usb_hdr[index] != '\0' ) index++ ; if ( usb_hdr[index] != '\0' ) continue ; cmd = usb_inbuffer[index++] ; if ((char)cmd == 's') // Ask size to upload { total= *(u32 *) &usb_inbuffer[index]; u32 size = 0; usb_code ('n', total); while (size < total) { u32 r; usb_recv ((u8 *)address, sizeof (usb_inbuffer)); while ((r= usb_rcvd ()) == 0); address += r ; size += r ; } usb_code ('o', size); } else if ((char)cmd == 'C') // set crc32 value for next download { crc = *(u32 *) &usb_inbuffer[index]; usb_code ('i', crc); } else if ((char)cmd == 'm') // Download size from memory { total= *(u32 *) &usb_inbuffer[index]; u32 size = 0; usb_code ('I', total); // Check crc32 if it has been set if (total && crc) { crc_cb_fnc_t* cb = NULL ; if (total>1024*1024) cb = &usb_crc32_pos ; u32 foundcrc = crc32(0, (char *)address,total,cb) ; usb_code ('C', foundcrc); // Skip download on crc match if (foundcrc == crc) total = 0 ; } if (total == 0) usb_blk ('D', (char *)address, 0); while (size < total) { u32 bs = MEM_READ_SIZE ; if ( total - size < MEM_READ_SIZE) bs = total - size ; usb_blk ('D', (char *)address, bs); address += bs ; size += bs ; } // reset crc for next download anyway crc = 0 ; } else if ((char)cmd == 'e') // End, just loop { usb_reset(); } else if ((char)cmd == 'f') // load file ACK { usb_msg ('f', (const char *)&usb_inbuffer[index]); } else if ((char)cmd == 'M') // dump memory command { usb_blk ('M', (char *)address, MEM_READ_SIZE); address += MEM_READ_SIZE ; } else if ((char)cmd == 'a') // set address command { address = *(u32 *) &usb_inbuffer[index]; usb_code ('i', address); } else if ((char)cmd == 'p') // do a poke on address { u32 size = memcpy( (char *) address, (char *) &usb_inbuffer[index], 4); address += size ; usb_code ('i', address ); } else if ((char)cmd == 'P') // do a peek on address { usb_code ('i', *(u32 *) address ); address += 4 ; } else if ((char)cmd == 'c') // call command { __asm__ __volatile__ ( "mov r0, #0 ;" "mov r1, #0 ;" "mov r2, #0 ;" "ldr r3, %1 ;" "blx r3 ;" "mov %0, r0 ;" : "=r"(index) : "m"(address) : "r0", "r1", "r2", "r3" ); usb_code ('i', index); } else if ((char)cmd == 'b') // boot command { usb_code ('b', address ); usb_reset(); __asm__ __volatile__ ( "ldr r0, %0 ;" "mov pc, r0 ;" : : "m"(address) ); } else if ((char)cmd == 'k') // boot kernel command { int machid = * (int *) &usb_inbuffer[index]; usb_code ('I', machid); set_kernel_args( RAM_SIZE, INITRD_SIZE, KERNEL_CMDLINE, CFG_BASESDRAM ); usb_code ('b', address ); usb_reset(); cleanup_before_linux(); __asm__ __volatile__ ( "mov r0, #0 ;" "ldr r1, %0 ;" "ldr r2, %1 ;" "mov r3, #0 ;" "mov r4, #0 ;" "ldr r5, %2 ;" "mov pc, r5 ;" : : "m" (machid), "m"(params), "m"(address) ); } else if ((char)cmd == 'S') // Stack command { __asm__ __volatile__ ( "ldr fp, %0 ;" "sub fp, #4 ;" "mov sp, fp ;" "sub sp, #256 ;" : : "m"(address) ); usb_code ('i', address); } } }