summaryrefslogtreecommitdiff
path: root/bootblocks/bzimage.c
diff options
context:
space:
mode:
Diffstat (limited to 'bootblocks/bzimage.c')
-rw-r--r--bootblocks/bzimage.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/bootblocks/bzimage.c b/bootblocks/bzimage.c
new file mode 100644
index 0000000..8d38753
--- /dev/null
+++ b/bootblocks/bzimage.c
@@ -0,0 +1,555 @@
+/*
+ * Load and execute a bzImage file from the device the 'open_file' and
+ * friends use.
+ */
+
+#include <stdio.h>
+#include "i86_funcs.h"
+#include "readfs.h"
+
+int auto_flag = 1;
+char * append_line = 0; /* A preset append line value */
+
+static char * initrd_name = 0; /* Name of init_ramdisk to load */
+static int vga_mode = -1; /* SVGA_MODE = normal */
+
+static char * read_cmdfile();
+static char * input_cmd();
+
+cmd_bzimage(ptr)
+char * ptr;
+{
+ char * image;
+ int ch;
+
+ if( (auto_flag=(ptr==0)) ) ptr="";
+
+ while(*ptr == ' ') ptr++;
+ image = ptr;
+ while(*ptr != ' ' && *ptr) ptr++;
+ ch = *ptr;
+ *ptr = '\0';
+ if( ch ) ptr++;
+ while(*ptr == '\n' || *ptr == ' ') ptr++;
+
+ if( *ptr == '\0' ) ptr = 0;
+ if( *image )
+ bzimage(image, ptr);
+ else
+ bzimage("vmlinuz", ptr);
+}
+
+bzimage(fname, command_line)
+char * fname;
+char * command_line;
+{
+ char buffer[1024];
+ long len;
+ char * ptr;
+ int low_sects;
+ unsigned int address;
+
+ if( open_file(fname) < 0 )
+ {
+ if( auto_flag == 0 )
+ printf("Cannot find file %s\n", fname);
+ return -1;
+ }
+
+ printf("Loading %s\n", fname);
+
+ if( read_block(buffer) < 0 || check_magics(buffer) < 0 )
+ {
+ printf("File %s isn't a bzImage.\n", fname);
+ return -1;
+ }
+
+ if( boot_mem_top < 0x9500 )
+ {
+ printf("There must be 640k of boot memory to load Linux\n");
+ return -1;
+ }
+
+ /* Guestimate how much the uncompressed kernel will use.
+ * I expect we could lookup the size in the gzip header but
+ * this is probably close enough (3*the size of the bzimage)
+ */
+ len = file_length() * 3 / 1024;
+ if( main_mem_top < len )
+ {
+ printf("This kernel needs at least %ld.%ldM of main memory\n",
+ len/1024, len*10/1024%10);
+ return -1;
+ }
+ if( main_mem_top < 3072 )
+ printf("RTFM warning: Linux really needs at least 4MB of memory.\n");
+
+ low_sects = buffer[497] + 1; /* setup sects + boot sector */
+ address = 0x900;
+
+ /* load the blocks */
+ rewind_file();
+ for(len = file_length(); len>0; len-=1024)
+ {
+ int v;
+
+ printf("%ldk to go \r", len/1024); fflush(stdout);
+
+ v = (bios_khit()&0x7F);
+ if( v == 3 || v == 27 )
+ {
+ printf("User interrupt!\n");
+ bios_getc();
+ return -1;
+ }
+
+ if( read_block(buffer) < 0 )
+ {
+ printf("Error loading %s\n", fname);
+ return -1;
+ }
+
+ for(v=0; v<1024; v+=512)
+ {
+ if( putsect(buffer+v, address) < 0 )
+ return -1;
+
+ address += 2;
+
+ if( low_sects )
+ {
+ low_sects--;
+ if( low_sects == 0 ) address = 0x1000;
+ }
+ }
+ }
+
+ /* Yesss, loaded! */
+ printf("Loaded, "); fflush(stdout);
+
+ /* Ok, deal with the command line */
+ if( build_linuxarg(auto_flag, fname, append_line, command_line) < 0 )
+ return -1;
+
+ if( initrd_name )
+ if( load_initrd(address) < 0 )
+ return -1;
+
+ printf("Starting ...\n");
+
+ if( x86 < 3 )
+ {
+ printf("RTFM error: Linux-i386 needs a CPU >= 80386\n");
+ if( !keep_going() ) return -1;
+ }
+
+ if( a20_closed() ) open_a20();
+ if( a20_closed() )
+ {
+ printf("Normal routine for opening A20 Gate failed, Trying PS/2 Bios\n");
+ bios_open_a20();
+ }
+ if( a20_closed() )
+ {
+ printf("All routines for opening A20 Gate failed, if I can't open it\n");
+ printf("then Linux probably can't either!\n");
+ if( !keep_going() ) return -1;
+ }
+
+ /* Patch setup to deactivate safety switch */
+ __set_es(0x9000);
+ __poke_es(0x210, 0xFF);
+
+ /* Set SVGA_MODE if not 'normal' */
+ if( vga_mode != -1 ) __doke_es(506, vga_mode);
+
+ /* Default boot drive is auto-detected floppy */
+ if( __peek_es(508) == 0 ) __poke_es(508, 0x200);
+
+ /* Finally do the deed */
+ {
+#asm
+ ! Kill the floppy motor, needed in case the kernel has no floppy driver.
+ mov dx,#0x3f2
+ xor al, al
+ outb
+
+ ! Setup required registers and go ...
+ mov ax,$9000
+ mov bx,$4000-12 ! Fix this to use boot_mem_top
+ mov es,ax
+ mov ds,ax
+ mov ss,ax
+ mov sp,bx
+ jmpi 0,$9020 ! Note SETUPSEG NOT INITSEG
+#endasm
+ }
+}
+
+check_magics(buffer)
+char * buffer;
+{
+ /* Boot sector magic number */
+ if( *(unsigned short*)(buffer+510) != 0xAA55 ) return -1;
+
+ /* Setup start */
+ if( memcmp(buffer+0x202, "HdrS", 4) != 0 ) return -1;
+
+ /* Setup version */
+ if( *(unsigned short*)(buffer+0x206) < 0x200 ) return -1;
+
+ /* Check load flags for bzImage */
+ if( (buffer[0x211] & 1) == 0 ) return -1;
+
+ /* Code 32 start address */
+ if( *(unsigned long*)(buffer+0x214) != 0x100000 ) return -1;
+
+ return 0;
+}
+
+putsect(buffer, address)
+char * buffer;
+unsigned int address;
+{
+ int rv, tc=3;
+retry:
+ tc--;
+#if 1
+ if( x86_emu )
+ {
+static unsigned int last_address = 0;
+ if( address <= last_address )
+ printf("Problem %d<=%d\n", address, last_address);
+ if( address < 0xA00 )
+ {
+ int i;
+ __set_es(address*16);
+ for(i=0; i<512; i++)
+ __poke_es(i, buffer[i]);
+ }
+ return 0;
+ }
+#endif
+ if( (rv=ext_put(buffer, address, 512)) != 0 )
+ {
+ switch(rv)
+ {
+ case 1:
+ printf("Parity error while copying to extended memory\n");
+ break;
+ case 2:
+ printf("Interrupt error while copying to extended memory\n");
+ if ( tc>0 ) goto retry;
+ break;
+ case 3:
+ printf("BIOS cannot open A20 Gate\n");
+ break;
+ case 0x80: case 0x86:
+ printf("BIOS has no extended memory support\n");
+ break;
+ default:
+ printf("Error %d while copying to extended memory\n", rv);
+ break;
+ }
+ if( x86 < 3 )
+ printf("RTFM error: Linux-i386 needs a CPU >= 80386\n");
+ else if( x86_emu )
+ printf("RTFM error: Linux-i386 cannot be run in an emulator.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static char *
+read_cmdfile(iname, extno)
+char * iname;
+int extno;
+{
+ char buffer[1024];
+ char buf[16];
+ long len;
+ char * ptr = strchr(iname, '.');
+
+ buf[8] = '\0';
+ strncpy(buf, iname, 8);
+ switch(extno)
+ {
+ case 1: strcat(buf, ".cmd"); break;
+ case 2: strcat(buf, ".app"); break;
+ case 3: strcat(buf, ".dfl"); break;
+ default: return 0;
+ }
+
+ if( ptr == 0 && open_file(buf) >= 0 )
+ {
+ /* Ah, a command line ... */
+ if( extno == 1 ) printf("Loading %s\n", buf);
+ len = file_length();
+ if( len > 1023 )
+ {
+ printf("Command line file %s truncated to 1023 characters\n", buf);
+ len = 1023;
+ }
+ if( read_block(buffer) >= 0 )
+ {
+ int i;
+ for(i=0; i<len; i++)
+ if( buffer[i] < ' ' ) buffer[i] = ' ';
+ buffer[len] = '\0';
+
+ return strdup(buffer);
+ }
+ }
+ return 0;
+}
+
+char *
+input_cmd(iname)
+char * iname;
+{
+ char buffer[1024];
+ char lbuf[20];
+ int cc;
+
+ for(;;)
+ {
+ printf("%s: ", iname); fflush(stdout);
+
+ cc = read(0, buffer, sizeof(buffer)-1);
+ if( cc <= 0 ) return 0;
+ buffer[cc] = 0;
+ if( cc == 1 && *buffer != '\n' )
+ {
+ putchar('\n');
+ cc = (buffer[0] & 0xFF);
+
+ if( cc == 0xAD ) /* ALT-X */
+ return 0;
+
+ sprintf(lbuf, "$%02x", cc);
+ cmd_help(lbuf);
+ continue;
+ }
+ if( buffer[cc-1] == '\n' ) buffer[cc-1] = '\0';
+
+ break;
+ }
+
+ return strdup(buffer);
+}
+
+build_linuxarg(auto_flg, image, append, cmd)
+int auto_flg;
+char *image, *append, *cmd;
+{
+static char * auto_str = "auto";
+static char * image_str = "BOOT_IMAGE=";
+ int len = 0;
+ char * ptr, *s, *d;
+
+ char * free_cmd = 0;
+ char * free_app = 0;
+ char * free_dfl = 0;
+
+ if( append == 0 )
+ append = free_app = read_cmdfile(image, 2);
+
+ if( cmd == 0 )
+ cmd = free_cmd = read_cmdfile(image, 1);
+
+ if( cmd == 0 )
+ free_dfl = read_cmdfile(image, 3);
+
+ close_file(); /* Free up loads a room */
+
+ if( cmd == 0 )
+ {
+ cmd = free_cmd = input_cmd(image);
+ auto_flg = 0;
+ }
+
+ if( cmd == 0 )
+ {
+ printf("Aborted execution\n");
+ return -1;
+ }
+ else if( *cmd == 0 )
+ cmd = free_dfl;
+
+ if( auto_flg ) len += strlen(auto_str) + 1;
+ if( image ) len += strlen(image_str) + strlen(image) + 1;
+ if( append ) len += strlen(append) + 1;
+ if( cmd ) len += strlen(cmd) + 1;
+
+ if( len == 0 ) return 0;
+
+ ptr = malloc(len+1);
+ if( ptr == 0 )
+ {
+ printf("Unable to create linux command line\n");
+ if( free_cmd ) free(free_cmd);
+ if( free_app ) free(free_app);
+ if( free_dfl ) free(free_dfl);
+ return -1;
+ }
+
+ *ptr = 0; ptr[1] = 0;
+ if( auto_flg ) { strcat(ptr, " "); strcat(ptr, auto_str); }
+ if( image ) { strcat(ptr, " "); strcat(ptr, image_str); strcat(ptr, image); }
+ if( append ) { strcat(ptr, " "); strcat(ptr, append); }
+ if( cmd ) { strcat(ptr, " "); strcat(ptr, cmd); }
+
+ if( free_cmd ) free(free_cmd);
+ if( free_app ) free(free_app);
+ if( free_dfl ) free(free_dfl);
+
+ if( (d = malloc(len+1)) == 0 )
+ {
+ printf("Unable to compact linux command line\n");
+ free(ptr);
+ return -1;
+ }
+ *d = '\0';
+ for(s=strtok(ptr, " "); s; s=strtok((char*)0, " "))
+ {
+ if( strncmp(s, "vga=",4) == 0 )
+ {
+ if( strncmp(s+4, "ask", 3) == 0 )
+ vga_mode = -3;
+ else
+ {
+ s+=4; getnum(&s, &vga_mode);
+ }
+ }
+ else if( strncmp(s, "ramdisk_file=", 13) == 0 )
+ {
+ if( initrd_name ) free(initrd_name);
+ if( s[13] )
+ initrd_name = strdup(s+13);
+ else
+ initrd_name = 0;
+ }
+ else
+ {
+ strcat(d, " ");
+ strcat(d, s);
+ }
+ }
+ free(ptr); ptr = d;
+ len = strlen(ptr);
+
+ if( len > 2048 )
+ {
+ printf("Linux command line truncated to 2047 characters\n");
+ ptr[2048] = 0;
+ len = 2048;
+ }
+
+ __set_es(0x9000);
+ __doke_es(0x0020, 0xA33F);
+ __doke_es(0x0022, 0x4000);
+
+ __movedata(__get_ds(), (unsigned)ptr+1, 0x9000, 0x4000, len);
+
+ free(ptr);
+
+ return 0;
+}
+
+keep_going()
+{
+static int burning = 0;
+ char buf[1];
+ int cc;
+
+ printf("Keep going ? "); fflush(stdout);
+
+ cc = read(0, buf, 1);
+ if( cc == 1 && (*buf == 'Y' || *buf == 'y') )
+ {
+ printf("Yes, Ok%s\n", burning?"":", burn baby burn!");
+ return burning=1;
+ }
+ printf("No, Ok returning to monitor prompt\n");
+ return 0;
+}
+
+load_initrd(k_top)
+unsigned int k_top;
+{
+ unsigned int address, rd_start, rd_len;
+ long file_len;
+ char * fname = initrd_name;
+
+ char buffer[1024];
+
+ /* Top of accessable memory */
+ if( main_mem_top >= 15360 ) address = 0xFFFF;
+ else address = 0x1000 + main_mem_top*4;
+
+ if( *initrd_name == '+' )
+ {
+ char buf[2];
+ fname++;
+ close_file();
+ printf("Insert root floppy and press return:"); fflush(stdout);
+ read(0, buf, 2);
+ }
+
+ if( open_file(fname) < 0 )
+ {
+ printf("Cannot open %s\n", fname);
+ return -1;
+ }
+ file_len = file_length();
+ rd_len = (file_len+1023)/1024;
+
+ if( file_len > 15000000L || k_top + rd_len*4 > address )
+ {
+ printf("Ramdisk file %s is too large to load\n", fname);
+ return -1;
+ }
+
+ rd_start = address - rd_len*4;
+ rd_start &= -16; /* Page boundry */
+ address = rd_start;
+
+ printf("Loading %s at 0x%x00\n", fname, rd_start);
+
+ for( ; rd_len>0 ; rd_len--)
+ {
+ int v = (bios_khit()&0x7F);
+ if( v == 3 || v == 27 )
+ {
+ printf("User interrupt!\n");
+ bios_getc();
+ return -1;
+ }
+
+ printf("%dk to go \r", rd_len); fflush(stdout);
+ if( read_block(buffer) < 0 )
+ {
+ printf("Read error loading ramdisk\n");
+ return -1;
+ }
+ if( putsect(buffer, address) < 0 ) return -1;
+ address+=2;
+ if( putsect(buffer+512, address) < 0 ) return -1;
+ address+=2;
+ }
+ printf("Loaded, ");
+
+ /* Tell the kernel where it is */
+ {
+ long tmp = ((long)rd_start << 8);
+
+ __set_es(0x9000);
+ __doke_es(0x218, (unsigned) tmp);
+ __doke_es(0x21A, (unsigned)(tmp>>16));
+
+ __doke_es(0x21C, (unsigned) file_len);
+ __doke_es(0x21E, (unsigned)(file_len>>16));
+ }
+
+ return 0;
+}