--- /dev/null
+/* (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> */
+/* (c) 2008, The V3VEE Project <http://www.v3vee.org> */
+
+#include <geekos/reboot.h>
+#include <libc/string.h>
+// from linux...
+
+
+#define RTC_PORT(x) (0x70 + (x))
+
+/* The following code and data reboots the machine by switching to real
+ mode and jumping to the BIOS reset entry point, as if the CPU has
+ really been reset. The previous version asked the keyboard
+ controller to pulse the CPU reset line, which is more thorough, but
+ doesn't work with at least one type of 486 motherboard. It is easy
+ to stop this code working; hence the copious comments. */
+
+static unsigned long long
+real_mode_gdt_entries [3] =
+{
+ 0x0000000000000000ULL, /* Null descriptor */
+ 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
+ 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
+};
+
+static struct
+{
+ unsigned short size __attribute__ ((packed));
+ unsigned long long * base __attribute__ ((packed));
+}
+real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries },
+ real_mode_idt = { 0x3ff, 0 };
+//no_idt = { 0, 0 };
+
+
+
+static unsigned char real_mode_switch [] =
+{
+ 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
+ 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */
+ 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
+ 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
+ 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */
+ 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */
+ 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */
+ 0x74, 0x02, /* jz f */
+ 0x0f, 0x09, /* wbinvd */
+ 0x24, 0x10, /* f: andb $0x10,al */
+ 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */
+};
+static unsigned char jump_to_bios [] =
+{
+ 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */
+};
+
+
+/*
+ * 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!
+ */
+void machine_real_restart() {
+ // unsigned long flags;
+
+ unsigned short rtp1, rtp2;
+ unsigned char val1, val2;
+
+ //JRL// local_irq_disable();
+
+ /* Write zero to CMOS register number 0x0f, which the BIOS POST
+ routine will recognize as telling it to do a proper reboot. (Well
+ that's what this book in front of me says -- it may only apply to
+ the Phoenix BIOS though, it's not clear). At the same time,
+ disable NMIs by setting the top bit in the CMOS address register,
+ as we're about to do peculiar things to the CPU. I'm not sure if
+ `outb_p' is needed instead of just `outb'. Use it to be on the
+ safe side. (Yes, CMOS_WRITE does outb_p's. - Paul G.)
+ */
+
+//JRL// spin_lock_irqsave(&rtc_lock, flags);
+//JRL// CMOS_WRITE(0x00, 0x8f);
+ val1 = 0x8f;
+ val2 = 0x00;
+ rtp1 = RTC_PORT(0);
+ rtp2 = RTC_PORT(1);
+
+ // outb_p(0x8f, RTC_PORT(0));
+ // outb_p(0x00, RTC_PORT(1));
+
+ __asm__ __volatile__ (
+ "outb %b0, %w1"
+ :
+ : "a" (val1), "Nd" (rtp1)
+ );
+
+ __asm__ __volatile__ (
+ "outb %b0, %w1"
+ :
+ : "a" (val2), "Nd" (rtp2)
+ );
+
+//JRL// spin_unlock_irqrestore(&rtc_lock, flags);
+
+ /* Remap the kernel at virtual address zero, as well as offset zero
+ from the kernel segment. This assumes the kernel segment starts at
+ virtual address PAGE_OFFSET. */
+
+//JRL// memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
+//JRL// sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
+
+ /*
+ * Use `swapper_pg_dir' as our page directory.
+ */
+//JRL// load_cr3(swapper_pg_dir);
+
+ /* Write 0x1234 to absolute memory location 0x472. The BIOS reads
+ this on booting to tell it to "Bypass memory test (also warm
+ boot)". This seems like a fairly standard thing that gets set by
+ REBOOT.COM programs, and the previous reset routine did this
+ too. */
+
+ *((unsigned short *)0x472) = 0x1234;
+
+ /* For the switch to real mode, copy some code to low memory. It has
+ to be in the first 64k because it is running in 16-bit mode, and it
+ has to have the same physical and virtual address, because it turns
+ 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), jump_to_bios, sizeof(jump_to_bios));
+
+ /* Set up the IDT for real mode. */
+
+ __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
+
+ /* Set up a GDT from which we can load segment descriptors for real
+ mode. The GDT is not used in real mode; it is just needed here to
+ prepare the descriptors. */
+
+ __asm__ __volatile__ ("lgdt %0" : : "m" (real_mode_gdt));
+
+ /* Load the data segment registers, and thus the descriptors ready for
+ real mode. The base address of each segment is 0x100, 16 times the
+ selector value being loaded here. This is so that the segment
+ registers don't have to be reloaded after switching to real mode:
+ the values are consistent for real mode operation already. */
+
+ __asm__ __volatile__ ("movl $0x0010,%%eax\n"
+ "\tmovl %%eax,%%ds\n"
+ "\tmovl %%eax,%%es\n"
+ "\tmovl %%eax,%%fs\n"
+ "\tmovl %%eax,%%gs\n"
+ "\tmovl %%eax,%%ss" : : : "eax");
+
+ /* Jump to the 16-bit code that we copied earlier. It disables paging
+ 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)));
+}