diff -ur linux-2.4.2-org/arch/i386/kernel/process.c linux-2.4.2/arch/i386/kernel/process.c --- linux-2.4.2-org/arch/i386/kernel/process.c Fri Feb 9 20:29:44 2001 +++ linux-2.4.2/arch/i386/kernel/process.c Thu Feb 22 18:26:45 2001 @@ -153,11 +153,15 @@ static long no_idt[2]; static int reboot_mode; static int reboot_thru_bios; +static int reboot_quick; static int __init reboot_setup(char *str) { while(1) { switch (*str) { + case 'q': /* "quick" reboot (only reload operating system) */ + reboot_quick = 1; + break; case 'w': /* "warm" reboot (no memory testing etc) */ reboot_mode = 0x1234; break; @@ -251,12 +255,61 @@ break; } +/* The idea for the following code is stolen form Quarterdeck's QuickReboot. + However, it is quite problematic, because after switching back to real mode + it relies on the BIOS-variables (including IDT) being in the same state + as before the BIOS loaded the system the first time. If you boot with LILO, + you should have no problems with it, as LILO does not change critical data, + AFAIK. Problems begin, when you use LOADLIN - then DOS and some drivers you + possibly loaded have scrambled your BIOS data. In this case you need + something like Quarterdeck's DOSDATA.SYS. For more info on this topic look + at http://www.inf.tu-dresden.de/cgi-bin/cgiwrap/ob6/archive?info%20bs.asm + The PCI-Bus is not reset and thus may cause malfunction. + It is also known, that at least some SCSI-systems hang upon QReboot. + + Setup stack, reset drives and reload operating system. +*/ +static unsigned char real_mode_reinit [] = +{ + 0x0f, 0x20, 0xc0, /* mov eax,cr0 */ + 0x66, 0x83, 0xe0, 0x16, /* and eax,16h */ + 0x0f, 0x22, 0xc0, /* mov cr0,eax */ + 0x0f, 0x22, 0xd8, /* mov cr3,eax */ + 0xea, 0x12, 0x0f, 0x00, 0x00, /* jmp far rm */ + 0x33, 0xc0, /* rm: xor ax,ax */ + 0x8e, 0xd8, /* mov ds,ax */ + 0x8e, 0xd0, /* mov ss,ax */ + 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ + /* the following code searches for my qreboot-helper and uses it */ + 0xbf, 0xe0, 0x01, /* mov di,1e0h */ + 0xb9, 0x80, 0x00, /* mov cx,80h */ + 0x81, 0x3d, 0xcd, 0x19, /* lo: cmp [di],19cdh */ + 0x74, 0x16, /* je ma */ + 0x47, /* inc di */ + 0xe2, 0xf7, /* loop lo */ + /* reset video mode, drives and reload system */ + 0xb8, 0x03, 0x00, /* rb: mov ax,3 */ + 0xcd, 0x10, /* int 10h */ + 0xb4, 0x00, /* mov ah,0 */ + 0xb2, 0x00, /* mov dl,0 */ + 0xcd, 0x13, /* int 13h */ + 0xb4, 0x00, /* mov ah,0 */ + 0xb2, 0x80, /* mov dl,80h */ + 0xcd, 0x13, /* int 13h */ + 0xcd, 0x19, /* int 19h */ + /* revector int 0e0h and let the helper-code clean up things */ + 0xc6, 0x45, 0x01, 0xe0, /* ma: mov [di+1],0e0h */ + 0xc7, 0x06, 0x80, 0x03, 0x2a, 0x0f, /* mov [0e0h*4],offset rb */ + 0x8c, 0x0e, 0x82, 0x03, /* mov [0e0h*4+2],cs */ + 0xcd, 0x19 /* int 19h */ +}; + /* * Switch to real mode and then execute the code * specified by the code and length parameters. - * We assume that length will aways be less that 100! + * We assume that length will always be less than 240! */ -void machine_real_restart(unsigned char *code, int length) +void copy_and_switch(unsigned char *code1, int len1, unsigned char *code2, int len2) { unsigned long flags; @@ -307,9 +360,9 @@ off paging. Copy it near the end of the first page, out of the way of BIOS variables. */ - memcpy ((void *) (0x1000 - sizeof (real_mode_switch) - 100), - real_mode_switch, sizeof (real_mode_switch)); - memcpy ((void *) (0x1000 - 100), code, length); + memcpy ((unsigned char *)0xf00, code1, len1); + if (code2) + memcpy ((unsigned char *)0xf00 + len1, code2, len2); /* Set up the IDT for real mode. */ @@ -338,12 +391,15 @@ and the cache, switches to real mode, and jumps to the BIOS reset entry point. */ - __asm__ __volatile__ ("ljmp $0x0008,%0" - : - : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100))); + __asm__ __volatile__ ("ljmp $0x0008,$0xf00"); +} + +void machine_real_restart(unsigned char *code, int length) +{ + copy_and_switch(real_mode_switch, sizeof (real_mode_switch), code, length); } -void machine_restart(char * __unused) +void machine_restart(char * mode) { #if CONFIG_SMP /* @@ -354,7 +410,7 @@ disable_IO_APIC(); #endif - if(!reboot_thru_bios) { + if(!reboot_thru_bios && (!reboot_quick || mode)) { /* rebooting needs to touch the page at absolute addr 0 */ *((unsigned short *)__va(0x472)) = reboot_mode; for (;;) { @@ -371,7 +427,21 @@ } } - machine_real_restart(jump_to_bios, sizeof(jump_to_bios)); + if(reboot_quick && !mode) { + + outb_p(0x34, 0x43); /* reset timer */ + outb_p(0xff, 0x40); outb_p(0xff, 0x40); + + outb_p(0x11, 0x20); outb_p(0x08, 0x21); /* reset PICs */ + outb_p(0x04, 0x21); outb_p(0x01, 0x21); outb_p(0x00, 0x21); + outb_p(0x11, 0xa0); outb_p(0x70, 0xa1); + outb_p(0x02, 0xa1); outb_p(0x01, 0xa1); outb_p(0x00, 0xa1); + + copy_and_switch(real_mode_reinit, sizeof (real_mode_reinit), 0, 0); + + } else + + machine_real_restart(jump_to_bios, sizeof(jump_to_bios)); } void machine_halt(void) diff -ur linux-2.4.2-org/include/linux/reboot.h linux-2.4.2/include/linux/reboot.h --- linux-2.4.2-org/include/linux/reboot.h Fri Feb 9 23:46:13 2001 +++ linux-2.4.2/include/linux/reboot.h Sun Feb 25 21:42:37 2001 @@ -23,6 +23,7 @@ */ #define LINUX_REBOOT_CMD_RESTART 0x01234567 +#define LINUX_REBOOT_CMD_LRESTART 0x07654321 #define LINUX_REBOOT_CMD_HALT 0xCDEF0123 #define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000 diff -ur linux-2.4.2-org/kernel/sys.c linux-2.4.2/kernel/sys.c --- linux-2.4.2-org/kernel/sys.c Mon Oct 16 21:58:51 2000 +++ linux-2.4.2/kernel/sys.c Sun Feb 25 21:45:23 2001 @@ -288,6 +288,12 @@ machine_restart(NULL); break; + case LINUX_REBOOT_CMD_LRESTART: + notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); + printk(KERN_EMERG "Restarting system (without qreboot).\n"); + machine_restart("long"); + break; + case LINUX_REBOOT_CMD_CAD_ON: C_A_D = 1; break;