Browse Source

WIP: abc disk emulation

... more work on abc disk emulation (does not yet compile) ...
H. Peter Anvin 3 years ago
parent
commit
7c730225c9
9 changed files with 346 additions and 105 deletions
  1. 3 2
      fw/Makefile
  2. 192 92
      fw/abcdisk.c
  3. 4 4
      fw/abcio.c
  4. 6 2
      fw/abcio.h
  5. 1 1
      fw/diskcache.c
  6. 2 0
      fw/fw.h
  7. 17 4
      fw/irq.h
  8. 115 0
      fw/main.c
  9. 6 0
      fw/max80.ld

+ 3 - 2
fw/Makefile

@@ -36,9 +36,10 @@ boot_depth  := 8192
 boot_width  := 32
 boot_stride := 1
 
-max80.elf: head.o die.o dummy.o irqtable.o irqasm.o sbrk.o hello.o \
+max80.elf: head.o die.o main.o dummy.o irqtable.o irqasm.o sbrk.o \
 	  console.o rtc.o \
-	  sdcard.o diskcache.o abcdrive.o \
+	  sdcard.o diskcache.o \
+	  abcio.o abcdisk.o \
 	  memset.o memcpy.o \
 	  testdata.o \
 	  fatfs.a

+ 192 - 92
fw/abcdisk.c

@@ -5,20 +5,14 @@
  */
 
 #include "fw.h"
+#include "abcio.h"
 #include "ff.h"
 
 /* Option flags, mostly for debugging */
 #define NOTTHERE	0
 #define READONLY	0
 #define INTERLEAVE	0
