[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[microblaze-uclinux] [patch] Microblaze kernel command line handling



Hi folks,

Thanks to Brett Boren, Microblaze uClinux (and the mbvanilla bootloader) 
now supports passing command line parameters to the kernel at boot-time.

File cmdline.patch applies from the linux-2.4.x directory, and you'll 
also need to copy the new bootloader.c file into the directory 
mbvanilla_net/code/bootloader.c, to replace the existing one.

Greg or David, would you please apply this patch to the uClinux CVS?

To enable command line handling, after patching (or CVS update) run make 
menuconfig, in Customize Kernel Settings -> Processor Type and Features 
- there is a new menu option to enable command line handling.  Select 
that, save and exit, make dep, then make.

Get the new kernel image into the board as per usual.

Rebuild the bitfile with the new bootloader, and upload to the FPGA. 
Now, there's a new bootloader menu item to set the kernel command line. 
  For starters, try something like "single" (force single user mode).

Then boot the board as normal.  The kernel should launch into single 
user mode.

If you don't want or need the command line handling, just leave it 
disabled in the kernel menuconfig.

Currently, the command line must be typed into the bootloader each time, 
  however it will be simple to put the params into flash instead, making 
them persistent.  Let us know if this would be useful, and we'll look 
into it.

Thanks again to Brett for working this out.

Cheers,

John
Index: arch/microblaze/config.in
===================================================================
RCS file: /var/cvs/uClinux-2.4.x/arch/microblaze/config.in,v
retrieving revision 1.7
diff -u -b -B -w -p -r1.7 config.in
--- arch/microblaze/config.in	2003/12/10 02:39:01	1.7
+++ arch/microblaze/config.in	2004/01/09 02:27:06
@@ -67,7 +67,7 @@ mainmenu_option next_comment
   bool 'GPIO driver'		CONFIG_XILINX_GPIO
   bool 'Ethernet driver'	CONFIG_XILINX_ENET
   bool 'SystemAce driver'       CONFIG_XILINX_SYSACE
-
+  bool 'Kernel cmdline support (needs new bootloader)'	CONFIG_MBVANILLA_CMDLINE
   int 'CPU CLOCK Frequency' CONFIG_CPU_CLOCK_FREQ 66000000
 
   # Default some stuff
Index: arch/microblaze/kernel/head.S
===================================================================
RCS file: /var/cvs/uClinux-2.4.x/arch/microblaze/kernel/head.S,v
retrieving revision 1.4
diff -u -b -B -w -p -r1.4 head.S
--- arch/microblaze/kernel/head.S	2003/10/22 07:55:27	1.4
+++ arch/microblaze/kernel/head.S	2004/01/09 02:27:06
@@ -1,6 +1,7 @@
 /*
  * arch/microblaze/kernel/head.S -- Lowest-level startup code
  *
+ *  Copyright (C) 2004       Brett Boren <borenb@eng.uah.edu>
  *  Copyright (C) 2003       John Williams <jwilliams@itee.uq.edu.au>
  *                           based upon v850 version
  *  Copyright (C) 2001,2002  NEC Corporation
@@ -49,6 +50,10 @@ C_ENTRY(start):
 	// standard sp->task ptr conversion functions later on
 	la	r1, r0, CSYM(init_task_union)+KERNEL_STACK_SIZE-4;
 
+#ifdef CONFIG_MBVANILLA_CMDLINE
+	// BAB 1/7/04 - Copy off cmdline pointer passed in by bootloader
+	swi	r5, r0, CSYM(bootloader_buf_addr)
+#endif	
 	// See if there's a platform-specific early-initialization routine
 	// defined; it's a weak symbol, so it will have an address of zero if
 	// there's not.
Index: arch/microblaze/kernel/setup.c
===================================================================
RCS file: /var/cvs/uClinux-2.4.x/arch/microblaze/kernel/setup.c,v
retrieving revision 1.5
diff -u -b -B -w -p -r1.5 setup.c
--- arch/microblaze/kernel/setup.c	2003/06/02 22:30:47	1.5
+++ arch/microblaze/kernel/setup.c	2004/01/09 02:27:07
@@ -1,6 +1,9 @@
 /*
- * arch/v850/kernel/setup.c -- Arch-dependent initialization functions
+ * arch/microblaze/kernel/setup.c -- Arch-dependent initialization functions
  *
+
+ *  Copyright (C) 2004	     Brett Boren <borenb@eng.uah.edu>
+ *  Copyright (C) 2003	     John Williams <jwilliams@itee.uq.edu.au>
  *  Copyright (C) 2001,2002  NEC Corporation
  *  Copyright (C) 2001,2002  Miles Bader <miles@gnu.org>
  *
@@ -9,6 +12,8 @@
  * archive for more details.
  *
  * Written by Miles Bader <miles@gnu.org>
+ * Microblaze port by John Williams <jwilliams@itee.uq.edu.au>
+ * Microblaze command line param handling by Brett Boren <borenb@eng.uah.edu>
  */
 
 #include <linux/sched.h>
@@ -37,8 +42,19 @@ extern char _init_start, _init_end;
 extern char _stext, _etext, _sdata, _edata, _sbss, _ebss;
 extern char _bootmap;
 
-char command_line[512];
 char saved_command_line[512];
+#ifdef CONFIG_MBVANILLA_CMDLINE
+/* This pointer gets set with the address of a buffer 
+   allocated (or specified) by the bootloader 
+*/
+/* 
+   BAB 1/8/2004 this initialization is needed to keep bootloader_buf_addr
+   from being placed in bss and getting cleared in mach_early_setup 
+*/
+void *bootloader_buf_addr = 0xFFFFFFFF;
+#else
+char command_line[512];
+#endif
 
 /* Memory not used by the kernel.  */
 static unsigned long total_ram_pages;
@@ -54,9 +70,22 @@ static void init_mem_alloc (unsigned lon
 
 void __init setup_arch (char **cmdline)
 {
+#ifdef CONFIG_MBVANILLA_CMDLINE
+	/* 
+	  If a cmdline has come from the bootlaoder, then this variable
+	  will have been initialised with the addr of the cmdline buffer.
+	  Just point the kernel there. 
+	*/
+	*cmdline = bootloader_buf_addr;
+
 	/* Keep a copy of command line */
+	memcpy (saved_command_line, *cmdline, sizeof saved_command_line);
+#else
 	*cmdline = command_line;
+
+	/* Keep a copy of command line */
 	memcpy (saved_command_line, command_line, sizeof saved_command_line);
+#endif
 	saved_command_line[sizeof saved_command_line - 1] = '\0';
 
 	console_verbose ();
/**********************************************************************
*
*  bootloader.c
*
*  Simple menu-driven bootloader for microblaze/uclinux/mbvanilla
*
*  uClinux kernel command line parameter handling 
*     by Brett Boren <borenb@eng.uah.edu>
*
*************************************************************************
*  Copyright (C) 2003 John Williams <jwilliams@itee.uq.edu.au>
*  Copyright (C) 2004 Brett Boren <borenb@eng.uah.edu>
*
*  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
*****************************************************************************/


/* Get general hardware memory map */
#include "xparameters.h"
#include "xbasic_types.h"
#include "xstatus.h"
#include "mb_interface.h"
#include "xuartlite_l.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include "flash.h"

/* Software flow control characters */
#define XON 17
#define XOFF 19

#define FLASH_BASEADDR 0xff000000

/* For now, hard code image length at 2MB */
unsigned int gImageLen=0x200000-1;

/* Physical start of flash memory */
unsigned int *flash_start = (unsigned int *)FLASH_BASEADDR;

/* Place within flash where os/rootfs image goes */
unsigned int *flash_image_table[]={
	(unsigned int *)(FLASH_BASEADDR+0x100000),
	(unsigned int *)(FLASH_BASEADDR+0x300000)};

int num_flash_images = sizeof(flash_image_table)/sizeof(unsigned int *);

/* Start and end of DDR ram */
unsigned int *ddr_start = (unsigned int *)XPAR_DDR_CONTROLLER_BASEADDR;
unsigned int *ddr_end = (unsigned int *)XPAR_DDR_CONTROLLER_HIGHADDR; 

/* Start and end of SRAM */
unsigned int *ram_start = (unsigned int *)0xffe00000;
unsigned int *ram_end = (unsigned int *)0xfff00000;

/* Static buffer to store kernel command line */
char cmdline[512];

/* Prototypes for menu functions */
typedef int (*menu_function)(void);
int menu_download_kernel(void);
int menu_image_into_flash(void);
int menu_execute_ram_image(void);
int menu_execute_flash_image(void);
int menu_erase_flash(void);
int menu_set_cmdline(void);

typedef struct {
	menu_function fn;
	char *menu_string;
} menu_item;

menu_item main_menu[]={
	{menu_execute_flash_image,"Execute Flash image"},
	{menu_execute_ram_image,"Execute RAM image"},
	{menu_erase_flash, "Erase Flash"},
	{menu_image_into_flash,"Write image into Flash"},
	{menu_set_cmdline,"Set kernel cmdline"},
	{0,0}};

// take an argument for the cmdline buffer
typedef void (*void_fn)(void *);
void_fn kernel_start;

char progress[]={'|','\\','-','/','|','\\','-','/','\0'};

/* A simple callback fn to pass to the flash routines.  A spinning bar
   will be displayed while the flash routines wait.
   Always return 0, ie never terminate the flash function */
int progress_callback(int x)
{
	static char *progress_ptr=progress;
	static skip_count=1000;

	if(skip_count--)
	{
		outbyte(*progress_ptr++);
		outbyte('\b');
	}
	else
		skip_count=1000;

	if(!*progress_ptr)
		progress_ptr=progress;

	return 0;
}

void progress_bar(int start, int finish, int current)
{
	int i, j;

	i=j=40*(current-start)/(finish-start);

	while(j--)
		outbyte('.');

	while(i--)
		outbyte('\b');
}


void memory_test(unsigned int *start, unsigned int *end)
{
	unsigned int *dst;

	print("\n\r\n\rTesting memory...");

	for(dst=start; dst<end; dst++)
		*dst=(unsigned int)dst;

	for(dst=start; dst<end; dst++)
		if(*dst!=(unsigned int)dst)
		{
			print("failed at 0x");
			putnum((int)dst);
			print("\n\rContents:0x");
			putnum((int)*dst);
			print(" expected 0x");
			putnum((unsigned int)dst);
			print("\n\rHalting...");
			for(;;)
				;
		}
}

void clear_memory(unsigned int *start, unsigned int *end)
{
	unsigned int *dst;

	for(dst=start; dst< end; dst++)
		*dst=0;
}

void copy_image(unsigned int *src, unsigned int *dst, unsigned int len)
{

	flash_read_reset();

	print("\n\rCopying kernel image...");
	while(len--)
	{
		*dst++=*src++;
	}
	print("done\n\r");
}

void verify_image(unsigned int *src, unsigned int *dst, unsigned int len)
{
	print("\n\rVerifying Kernel Image...");	

	flash_read_reset();
	while(len--)
	{
		if(*dst++ != *src++)
		{
			print("Error at ");
			putnum((int)dst-1);
			print("/");
			putnum((int)src-1);
			print("  Contents ");
			putnum(*(src-1));
			print("/");
			putnum(*(dst-1));
			print("\n\rHalting...");
			for(;;)
				;
		}
	}

	print("...done\n\r");
}

#if 0
int receive_line(char *buffer, int maxchars)
{
	int finished=0;

	outbyte(XOFF); 
	while(!finished)
	{

		/* Drain any chars currently in buffer */
		while(XUartLite_mGetStatusReg(XPAR_CONSOLE_UART_BASEADDR) & 
				XUL_SR_RX_FIFO_VALID_DATA)
		{
                	*buffer = (Xuint8)XIo_In32(XPAR_CONSOLE_UART_BASEADDR + XUL_RX_FIFO_OFFSET);

			if(*buffer=='\r' || !(--maxchars))
			{
				finished=1;
				break;
			}

			buffer++;
		}

		if(!finished)
		{
			int i;
                	XIo_Out32(((Xuint32)XPAR_CONSOLE_UART_BASEADDR) + XUL_TX_FIFO_OFFSET, XON);
		}
	}
	*buffer='\0';
	XIo_Out32(((Xuint32)XPAR_CONSOLE_UART_BASEADDR) + XUL_TX_FIFO_OFFSET, XOFF);
	return 0;
}

int menu_download_kernel(void)
{
	int finished=0;
	unsigned char *ptr=ddr_start;
	unsigned int bytes_rcvd=0;
	char keyword[]="xxsmoodgexx";
	int keywordlen = strlen(keyword);
	char buffer[256], *ptr1, *ptr2;

	xil_printf("Image will be loaded to %08x\n\r",ddr_start);

	xil_printf("Initiate file transfer now.  Enter '%s' when finished...\n\r",keyword);

	gImageLen=0;

	while(1)
	{
		/* Get the line */
		receive_line(buffer,256);

		/* Chew through any leading white space */
		ptr1=buffer;
		while(*ptr1&& isspace(*ptr1))
			ptr1++;
	
		/* And truncate any trailing white space */
		ptr2=ptr1;
		while(*ptr2 && !isspace(*ptr2))
			ptr2++;
		*ptr2='\0';

		xil_printf("%s\n\r",ptr1);
		
		/* Is it the magic word? */
		if(!strncmp(ptr1,keyword,keywordlen))
			break;
		 else
			gImageLen+=decode_srec_line(ptr1);
	}

	xil_printf("%08x bytes received\n\r",gImageLen);

	return 0;
}

#endif

int get_flash_number(void)
{
	int flash_slot=0;

	do {
		xil_printf("Which flash slot? (1-%d, 0 to cancel) ",num_flash_images);
		flash_slot= get_number();
	} while (flash_slot<0 || flash_slot > num_flash_images);

	xil_printf("you entered %d\n",flash_slot);

	return flash_slot;
}

int menu_image_into_flash(void)
{
	unsigned int *ddr_ptr = ddr_start, *flash_ptr;
	unsigned int words_to_write;
	int flash_slot=0;
	char ch;

	flash_slot = get_flash_number();
	
	if(!flash_slot)
		return -1;

	flash_ptr = flash_image_table[flash_slot-1];

	print("Are you sure? ");
	ch=inbyte();

	if(ch!='y' && ch!='Y')
		return -1;


	words_to_write=(gImageLen)/4;

	if(*flash_ptr != 0xFFFFFFFF)
	{
		print("Error - Flash must be erased first!\n");
		return -1;
	}

	while(words_to_write--)
	{
		flash_read_reset();
		flash_write_word(flash_ptr++,*(ddr_ptr++),NULL);
		progress_callback(0);
	}

	print("\n\rDone...\n\r");

	return 0;
}

int menu_erase_flash(void)
{
	char ch;

	print("This will the entire flash - are you sure? ");
	ch=inbyte();

	if(ch=='y' || ch=='Y')
	{
		print("Erasing flash...\n\r");
		flash_auto_chip_erase(progress_callback);
		print("\n\rDone.\n\r");
	}


	return 0;
}

int menu_execute_ram_image(void)
{
	if(!gImageLen)
	{
		print("Error - no image downloaded yet!\n\r");
		return -1;
	}

	kernel_start = (void_fn)ddr_start;
	kernel_start(cmdline);		/* Never returns */

	return 0;

}

int menu_execute_flash_image(void)
{
	
	unsigned int *ddr_ptr = ddr_start, *flash_ptr;
	int flash_slot = get_flash_number();
	
	if(!flash_slot)
		return -1;

	flash_ptr = flash_image_table[flash_slot-1];

	print("flash addr "); putnum((int)flash_ptr); print("\n\r");

	copy_image(flash_ptr, ddr_ptr, (gImageLen)/4);
	verify_image(flash_ptr, ddr_ptr, (gImageLen)/4);

	print("Jumping to kernel startup...\n\r\n\r");
	kernel_start = (void_fn)ddr_start;
	kernel_start(cmdline);
}

/* simple routine to get a number typed on stdin */
int get_number(void)
{
	char ch, buffer[100];
	int num_chars;

	num_chars=0;

	/* Read chars until buffer full or enter pressed */
	while((ch=inbyte()) != '\r' && num_chars < 99)
		if(isdigit(ch))
			outbyte(buffer[num_chars++]=ch);

	print("\n\r");

	buffer[num_chars]='\0';

	return atoi(buffer);
}

int do_menu(menu_item *menu)
{
	int i, menu_choice, num_items;

	outbyte(XON); 

	print("\n\n\r");
	print("J-Boot Menu\n\r");
	print("-----------\n\r");
	print("\n\r");

	while(menu[num_items].fn)
	{
		xil_printf("%d.    %s\n\r",num_items+1,
					menu[num_items].menu_string);
		num_items++;
	}

	print("\n\r");
	print("\n\r");

	do
	{
		print("Make your choice>");
		menu_choice = get_number();
	} while (menu_choice<1 || menu_choice > num_items);

	outbyte(XON); 

	return menu[menu_choice-1].fn();
}

int menu_set_cmdline(void)
{
  /* overwrite the first part of the address space since we're 
     pretty well guaranteed not to have to use it again */
  int i = 0;
  char ch = '\0';

  outbyte(XON); 

  print("Enter cmdline: ");

  ch = inbyte();

  // 0x0d is the carriage return (Enter) ascii value
  // cmdline buffer is only 512 bytes
  while (ch != 0x0d && i < 512-1)
    {
      cmdline[i++] = ch;
      outbyte(XON); 
      outbyte(ch);
      ch = inbyte();
    }

  cmdline[i] = '\0';

  outbyte(XON); 
}

int main(void)
{
	flash_set_base((unsigned int *)FLASH_BASEADDR);
	flash_read_reset();

	/* Ensure cmdline defaults to null (zero-length) string */
	cmdline[0]='\0';

	while(1)
		do_menu(main_menu);
}