summaryrefslogtreecommitdiffstats
path: root/flash/bootloader
diff options
context:
space:
mode:
authorJörg Hohensohn <hohensoh@rockbox.org>2003-11-30 11:37:43 +0000
committerJörg Hohensohn <hohensoh@rockbox.org>2003-11-30 11:37:43 +0000
commit6a4e4c87c24455e18bbd77565cb3e993ee350618 (patch)
tree6f2ceac4da97aa63ff8deef939bd3cc2d56d3466 /flash/bootloader
parent014c4fa1f8d56850cfd504a90221c293e4a158f6 (diff)
downloadrockbox-6a4e4c87c24455e18bbd77565cb3e993ee350618.tar.gz
rockbox-6a4e4c87c24455e18bbd77565cb3e993ee350618.tar.bz2
rockbox-6a4e4c87c24455e18bbd77565cb3e993ee350618.zip
source code for all my flash stuff, now finally in cvs
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4083 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'flash/bootloader')
-rw-r--r--flash/bootloader/Makefile78
-rw-r--r--flash/bootloader/README4
-rw-r--r--flash/bootloader/bootloader.c480
-rw-r--r--flash/bootloader/bootloader.h111
-rw-r--r--flash/bootloader/bootloader.lds34
-rw-r--r--flash/bootloader/no_rom.lds62
6 files changed, 769 insertions, 0 deletions
diff --git a/flash/bootloader/Makefile b/flash/bootloader/Makefile
new file mode 100644
index 0000000000..ae56cf9425
--- /dev/null
+++ b/flash/bootloader/Makefile
@@ -0,0 +1,78 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+CC = sh-elf-gcc
+LD = sh-elf-ld
+AR = sh-elf-ar
+AS = sh-elf-as
+OC = sh-elf-objcopy
+
+FIRMWARE := ../../firmware
+TOOLSDIR=../../tools
+
+TARGET = bootloader
+LDS := $(TARGET).lds
+
+
+ifndef PLATFORM
+not_configured:
+ @echo "No platform given."
+ @echo "Use make PLATFORM=PLAYER|RECORDER|RECORDER|FM {NO_ROM=1}"
+##else
+##configured:
+## @echo "Building bootloader for platform "$(PLATFORM)
+endif
+
+
+INCLUDES= -I$(FIRMWARE)/export -I. -I$(OBJDIR)
+DEFINES= -DPLATFORM_$(PLATFORM)
+
+OBJDIR := .
+
+CFLAGS = -O -W -Wall -m1 -nostdlib -ffreestanding -Wstrict-prototypes -fomit-frame-pointer -fschedule-insns $(INCLUDES) $(DEFINES)
+AFLAGS += -small -relax
+
+
+ifdef DEBUG
+ DEFINES := -DDEBUG
+ CFLAGS += -g
+endif
+
+SRC := $(wildcard *.c)
+
+OBJS := $(SRC:%.c=$(OBJDIR)/%.o)
+
+ifdef NO_ROM
+LINKFILE = $(OBJDIR)/no_rom.lds
+ORIGIN = 0
+DEFINES += -DNO_ROM
+else
+LINKFILE = $(OBJDIR)/$(TARGET).lds
+ORIGIN = FFFF500
+endif
+
+$(OBJDIR)/$(TARGET).bin : $(OBJDIR)/$(TARGET).elf
+ $(OC) -O binary $(OBJDIR)/$(TARGET).elf $(OBJDIR)/$(TARGET).bin
+ $(TOOLSDIR)/sh2d $(OBJDIR)/$(TARGET).bin -o $(ORIGIN) > $(OBJDIR)/$(TARGET).asm
+ifndef NO_ROM
+ $(TOOLSDIR)/scramble $(OBJDIR)/$(TARGET).bin $(OBJDIR)/$(TARGET).ajz
+endif
+
+$(OBJDIR)/$(TARGET).elf : $(OBJS)
+ $(CC) -Os -nostdlib -o $(OBJDIR)/$(TARGET).elf -L$(OBJDIR) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/$(TARGET).map
+
+
+clean:
+ -rm -f \
+ $(OBJS) \
+ $(OBJDIR)/$(TARGET).asm \
+ $(OBJDIR)/$(TARGET).bin \
+ $(OBJDIR)/$(TARGET).ajz \
+ $(OBJDIR)/$(TARGET).elf \
+ $(OBJDIR)/$(TARGET).map
diff --git a/flash/bootloader/README b/flash/bootloader/README
new file mode 100644
index 0000000000..16c27d9876
--- /dev/null
+++ b/flash/bootloader/README
@@ -0,0 +1,4 @@
+(c) 2003 by Jrg Hohensohn
+
+This is the source code for the flash bootloader.
+It give the dual boot feature, decompresses one of two software images.
diff --git a/flash/bootloader/bootloader.c b/flash/bootloader/bootloader.c
new file mode 100644
index 0000000000..7179bbef95
--- /dev/null
+++ b/flash/bootloader/bootloader.c
@@ -0,0 +1,480 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2003 by Jrg Hohensohn
+ *
+ * Second-level bootloader, with dual-boot feature by holding F1/Menu
+ * This is the image being descrambled and executed by the boot ROM.
+ * It's task is to copy Rockbox from Flash to DRAM.
+ * The image(s) in flash may optionally be compressed with UCL 2e
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "sh7034.h"
+#include "bootloader.h"
+
+
+#ifdef NO_ROM
+// start with the vector table
+UINT32 vectors[] __attribute__ ((section (".vectors"))) =
+{
+ (UINT32)_main, // entry point, the copy routine
+ (UINT32)(end_stack - 1), // initial stack pointer
+ FLASH_BASE + 0x200, // source of image in flash
+ (UINT32)total_size, // size of image
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0x03020080 // mask and version (just as a suggestion)
+};
+#else
+// our binary has to start with a vector to the entry point
+tpMain start_vector[] __attribute__ ((section (".startvector"))) = {main};
+#endif
+
+#ifdef NO_ROM // some code which is only needed for the romless variant
+void _main(void)
+{
+ UINT32* pSrc;
+ UINT32* pDest;
+ UINT32* pEnd;
+/*
+ asm volatile ("ldc %0,sr" : : "r"(0xF0)); // disable interrupts
+ asm volatile ("mov.l @%0,r15" : : "r"(4)); // load stack
+ asm volatile ("ldc %0,vbr" : : "r"(0)); // load vector base
+*/
+ // copy everything to IRAM and continue there
+ pSrc = begin_iramcopy;
+ pDest = begin_text;
+ pEnd = pDest + (begin_stack - begin_text);
+
+ do
+ {
+ *pDest++ = *pSrc++;
+ }
+ while (pDest < pEnd);
+
+ main(); // jump to the real main()
+}
+
+
+void BootInit(void)
+{
+ // inits from the boot ROM, whether they make sense or not
+ PBDR &= 0xFFBF; // LED off (0x131E)
+ PBCR2 = 0; // all GPIO
+ PBIOR |= 0x40; // LED output
+ PBIOR &= 0xFFF1; // LCD lines input
+
+ // init DRAM like the boot ROM does
+ PACR2 &= 0xFFFB;
+ PACR2 |= 0x0008;
+ CASCR = 0xAF;
+ BCR |= 0x8000;
+ WCR1 &= 0xFDFD;
+ DCR = 0x0E00;
+ RCR = 0x5AB0;
+ RTCOR = 0x9605;
+ RTCSR = 0xA518;
+}
+#endif // #ifdef NO_ROM
+
+
+int main(void)
+{
+ int nButton;
+
+ PlatformInit(); // model-specific inits
+
+ nButton = ButtonPressed();
+
+ if (nButton == 3)
+ { // F3 means start monitor
+ MiniMon();
+ }
+ else
+ {
+ tImage* pImage;
+ pImage = GetStartImage(nButton); // which image
+ DecompressStart(pImage); // move into place and start it
+ }
+
+ return 0; // I guess we won't return ;-)
+}
+
+
+// init code that is specific to certain platform
+void PlatformInit(void)
+{
+#ifdef NO_ROM
+ BootInit(); // if not started by boot ROM, we need to init what it did
+#endif
+
+#if defined PLATFORM_PLAYER
+ BRR1 = 0x0019; // 14400 Baud for monitor
+ if (FW_VERSION > 451) // "new" Player?
+ {
+ PBDR &= ~0x10; // set PB4 to 0 to power-up the harddisk early
+ PBIOR |= 0x10; // make PB4 an output
+ }
+#elif defined PLATFORM_RECORDER
+ BRR1 = 0x0002; // 115200 Baud for monitor
+ if (ReadADC(7) > 0x100) // charger plugged?
+ { // switch off the HD, else a flat battery may not start
+ PACR2 &= 0xFBFF; // GPIO for PA5
+ PAIOR |= 0x20; // make PA5 an output (low by default)
+ }
+#elif defined PLATFORM_FM
+ BRR1 = 0x0002; // 115200 Baud for monitor
+ PBDR |= 0x20; // set PB5 to keep power (fixes the ON-holding problem)
+ PBIOR |= 0x20; // make PB5 an output
+ if (ReadADC(0) < 0x1FF) // charger plugged?
+ {
+ // how do we switch this off?
+ }
+#endif
+
+ // platform-independent inits
+ DCR |= 0x1000; // enable burst mode on DRAM
+ BCR |= 0x2000; // activate Warp mode (simultaneous internal and external mem access)
+}
+
+
+// Thinned out version of the UCL 2e decompression sourcecode
+// Original (C) Markus F.X.J Oberhumer under GNU GPL license
+#define GETBIT(bb, src, ilen) \
+ (((bb = bb & 0x7f ? bb*2 : ((unsigned)src[ilen++]*2+1)) >> 8) & 1)
+
+int ucl_nrv2e_decompress_8(
+ const UINT8 *src, UINT8 *dst, UINT32* dst_len)
+{
+ UINT32 bb = 0;
+ unsigned ilen = 0, olen = 0, last_m_off = 1;
+
+ for (;;)
+ {
+ unsigned m_off, m_len;
+
+ while (GETBIT(bb,src,ilen))
+ {
+ dst[olen++] = src[ilen++];
+ }
+ m_off = 1;
+ for (;;)
+ {
+ m_off = m_off*2 + GETBIT(bb,src,ilen);
+ if (GETBIT(bb,src,ilen)) break;
+ m_off = (m_off-1)*2 + GETBIT(bb,src,ilen);
+ }
+ if (m_off == 2)
+ {
+ m_off = last_m_off;
+ m_len = GETBIT(bb,src,ilen);
+ }
+ else
+ {
+ m_off = (m_off-3)*256 + src[ilen++];
+ if (m_off == 0xffffffff)
+ break;
+ m_len = (m_off ^ 0xffffffff) & 1;
+ m_off >>= 1;
+ last_m_off = ++m_off;
+ }
+ if (m_len)
+ m_len = 1 + GETBIT(bb,src,ilen);
+ else if (GETBIT(bb,src,ilen))
+ m_len = 3 + GETBIT(bb,src,ilen);
+ else
+ {
+ m_len++;
+ do {
+ m_len = m_len*2 + GETBIT(bb,src,ilen);
+ } while (!GETBIT(bb,src,ilen));
+ m_len += 3;
+ }
+ m_len += (m_off > 0x500);
+ {
+ const UINT8 *m_pos;
+ m_pos = dst + olen - m_off;
+ dst[olen++] = *m_pos++;
+ do dst[olen++] = *m_pos++; while (--m_len > 0);
+ }
+ }
+ *dst_len = olen;
+
+ return ilen;
+}
+
+
+// move the image into place and start it
+void DecompressStart(tImage* pImage)
+{
+ UINT32* pSrc;
+ UINT32* pDest;
+
+ pSrc = pImage->image;
+ pDest = pImage->pDestination;
+
+ if (pSrc != pDest) // if not linked to that flash address
+ {
+ if (pImage->flags & IF_UCL_2E)
+ { // UCL compressed, algorithm 2e
+ UINT32 dst_len; // dummy
+ ucl_nrv2e_decompress_8((UINT8*)pSrc, (UINT8*)pDest, &dst_len);
+ }
+ else
+ { // uncompressed, copy it
+ UINT32 size = pImage->size;
+ UINT32* pEnd;
+ size = (size + 3) / 4; // round up to 32bit-words
+ pEnd = pDest + size;
+
+ do
+ {
+ *pDest++ = *pSrc++;
+ }
+ while (pDest < pEnd);
+ }
+ }
+
+ pImage->pExecute();
+}
+
+
+int ReadADC(int channel)
+{
+ // after channel 3, the ports wrap and get re-used
+ volatile UINT16* pResult = (UINT16*)(ADDRAH_ADDR + 2 * (channel & 0x03));
+ int timeout = 266; // conversion takes 266 clock cycles
+
+ ADCSR = 0x20 | channel; // start single conversion
+ while (((ADCSR & 0x80) == 0) && (--timeout)); // 6 instructions per round
+
+ return (timeout == 0) ? -1 : *pResult>>6;
+}
+
+
+// This function is platform-dependent,
+// until I figure out how to distinguish at runtime.
+int ButtonPressed(void) // return 1,2,3 for F1,F2,F3, 0 if none pressed
+{
+ int value = ReadADC(CHANNEL);
+
+ if (value >= F1_LOWER && value <= F1_UPPER) // in range
+ return 1;
+ else if (value >= F2_LOWER && value <= F2_UPPER) // in range
+ return 2;
+ else if (value >= F3_LOWER && value <= F3_UPPER) // in range
+ return 3;
+
+ return 0;
+}
+
+
+// Determine the image to be started
+tImage* GetStartImage(int nPreferred)
+{
+ tImage* pImage1;
+ tImage* pImage2 = NULL; // default to not present
+ UINT32 pos;
+ UINT32* pFlash = (UINT32*)FLASH_BASE;
+
+ // determine the first image position
+ pos = pFlash[2] + pFlash[3]; // position + size of the bootloader = after it
+ pos = (pos + 3) & ~3; // be shure it's 32 bit aligned
+
+ pImage1 = (tImage*)pos;
+
+ if (pImage1->size != 0)
+ { // check for second image
+ pos = (UINT32)(&pImage1->image) + pImage1->size;
+ pImage2 = (tImage*)pos;
+
+ // does it make sense? (not in FF or 00 erazed space)
+ if (pImage2->pDestination == (void*)0xFFFFFFFF
+ || pImage2->size == 0xFFFFFFFF
+ || pImage2->pExecute == (void*)0xFFFFFFFF
+ || pImage2->flags == 0xFFFFFFFF
+ || pImage2->pDestination == NULL) // size, execute and flags can legally be 0
+ {
+ pImage2 = NULL; // invalidate
+ }
+ }
+
+ if (pImage2 == NULL || nPreferred == 1)
+ { // no second image or overridden: return the first
+ return pImage1;
+ }
+
+ return pImage2; // return second image
+}
+
+// diagnostic functions
+
+void SetLed(BOOL bOn)
+{
+ if (bOn)
+ PBDR |= 0x40;
+ else
+ PBDR &= ~0x40;
+}
+
+
+void UartInit(void)
+{
+ PBIOR &= 0xFBFF; // input: RXD1 remote pin
+ PBCR1 |= 0x00A0; // set PB3+PB2 to UART
+ PBCR1 &= 0xFFAF; // clear bits 6, 4 -> UART
+ SMR1 = 0x0000; // async format 8N1, baud generator input is CPU clock
+ SCR1 = 0x0030; // transmit+receive enable
+ PBCR1 &= 0x00FF; // set bit 12...15 as GPIO
+ SSR1 &= 0x00BF; // clear bit 6 (RDRF, receive data register full)
+}
+
+
+UINT8 UartRead(void)
+{
+ UINT8 byte;
+ while (!(SSR1 & SCI_RDRF)); // wait for char to be available
+ byte = RDR1;
+ SSR1 &= ~SCI_RDRF;
+ return byte;
+}
+
+
+void UartWrite(UINT8 byte)
+{
+ while (!(SSR1 & SCI_TDRE)); // wait for transmit buffer empty
+ TDR1 = byte;
+ SSR1 &= ~SCI_TDRE;
+}
+
+
+// include the mini monitor as a rescue feature, started with F3
+void MiniMon(void)
+{
+ UINT8 cmd;
+ UINT32 addr;
+ UINT32 size;
+ UINT32 content;
+ volatile UINT8* paddr = NULL;
+ volatile UINT8* pflash = NULL; // flash base address
+
+ UartInit();
+
+ while (1)
+ {
+ cmd = UartRead();
+ switch (cmd)
+ {
+ case BAUDRATE:
+ content = UartRead();
+ UartWrite(cmd); // acknowledge by returning the command value
+ while (!(SSR1 & SCI_TEND)); // wait for empty shift register, before changing baudrate
+ BRR1 = content;
+ break;
+
+ case ADDRESS:
+ addr = (UartRead() << 24) | (UartRead() << 16) | (UartRead() << 8) | UartRead();
+ paddr = (UINT8*)addr;
+ pflash = (UINT8*)(addr & 0xFFF80000); // round down to 512k align
+ UartWrite(cmd); // acknowledge by returning the command value
+ break;
+
+ case BYTE_READ:
+ content = *paddr++;
+ UartWrite(content); // the content is the ack
+ break;
+
+ case BYTE_WRITE:
+ content = UartRead();
+ *paddr++ = content;
+ UartWrite(cmd); // acknowledge by returning the command value
+ break;
+
+ case BYTE_READ16:
+ size = 16;
+ while (size--)
+ {
+ content = *paddr++;
+ UartWrite(content); // the content is the ack
+ }
+ break;
+
+ case BYTE_WRITE16:
+ size = 16;
+ while (size--)
+ {
+ content = UartRead();
+ *paddr++ = content;
+ }
+ UartWrite(cmd); // acknowledge by returning the command value
+ break;
+
+ case BYTE_FLASH:
+ content = UartRead();
+ pflash[0x5555] = 0xAA; // set flash to command mode
+ pflash[0x2AAA] = 0x55;
+ pflash[0x5555] = 0xA0; // byte program command
+ *paddr++ = content;
+ UartWrite(cmd); // acknowledge by returning the command value
+ break;
+
+ case BYTE_FLASH16:
+ size = 16;
+ while (size--)
+ {
+ content = UartRead();
+ pflash[0x5555] = 0xAA; // set flash to command mode
+ pflash[0x2AAA] = 0x55;
+ pflash[0x5555] = 0xA0; // byte program command
+ *paddr++ = content;
+ }
+ UartWrite(cmd); // acknowledge by returning the command value
+ break;
+
+ case HALFWORD_READ:
+ content = *(UINT16*)paddr;
+ paddr += 2;
+ UartWrite(content >> 8); // highbyte
+ UartWrite(content & 0xFF); // lowbyte
+ break;
+
+ case HALFWORD_WRITE:
+ content = UartRead() << 8 | UartRead();
+ *(UINT16*)paddr = content;
+ paddr += 2;
+ UartWrite(cmd); // acknowledge by returning the command value
+ break;
+
+ case EXECUTE:
+ {
+ tpFunc pFunc = (tpFunc)paddr;
+ pFunc();
+ UartWrite(cmd); // acknowledge by returning the command value
+ }
+ break;
+
+ case VERSION:
+ UartWrite(1); // return our version number
+ break;
+
+ default:
+ {
+ SetLed(TRUE);
+ UartWrite(~cmd); // error acknowledge
+ }
+
+ } // case
+ } // while (1)
+}
diff --git a/flash/bootloader/bootloader.h b/flash/bootloader/bootloader.h
new file mode 100644
index 0000000000..eee61c4809
--- /dev/null
+++ b/flash/bootloader/bootloader.h
@@ -0,0 +1,111 @@
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+// scalar types
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+typedef unsigned long UINT32;
+typedef int BOOL;
+
+typedef void(*tpFunc)(void); // type for execute
+typedef int(*tpMain)(void); // type for start vector to main()
+
+
+// structure of an image in the flash
+typedef struct
+{
+ UINT32* pDestination; // address to copy it to
+ UINT32 size; // how many bytes of payload (to the next header)
+ tpFunc pExecute; // entry point
+ UINT32 flags; // uncompressed or compressed
+ // end of header, now comes the payload
+ UINT32 image[]; // the binary image starts here
+ // after the payload, the next header may follow, all 0xFF if none
+} tImage;
+
+// flags valid for image header
+#define IF_NONE 0x00000000
+#define IF_UCL_2E 0x00000001 // image is compressed with UCL, algorithm 2e
+
+
+// resolve platform dependency of F1 button check
+#if defined PLATFORM_PLAYER
+#define CHANNEL 1
+#define F1_LOWER 0 // this is the "Menu" key
+#define F1_UPPER 384
+#define F2_LOWER 1024 // not present
+#define F2_UPPER 1024
+#define F3_LOWER 1024
+#define F3_UPPER 1024
+#elif defined PLATFORM_RECORDER
+#define CHANNEL 4
+#define F1_LOWER 250
+#define F1_UPPER 499
+#define F2_LOWER 500
+#define F2_UPPER 699
+#define F3_LOWER 900
+#define F3_UPPER 1023
+#elif defined PLATFORM_FM
+#define CHANNEL 4
+#define F1_LOWER 150
+#define F1_UPPER 384
+#define F2_LOWER 385
+#define F2_UPPER 544
+#define F3_LOWER 700
+#define F3_UPPER 1023
+#else
+#error ("No platform given!")
+#endif
+
+#define FLASH_BASE 0x02000000 // start of the flash memory
+#define FW_VERSION *(unsigned short*)(FLASH_BASE + 0xFE) // firmware version
+
+
+// prototypes
+void _main(void) __attribute__ ((section (".startup")));
+int main(void);
+void PlatformInit(void);
+void DramInit(void);
+int ucl_nrv2e_decompress_8(const UINT8 *src, UINT8 *dst, UINT32* dst_len);
+void DecompressStart(tImage* pImage);
+int ReadADC(int channel);
+int ButtonPressed(void);
+tImage* GetStartImage(int nPreferred);
+// test functions
+void SetLed(BOOL bOn);
+void UartInit(void);
+UINT8 UartRead(void);
+void UartWrite(UINT8 byte);
+void MiniMon(void);
+
+
+// minimon commands
+#define BAUDRATE 0x00 // followed by BRR value; response: command byte
+#define ADDRESS 0x01 // followed by 4 bytes address; response: command byte
+#define BYTE_READ 0x02 // response: 1 byte content
+#define BYTE_WRITE 0x03 // followed by 1 byte content; response: command byte
+#define BYTE_READ16 0x04 // response: 16 bytes content
+#define BYTE_WRITE16 0x05 // followed by 16 bytes; response: command byte
+#define BYTE_FLASH 0x06 // followed by 1 byte content; response: command byte
+#define BYTE_FLASH16 0x07 // followed by 16 bytes; response: command byte
+#define HALFWORD_READ 0x08 // response: 2 byte content
+#define HALFWORD_WRITE 0x09 // followed by 2 byte content; response: command byte
+#define EXECUTE 0x0A // response: command byte if call returns
+#define VERSION 0x0B // response: version
+
+
+// linker symbols
+extern UINT32 begin_text[];
+extern UINT32 end_text[];
+extern UINT32 begin_data[];
+extern UINT32 end_data[];
+extern UINT32 begin_bss[];
+extern UINT32 end_bss[];
+extern UINT32 begin_stack[];
+extern UINT32 end_stack[];
+extern UINT32 begin_iramcopy[];
+extern UINT32 total_size[];
diff --git a/flash/bootloader/bootloader.lds b/flash/bootloader/bootloader.lds
new file mode 100644
index 0000000000..143d83bdc7
--- /dev/null
+++ b/flash/bootloader/bootloader.lds
@@ -0,0 +1,34 @@
+OUTPUT_FORMAT(elf32-sh)
+INPUT(bootloader.o)
+
+MEMORY
+{
+ /* the boot ROM uses IRAM at 400-430, stay away and start at 500 */
+ IRAM : ORIGIN = 0x0FFFF500, LENGTH = 0xA00
+ /* and leave some room for stack at the end */
+}
+
+SECTIONS
+{
+ .startvector :
+ {
+ *(.startvector)
+ . = ALIGN(0x4);
+ } > IRAM
+
+ .text :
+ {
+ *(.text)
+ . = ALIGN(0x4);
+ } > IRAM
+
+ .data :
+ {
+ *(.data)
+ } > IRAM
+
+ .bss :
+ {
+ *(.bss)
+ } > IRAM
+}
diff --git a/flash/bootloader/no_rom.lds b/flash/bootloader/no_rom.lds
new file mode 100644
index 0000000000..e65e7fdd3c
--- /dev/null
+++ b/flash/bootloader/no_rom.lds
@@ -0,0 +1,62 @@
+/* This is for the variant without boot ROM,
+ where the flash ROM is mirrored to address zero */
+
+OUTPUT_FORMAT(elf32-sh)
+INPUT(bootloader.o)
+
+MEMORY
+{
+ IRAM : ORIGIN = 0x0FFFF000, LENGTH = 0x1000
+ FLASH : ORIGIN = 0x00000000, LENGTH = 0x40000
+}
+
+SECTIONS
+{
+ .vectors :
+ {
+ *(.vectors)
+ . = ALIGN(0x200);
+ } > FLASH
+
+ .startup :
+ {
+ *(.startup)
+ . = ALIGN(0x4);
+ _begin_iramcopy = .;
+ } > FLASH
+
+ .text : AT ( _begin_iramcopy )
+ {
+ _begin_text = .;
+ *(.text)
+ . = ALIGN(0x4);
+ _end_text = .;
+ } > IRAM
+
+ .data : AT ( _end_text )
+ {
+ _begin_data = .;
+ *(.data)
+ . = ALIGN(0x4);
+ _end_data = .;
+ } > IRAM
+
+ .bss : AT ( _end_data )
+ {
+ _begin_bss = .;
+ *(.bss)
+ . = ALIGN(0x4);
+ _end_bss = .;
+ } > IRAM
+
+ .stack :
+ {
+ _begin_stack = .;
+ *(.stack)
+ . = ALIGN(0x1000);
+ _end_stack = .;
+ } > IRAM
+
+ /* size of the program (without vectors) */
+ _total_size = SIZEOF(.startup) + SIZEOF(.text) + SIZEOF(.data);
+}