-
-/* Disk controller I/O state machine */
-enum controller_state {
-    ctl_uninit,			/* Disks not initialized */
-    ctl_command,		/* Expecting command */
-    ctl_upload,			/* Expecting data block upload */
-    ctl_download		/* Expecting data block download */
-};
+#define FORMAT_SUPPORT	0
 
 enum drive_flags {
     DF_MOUNTED,			/* Host file open, drive exported */
@@ -26,19 +20,26 @@ enum drive_flags {
     DF_DISABLED			/* Drive is disabled */
 };
 
+enum pending {
+    PEND_IO    = 1,
+    PEND_RESET = 2
+};
+
 /* Per-drive state */
 struct drive_state {
     FIL file;			/* Host file */
     char name[4];               /* Drive name */
     uint16_t sectors;		/* Total size in 256-byte sectors */
+#if INTERLEAVE
     uint8_t ilmsk, ilfac;       /* Software interleaving parameters */
+#endif
     enum drive_flags flags;
     enum drive_flags force;	/* Option to force flags */
 };
 
 /* Per-controller state */
 struct ctl_state {
-    enum controller_state state;
+    struct abc_dev *iodev;
     uint8_t k[4];		/* Command bytes */
     uint8_t clustshift;		/* log2(clustersize/256) */
     uint8_t devsel;		/* I/O device select */
@@ -46,10 +47,15 @@ struct ctl_state {
     uint8_t c, h, s;		/* Disk geometry */
     uint8_t drives;		/* Total drives present */
     bool newaddr;		/* "New addressing" */
-    bool fmtdata_in_buf;	/* Use user-provided formatting data */
     const char name[4];		/* Name of controller (disk type) */
+    bool initialized;	        /* Controller initialized */
+#if INTERLEAVE
     uint8_t ilmsk, ilfac;	/* Software interleaving parea */
-    uint8_t error;		/* Error code status */
+#endif
+#if FORMAT_SUPPORT
+    bool fmtdata_i_buf;	/* Use user-provided formatting data */
+#endif
+    volatile enum pending pending;	/* Need to do I/O */
     struct drive_state drv[8];	/* Per-drive state */
     uint8_t buf[4][256];	/* 4 host buffers @ 256 bytes */
 };
@@ -61,6 +67,11 @@ struct ctl_state {
  */
 #define OUT_OF_RANGE 0x21	/* Status code for an invalid sector */
 
+#define DISK_NOT_READY 0x80
+#define WRITE_PROTECT  0x40
+#define WRITE_FAULT    0x20
+#define CRC_ERROR      0x08
+
 enum controller_types {
     MOx,
     MFx,
@@ -69,6 +80,12 @@ enum controller_types {
     CONTROLLER_TYPES
 };
 
+#if INTERLEAVE
+# define IL(mask, fac) .ilmsk = (mask), .ilfac = (fac),
+#else
+# define IL(mask, fac)
+#endif
+
 static struct ctl_state controllers[CONTROLLER_TYPES] = {
     /*
      * MOx: covers all of these formats:
@@ -81,10 +98,11 @@ static struct ctl_state controllers[CONTROLLER_TYPES] = {
 	.clustshift = 0,
 	.maxsectors = 40 * 2 * 16,
 	.c = 40, .h = 2, .s = 16,
+	.name = "mo",
+#if FORMAT_SUPPORT
 	.fmtdata_in_buf = true,
-	.ilmsk = 15,
-	.ilfac = 7,
-	.name = "mo"
+#endif
+	IL(15, 7)
     },
 
     /* MFx: DSQD = 80×16x2 (640K, ABC832/834) */
@@ -144,10 +162,12 @@ static inline unsigned int cur_sector(const struct ctl_state *state)
 static inline unsigned int
 virt2phys(const struct drive_state *drv, unsigned int sector)
 {
+#if INTERLEAVE
     unsigned int ilmsk = drv->ilmsk;
     unsigned int ilfac = drv->ilfac;
 
     sector = (sector & ~ilmsk) | ((sector * ilfac) & ilmsk);
+#endif
     return sector;
 }
 
@@ -181,18 +201,32 @@ static inline uint8_t *cur_buf(struct ctl_state *state)
     return state->buf[state->k[1] >> 6];
 }
 
+static void disk_start_command(struct ctl_state *state)
+{
+    abc_setup_out_queue(&state->iodev, state->k, 4, 0x81);
+}
+
+static void sync_drives(struct ctl_state *state)
+{
+    for (int i = 0; i < 8; i++)
+	if (state->drv[i].flags & DF_MOUNTED)
+	    f_sync(&state->drv[i].file);
+}
+    
 static void disk_reset_state(struct ctl_state *state)
 {
-    int i;
+    abc_set_inp_status(&state->iodev, 0);
+    disk_set_error(state, 0);
 
-    state->state = disk_k0;
-    state->error = 0;
-    state->in_ptr = -1;
-    state->out_ptr = 0;
-    state->notready_ctr = NOT_READY;
+    sync_drives(state);
 
-    for (i = 0; i < 8; i++)
-        f_sync(&state->drv[i].file);
+    disk_start_command(state);
+}
+
+static void disk_set_error(struct ctl_state *state, unsigned int error)
+{
+    state->error = error;
+    abc_set_inp_default(&state->iodev, error);
 }
 
 static struct drive_state *
@@ -283,57 +317,80 @@ static int mount_drive(struct drive_state *drv, const char *filename)
 #if INTERLEAVE
 	drv->ilfac = state->ilfac;
 	drv->ilmsk = state->ilmsk;
-#else
-	drv->ilfac = drv->ilmsk = 0;
 #endif
 
 	return 0;
 }
 
+#define IDLE_CALLBACK_MASK	(1 << 4)
+
+static void abcdisk_callback_out(struct abc_dev *dev, uint8_t data)
+{
+    struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
+    
+    dev->callback_mask = IDLE_CALLBACK_MASK;
+    ABC_INP1_DATA = dev->inp_data[1] = 0;
+    state->pending |= PEND_IO;
+}
+
+static void abcdisk_callback_inp(struct abc_dev *dev)
+{
+    struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
+
+    dev->callback_mask = IDLE_CALLBACK_MASK;
+    ABC_INP1_DATA = dev->inp_data[1] = 0;
+    state->pending |= PEND_IO;
+}
+
+static void abcdisk_callback_cmd(struct abc_dev *dev, uint8_t data, uint8_t addr)
+{
+    struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
+    if (addr == 4)
+	state->pending |= PEND_RESET;
+}
+
 static const char * const disk_pfx_abc80[]  = { "/abcdisk.80/",  "/abcdisk/", "/", NULL };
 static const char * const disk_pfx_abc800[] = { "/abcdisk.800/", "/abcdisk/", "/", NULL };
 
-static void disk_init(struct ctl_state *state)
+static void init_drives(struct ctl_state *state)
 {
+    static bool disk_initialized;
+    static int disk_init_status;
     uint32_t abc_status = ABC_STATUS;
     const char * const *pfx_list;
     int i;
 
-    /* If no ABC, don't initialize disks; should in fact unmount them all */
-    if (!(abc_status & ABC_STATUS_LIVE))
-	return;
-
-    pfx_list = (abc_status & ABC_STATUS_800) ? disk_pfx_abc800 : disk_pfx_abc80;
+    if (!disk_initialized) {
+	disk_init_status = disk_init();
+	disk_initialized = true;
+    }
     
+    pfx_list = (abc_status & ABC_STATUS_800) ? disk_pfx_abc800 : disk_pfx_abc80;
+	
     /* If any of these don't exist we simply report device not ready */
     state->drives = 0;
-    
-    for (i = 0; i < 8; i++) {
-        struct drive_state *drv = &state->drv[i];
-        unsigned int filesec;
-	const char **pfx;
-
-        snprintf(drv->name, sizeof drv->name, "%-.2s%c", state->name, i + '0');
-
-	for (pfx = pfx_list; *pfx; pfx++) {
-	    char filename_buf[64];
-	    snprintf(filename_buf, sizeof filename_buf, "%s%s", *pfx, drv->name);
 
-	    if (!mount_drive(drv, filename_buf)) {
-		state->drives++;
-		break;
+    if (!disk_init_status) {
+	for (i = 0; i < 8; i++) {
+	    struct drive_state *drv = &state->drv[i];
+	    unsigned int filesec;
+	    const char **pfx;
+	    
+	    snprintf(drv->name, sizeof drv->name, "%-.2s%c", state->name, i + '0');
+	    
+	    for (pfx = pfx_list; *pfx; pfx++) {
+		char filename_buf[64];
+		snprintf(filename_buf, sizeof filename_buf, "%s%s", *pfx, drv->name);
+		
+		if (!mount_drive(drv, filename_buf)) {
+		    state->drives++;
+		    break;
+		}
 	    }
 	}
     }
-
-    disk_reset_state(state);
 }
 
-static void setup_dma(struct drive_state *drv, unsigned int len, uint8_t devsel, bool out_dir)
-{
-    
-    
-
 static void do_next_command(struct ctl_state *state)
 {
     struct drive_state *drv = cur_drv_mutable(state);
@@ -341,12 +398,12 @@ static void do_next_command(struct ctl_state *state)
 
     if (state->k[0] & 0x01) {
         /* READ SECTOR */
-	if (!) {
-	    state->error = 0x80; /* Device not ready */
+	if (!(drv->flags & DF_MOUNTED)) {
+	    disk_set_error(DISK_NOT_READY);
 	} else if (!cur_sector_valid(state)) {
-	    state->error = OUT_OF_RANGE;
+	    disk_set_error(OUT_OF_RANGE);
 	} else if (!file_pos_valid(state)) {
-	    state->error = 0x08; /* CRC error(?) */
+	    disk_set_error(CRC_ERROR);
 	} else {
 	    UINT rlen = 0;
 	    FRESULT rv;
@@ -355,55 +412,50 @@ static void do_next_command(struct ctl_state *state)
 	    if (rv == FR_OK)
 		rv = f_read(&drv->file, buf, 256, &rlen);
 	    if (rv != FR_OK || rlen != 256) {
-		state->error = 0x08; /* CRC error */
+		disk_set_error(CRC_ERROR);
 	    }
 	}
         state->k[0] &= ~0x01;   /* Command done */
     }
     if (state->k[0] & 0x02) {
         /* SECTOR TO HOST */
-	
-	state->in_ptr = 0;
-        state->state = disk_download;
         state->k[0] &= ~0x02;   /* Command done */
+	abc_setup_inp_queue(&state->iodev, buf, 256, 0x01);
         return;
     }
     if (state->k[0] & 0x04) {
         /* SECTOR FROM HOST */
-        state->state = disk_upload;
-        state->out_ptr = 0;
         state->k[0] &= ~0x04;   /* Command done */
+	abc_setup_out_queue(&state->iodev, buf, 256, 0x01);
         return;
     }
     if (state->k[0] & 0x08) {
         /* WRITE SECTOR */
-	if (!hf) {
-	    state->error = 0x80; /* Not ready */
-	} else if (!file_wrok(hf)) {
-	    state->error = 0x40; /* Write protect */
+	if (!(drv->flags & DF_MOUNTED)) {
+	    disk_set_error(DISK_NOT_READY);
+	} else if (df->flags & DF_READONLY) {
+	    disk_set_error(WRITE_PROTECT);
 	} else if (!cur_sector_valid(state)) {
-	    state->error = OUT_OF_RANGE;
-	    if (tracing(TRACE_DISK)) {
-		fprintf(tracef, "%s: write: disk sector out of range: %u/%u (cluster %u/%u)\n",
-			drv->name, cur_sector(state), drv->sectors,
-			cur_sector(state) >> state->clustshift,
-			drv->sectors >> state->clustshift);
-	    }
+	    disk_set_error(OUT_OF_RANGE);
 	} else if (!file_pos_valid(state)) {
-	    state->error = 0x08; /* CRC error(?) */
+	    disk_set_error(CRC_ERROR);
 	} else {
-	    if (hf->map) {
-		memcpy(hf->map + file_pos(state), buf, 256);
-	    } else {
-		clearerr(hf->f);
-		fseek(hf->f, file_pos(state), SEEK_SET);
-		fwrite(buf, 1, 256, hf->f);
-		if (ferror(hf->f))
-		    state->error = 0x20; /* Write fault */
+	    UINT wlen = 0;
+	    FRESULT rv;
+	    
+	    rv = f_lseek(&state->file, file_pos(state));
+	    if (rv == FR_OK)
+		rv = f_write(&state->file, buf, 256, &wlen);
+	    if (rv != FR_OK || wlen != 256)
+		disk_set_error(WRITE_FAULT);
 	    }
         }
         state->k[0] &= ~0x08;   /* Command done */
     }
+
+#if FORMAT_SUPPORT
+    /* This code needs additional work */
+
     if (state->k[0] & 0x10 && state->k[1] & 0x08) {
 	state->out_ptr = 0;
 	/* FORMAT */
@@ -499,24 +551,72 @@ static void do_next_command(struct ctl_state *state)
 	}
 	state->k[1] &= ~0x08;
     }
+#endif	/* FORMAT support */
 
     if (!(state->k[1] & 0x38))
 	state->k[0] &= ~0x10;
 
-    if (tracing(TRACE_DISK)) {
-      if (state->trace_dump) {
-	trace_dump(TRACE_DISK, drv->name, buf, 256);
-	state->trace_dump = false;
-      }
-    }
-    state->state = disk_k0;
+    disk_start_command(state);
 }
 
-static void disk_reset(uint8_t sel)
+#define SYNC_TIME (2*CPU_HZ)
+
+/* Called from the main loop */
+void abcdisk_io_poll(void)
 {
-    struct ctl_state *state = sel_to_state[sel];
+    static uint32_t last_sync;
+    uint32_t now = rdtime();
+    bool need_sync = (now - last_sync) >= SYNC_TIME;
+    last_sync = now;
+    
+    for (int i = 0; i < CONTROLLER_TYPES; i++) {
+	struct ctl_state *state = &controllers[i];
+	enum pending pending;
 
-    if (state && state->state != disk_need_init)
-	disk_reset_state(state);
+	mask_irq(ABC_IRQ);
+	pending = state->pending;
+	state->pending = 0;
+	unmask_irq(ABC_IRQ);
+
+	if (!pending)
+	    continue;
+
+	if (pending & PEND_RESET) {
+	    if (state->initialized)
+		disk_reset_state(state);
+	}
+
+	if (pending & PEND_IO) {
+	    if (!state->initialized)
+		init_drives(state);
+
+	    do_next_command(state);
+	}
+
+	if (need_sync)
+	    sync_drives(state);
+    }
 }
 
+/*
+ * Called during initialization. Don't initialize the SD card here;
+ * it will take too long and ABC will time out claiming no drives present.
+ */
+void abcdisk_init(void)
+{
+    for (int i = 0; i < CONTROLLER_TYPES; i++) {
+	struct state *state = &controllers[i];
+
+	/* Set up the bus device */
+	memset(&state->iodev, 0, sizeof state->iodev);
+	state->iodev.callback_mask = IDLE_CALLBACK_MASK;
+	state->iodev.state_first_out_mask = ~0x80;
+	state->iodev.state_first_inp_mask = ~0x80;
+	state->callback_out = abcdisk_callback_out;
+	state->callback_inp = abcdisk_callback_inp;
+	state->callback_cmd = abcdisk_callback_cmd;
+    
+	disk_reset_state(state);
+	abcio_register(&state->iodev, state->devsel);
+    }
+}

+ 4 - 4
fw/abcio.c

@@ -99,28 +99,28 @@ IRQHANDLER(abc)
 }
 
 void abc_setup_out_queue(struct abc_dev *dev, void *buf, size_t len,
-			 uint8_t status, uint8_t status_first_mask)
+			 uint8_t status)
 {
     mask_irq(ABC_IRQ);
 
-    dev->status_first_out_mask = status_first_mask;
     dev->out_buf = buf;
     dev->out_cnt = len;
     ABC_INP1_DATA = dev->inp_data[1] = status;
+    dev->callback_mask |= (1 << 0);
 
     unmask_irq(ABC_IRQ);
 }
 
 void abc_setup_inp_queue(struct abc_dev *dev, const void *buf, size_t len,
-			 uint8_t status, uint8_t status_first_mask)
+			 uint8_t status)
 {
     mask_irq(ABC_IRQ);
 
-    dev->status_first_inp_mask = status_first_mask;
     dev->inp_buf = buf;
     dev->inp_cnt = len;
     ABC_INP0_DATA = dev->inp_data[0] = *dev->inp_buf++;
     ABC_INP1_DATA = dev->inp_data[1] = status;
+    dev->callback_mask |= (1 << 8);
 
     unmask_irq(ABC_IRQ);
 }

+ 6 - 2
fw/abcio.h

@@ -33,9 +33,13 @@ struct abc_dev {
 };
 
 void abc_setup_out_queue(struct abc_dev *dev, void *buf, size_t len,
-			 uint8_t status, uint8_t status_first_mask);
+			 uint8_t status);
 void abc_setup_inp_queue(struct abc_dev *dev, const void *buf, size_t len,
-			 uint8_t status, uint8_t status_first_mask);
+			 uint8_t status);
 void abc_register(struct abc_dev *dev, unsigned int devsel);
+void abc_init(void);
+
+void abcdisk_init(void);
+void abcdisk_io_poll(void);
 
 #endif /* ABCIO_H */

+ 1 - 1
fw/diskcache.c

@@ -221,7 +221,7 @@ int disk_init(void)
     con_printf("sdcard: %u/%u clusters free, clusters = %u bytes\n",
 	       freeclust, fs->n_fatent - 2, fs->csize << 9);
 
-    mount_abcdrives();
+    abcdisk_init();
 
     return 0;
 }

+ 2 - 0
fw/fw.h

@@ -46,6 +46,8 @@ typedef union xcptr {
 #define min(a,b) (((a) < (b)) ? (a) : (b))
 #define max(a,b) (((a) > (b)) ? (a) : (b))
 
+#define ARRAY_SIZE (sizeof(a)/sizeof(a[0]))
+
 #define no_return void __attribute__((noreturn))
 
 #define __dram_bss	__attribute__((section(".dram.bss")))

+ 17 - 4
fw/irq.h

@@ -13,16 +13,22 @@ extern irqhandler_t __irq_handler_table[];
   void irqhandler_ ##x (unsigned int vector __attribute__((unused)), \
 			size_t pc __attribute__((unused)))
 
-static inline unsigned int mask_irq(unsigned int vector)
+static inline unsigned int mask_irqs(unsigned int mask)
 {
-    const unsigned int mask = 1U << vector;
     return p_maskirq(mask, ~mask);
 }
-static inline unsigned int unmask_irq(unsigned int vector)
+static inline unsigned int mask_irq(unsigned int vector)
+{
+    return mask_irqs(1U << vector);
+}
+static inline unsigned int unmask_irqs(unsigned int mask)
 {
-    const unsigned int mask = 1U << vector;
     return p_maskirq(0, ~mask);
 }
+static inline unsigned int unmask_irq(unsigned int vector)
+{
+    return unmask_irqs(1U << vector);
+}
 static inline unsigned int irqmask(void)
 {
     return p_maskirq(0, ~0);
@@ -34,4 +40,11 @@ static inline unsigned int waitfor(unsigned int vector)
     return p_waitirq(mask, 0);
 }
 
+/* Wait for any enabled IRQ */
+static inline unsigned int wait_for_irq(void)
+{
+    unsigned int mask = irqmask();
+    return p_waitirq(mask, mask);
+}
+
 #endif /* IRQ_H */

+ 115 - 0
fw/main.c

@@ -0,0 +1,115 @@
+#include "fw.h"
+#include "io.h"
+#include "sys.h"
+#include "console.h"
+
+volatile uint32_t timer_irq_count;
+IRQHANDLER(sysclock)
+{
+    uint32_t count = timer_irq_count;
+
+    count++;
+    timer_irq_count = count;
+    set_led(count >> 3); /* 4 Hz */
+}
+
+/* Configure ABC memory map */
+static void init_abc_memmap(void)
+{
+    volatile uint32_t *pg = &ABCMEMMAP_PAGE(0);
+
+    for (unsigned int addr = 0; addr < 0x10000; addr += 512) {
+	if (addr >= 0x5800 && addr < 0x6000) {
+	    *pg++ = ABCMEMMAP_RD | addr;
+	} else if (addr >= 0x8000 && addr < 0xc000) {
+	    *pg++ = ABCMEMMAP_RD | ABCMEMMAP_WR | addr;
+	} else {
+	    *pg++ = addr;	/* Disabled */
+	}
+    }
+}
+
+extern const char __dram_init_start[], __dram_init_end[], __dram_init_len[];
+extern const char __dram_bss_start[], __dram_bss_end[], __dram_bss_len[];
+
+static uint32_t romcopy_time[2];
+static unsigned int romcopy_state;
+IRQHANDLER(romcopy)
+{
+    switch (romcopy_state++) {
+    case 0:
+	/* Copy testdata */
+	ROMCOPY_RAMADDR = (size_t)__dram_init_start;
+	ROMCOPY_ROMADDR = 0x100000;
+	ROMCOPY_DATALEN = (size_t)__dram_init_len;
+	break;
+    case 1:
+	/* Zero .dram.bss */
+	romcopy_time[0] = rdtime() - time_zero;
+
+	ROMCOPY_RAMADDR = (size_t)__dram_bss_start;
+	ROMCOPY_ROMADDR = 0;	/* Clear */
+	ROMCOPY_DATALEN = (size_t)__dram_bss_len;
+	break;
+    default:
+	romcopy_time[1] = rdtime() - romcopy_time[0];
+	mask_irq(ROMCOPY_IRQ);
+	return;
+    }
+}
+
+static no_return killed(const char *how, size_t pc)
+{
+    con_printf("ERROR: %s at 0x%08x\n", how, pc);
+
+    con_flush();
+    udelay(5000000);
+    reset();
+}
+
+IRQHANDLER(BUSERR)
+{
+    killed("misaligned", pc);
+}
+
+IRQHANDLER(EBREAK)
+{
+    killed("invalid instruction", pc);
+}
+
+static uint32_t timer_irq_start;
+static void init(void)
+{
+    static const char hello[] =
+	"\n*** Hello, World! ***\n"
+	"Firmware compiled on: " __DATE__ " " __TIME__ "\n\n";
+
+    /* Start ROM copy engine */
+    romcopy_state = 0;
+    unmask_irqs((1U << ROMCOPY_IRQ)|(1U << EBREAK_IRQ)|(1U << BUSERR_IRQ));
+
+    set_led(0);
+    con_set_baudrate(115200);
+    con_puts(hello);
+
+    abc_init();
+    init_abc_memmap();
+
+    timer_irq_count = 0;
+    timer_irq_start = rdtime();
+    unmask_irq(SYSCLOCK_IRQ);
+
+    read_rtc();
+
+    abcdisk_init();
+}
+
+void main(void)
+{
+    init();
+
+    while (1) {
+	wait_for_irq();
+	abcdisk_io_poll();
+    }
+}

+ 6 - 0
fw/max80.ld

@@ -169,6 +169,9 @@ SECTIONS
 	}
 
 	/* Sections in SDRAM */
+	. = SDRAM_ADDR;
+	__dram_start = .;
+	__dram_init_start = .;
 
 	.dram.text : ALIGN(4) {
 		*(.dram.text*)
@@ -183,6 +186,9 @@ SECTIONS
 	} >DRAM
 
 	. = ALIGN(8);
+	__dram_init_end = .;
+	__dram_init_len = __dram_init_end - __dram_init_start;
+
 	__dram_bss_start = .;
 	.dram.bss (NOLOAD) : ALIGN(8) {
 		*(.dram.bss*)