Page 1 of 2

Shutting Down and Restarting the computer?

Posted: Fri Mar 02, 2007 1:41 am
by pcmattman
Does anyone know how to turn off or restart the computer from protected mode?

Posted: Fri Mar 02, 2007 1:53 am
by Solar
Restart is easy - do any cleanup necessary, then provoke a tripple-fault.

Power-Down can be triggered through APM or ACPI, both being power management protocols, with APM being obsolete by now. Check out the link, it should give you some starters. I think there are several threads on ACPI in this forum, too.

Posted: Fri Mar 02, 2007 1:56 am
by pcmattman
So, to restart I actually want to throw exceptions? How do I go about invoking a triple fault?

I'll look into that site for help with ACPI.

Posted: Fri Mar 02, 2007 2:33 am
by pcmattman
Woah... ACPI looks like hell to implement :(. I now have rebooting working, just need to get shutdown to work!

Does anyone known anywhere where I could find code examples for shutting down the PC via ACPI?

Posted: Fri Mar 02, 2007 3:10 am
by Combuster
Closest wiki match: Shutdown

Posted: Fri Mar 02, 2007 3:41 am
by XCHG
Oh you can also restart the PC by setting Bit#0 in keyboard’s microcontroller output port. First you have to poll Port 0x64 and see if Bit#0 is zero and then go on if yes. Then send the command 0xD1 to Port 0x64 and poll the same port and wait until Bit#0 is zero again just like you did at the first step. Now send the command 0x01 to Port 0x60 and the PC will be restarted. Here is the code that I wrote:

Code: Select all

  @@__WaitInputEmpty:
    IN      AL , 0x64
    TEST    AL , 0x01
    JNE     @@__WaitInputEmpty
  MOV     AL , 0xD1
  OUT     0x64 , AL
  @@__WaitAgain:
    IN      AL , 0x64
    TEST    AL , 0x01
    JNE     @@__WaitAgain
  MOV     AL , 0x01
  OUT     0x60 , AL
Hope that it helps.

Posted: Fri Mar 02, 2007 6:59 am
by Solar
pcmattman wrote:How do I go about invoking a triple fault?
I'm very surprised you don't know. A CPU exception triggers the assigned exception handler. If that handler triggers another exception, that is called a double fault. If the second exception handler triggers another exception, that's a tripple fault, and results in a CPU reset.

Usually you get a couple dozen of them before you get your GDT / LDT setup working, due to faulty table / handler setups.

Posted: Fri Mar 02, 2007 7:57 am
by ineo
Solar wrote:Usually you get a couple dozen of them before you get your GDT / LDT setup working, due to faulty table / handler setups.
Yes that was true some times ago, but nowadays thanks to the OS dev community you can start using a barebone that does a basic setup good enough to avoid such problems.

At least until you try to implement some new feature by yourself ;)

I was pleased to use the wiki to do some experiments some time ago, and the code was running almost perfectly the 1st time.

Posted: Fri Mar 02, 2007 9:15 am
by Solar
I see we have to weaken our content considerably so that people get to learn the hard way. :twisted: 8)

Posted: Fri Mar 02, 2007 4:05 pm
by Combuster
this should work on barebones:

Code: Select all

void reboot()
{
     volatile int * result = NULL;
     *result = 0;
     *result = 1 / *result;
}
:twisted:

Posted: Fri Mar 02, 2007 5:36 pm
by pcmattman
Solar wrote:
pcmattman wrote:How do I go about invoking a triple fault?
I'm very surprised you don't know. A CPU exception triggers the assigned exception handler. If that handler triggers another exception, that is called a double fault. If the second exception handler triggers another exception, that's a tripple fault, and results in a CPU reset.

Usually you get a couple dozen of them before you get your GDT / LDT setup working, due to faulty table / handler setups.
I do know how to invoke one accidentally (it happens quite a lot these days, I'm polishing my multitasker), but I don't know how to invoke one deliberately. Thankyou Combuster for your code sample, I'll have to try that.

Posted: Sat Mar 03, 2007 12:02 am
by Brendan
Hi,
pcmattman wrote:I do know how to invoke one accidentally (it happens quite a lot these days, I'm polishing my multitasker), but I don't know how to invoke one deliberately.
The most effective way to cause a triple fault is to load an IDT with "limit = 0", and then do any software interrupt:

Code: Select all

   section .data
rebootIDT:
    dd 0,0
   section .text

reboot:
   lidt [rebootIDT]
   int3
The interrupt will cause a general protection fault because of the IDT limit...


Cheers,

Brendan

Re: Shutting Down and Restarting the computer?

Posted: Sat Mar 03, 2007 1:51 am
by B.E
pcmattman wrote:Does anyone know how to turn off or restart the computer from protected mode?
You could jamp back to real mode and put 0x1234 for a warm reboot (0x0000 for a cold reboot) in to 0x0040:0x0072 and then jump to 0xFFFF:0x0000.

Also accounding to this, to shutdown, you can insert 0xABCD into to the 0x0040:0x0072 adress and jump.

The other way that is not a hack, is using the ACPI.

Also you could use the keyboard controler to reboot.

Posted: Sat Mar 03, 2007 5:21 am
by inflater
The *easiest* method to reset the system in real/unreal/protected mode is this:

OUT 0x64,0xFE

I discovered this when writing a PS/2 mouse driver :D

For shutdown, I use APM 1.2:

Code: Select all

;***remove this if your are using real/unreal mode***
xor eax,eax
mov cr0,eax
;***to here***
mov ax,5301h
xor bx,bx
int 15h
mov ax,530Eh
mov cx,0102h
int 15h
mov ax,5307h
mov bl,0001h
mov cx,0003h
int 15h
;***system will shutdown, or else there was a error***
Error:
The above code will shut down the system using APM 1.2 (predecessor of ACPI) and it works for me perfectly, if using ATX.

inflater

Re: Shutting Down and Restarting the computer?

Posted: Sat Mar 03, 2007 8:50 am
by Jeko
pcmattman wrote:Does anyone know how to turn off or restart the computer from protected mode?
try to read the code in panic.c in this italian open source project: http://sourceforge.net/projects/minirighi

this is the code:

Code: Select all

//! GDT to switch back into real mode.
/**
 *	The base address of the data descriptor is 0x100 because
 *	after the switching to real mode the segment registers
 *	don't have to be reloaded, the values are consistent for
 *	real mode operations.
 */
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 register.
	real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries },
	//! Real mode IDT register.
	real_mode_idt = { 0x3ff, 0 };

//! IDT register to generate a triple fault and reboot the system.
static long no_idt[2] = { 0, 0 };

//! \brief Switch back to real mode.
//! \note This is embedded code.
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 1f
	0x0f, 0x09,					// wbinvd
	0x24, 0x10,					// 1: andb $0x10, %al
	0x66, 0x0f, 0x22, 0xc0,				// movl %eax, %cr0
	// Reflush %cs register.
	0xea, 0x00, 0x10, 0x00, 0x00,			// ljmp $0x00, $0x1000
};

//! \brief Jump to the BIOS reboot routine.
//! \note This is 16-bit embedded code.
static unsigned char jump_to_bios_reboot[] =
{
	0xea, 0x00, 0x00, 0xff, 0xff,		// ljmp $0xffff, $0x0000
};

//! \brief Turn off power using APM BIOS features in real-mode.
//! \note This is 16-bit embedded code.
static unsigned char apm_real_mode_poweroff[] =
{
	// Setup the real-mode stack pointer.
	0xb8, 0x00, 0x80,			// movw $0x9000, %ax
	0x8e, 0xd0,				// movw %ax, %ss
	0xbc, 0x00, 0xf0,			// movw $0xf000, %sp

	// Connect to the APM BIOS.
	0xb8, 0x01, 0x53,			// mov $0x5301, %ax
	0x31, 0xdb,				// xor %bx, %bx
	0xcd, 0x15,				// int $0x15

	// Enable power management.
	0xb8, 0x08, 0x53,			// mov $0x5308, %ax
	0xbb, 0x01, 0x00,			// mov $0x0001, %bx
	0xb9, 0x01, 0x00,			// mov $0x0001, %cx
	0xcd, 0x15,				// int $0x15

	// Enable device power management.
	0xb8, 0x0d, 0x53,			// mov $0x530d, %ax
	0xbb, 0x01, 0x00,			// mov $0x0001, %bx
	0xb9, 0x01, 0x00,			// mov $0x0001, %cx
	0xcd, 0x15,				// int $0x15

	// Driver version.
	0xb8, 0x0e, 0x53,			// mov $0x530e, %ax
	0x31, 0xdb,				// xor %bx, %bx
	0xb9, 0x02, 0x01,			// mov $0x0102, %cx
	0xcd, 0x15,				// int $0x15

	// Power down.
	0xb8, 0x07, 0x53,			// mov $0x5307, %ax
	0xbb, 0x01, 0x00,			// mov $0x0001, %bx
	0xb9, 0x03, 0x00,			// mov $0x0003, %cx
	0xcd, 0x15,				// int $0x15

	// Something goes wrong...
	0xf4,					// 1: hlt
	0xeb, 0xfd,				// jmp 1b
};

//! \brief Switch back to real-mode and execute a routine.
//! \param code The 16-bit code to execute after the switch.
//! \param length The length of the code.
__NORETURN__ void machine_real_exec( unsigned char *code, int length )
{
	size_t addr;

	disable();

	// Identical map first 64k of memory.
	for( addr=0; addr<0x10000; addr+=PAGE_SIZE )
	{
		if( !map_page( addr, addr, P_WRITE | P_PRESENT ) )
		{
			// Something wrong with page-mapping =>
			// force a triple fault...
			__asm__ __volatile__ ( "lidt (%0)" : : "r"(no_idt) );
			__asm__ __volatile__ ( "int3" );
		}
	}
	flush_tlb_all();

	// Copy the real-mode switch code below the first page.
	memcpy( (void *)(0x1000-sizeof(real_mode_switch)),
		real_mode_switch, sizeof(real_mode_switch)
	);
	// Copy the real-mode code to the first page.
	memcpy( (void *)(0x1000), code, length );

	// Setup IDT for real mode.
	__asm__ __volatile__ ( "lidt %0" : : "m"(real_mode_idt) );

	// Setup GDT for real mode.
	__asm__ __volatile__ ( "lgdt %0" : : "m"(real_mode_gdt) );

	// Load data segment registers.
	__asm__ __volatile__ (
		"movl $0x10, %%eax\n"
		"movl %%eax, %%ds\n"
		"movl %%eax, %%es\n"
		"movl %%eax, %%fs\n"
		"movl %%eax, %%gs\n"
		"movl %%eax, %%ss"
		: : : "eax"
	);
	// Jump to the 16-bit code.
	__asm__ __volatile__ (
		"ljmp $0x0008, %0"
		: : "i"((void *)(0x1000-sizeof(real_mode_switch)))
	);
	for(;;)
		halt();
}

//! \brief Common system exit procedures.
void system_close()
{
	kprintf( "\n" );

	// Umount filesystems.
	printk( KERN_EMERG "Umounting filesystems...\n" );
	fat12_umount();

	// Sync device buffers.
	printk( KERN_EMERG "Flushing device buffers...\n" );
	fdc_sync();

	// Reset system clock.
	printk( KERN_EMERG "Reset system clock...\n" );
	stop_clock();

	// Turn the floppy motor off.
	printk( KERN_EMERG "Turn the floppy motor off...\n" );
	out( 0x03f2, 0x0c );
}

//! \brief Reboot the system using the keyboard CPU reset line.
void reboot()
{
	// System close operations.
	system_close();

	// Disable all interrupts.
	disable();

	// Inform the BIOS that this is a warm reboot.
	*(unsigned short *)PHYSICAL( 0x472 ) = 0x1234;

	// Pulse the CPU reset line.
	printk( KERN_EMERG "Pulse the CPU reset line.\n" );
	keyb_wait_controller();
	outb_p( 0x64, 0xFE );
	keyb_wait_controller();

	// Switch-back into real-mode to reboot.
	printk( KERN_EMERG "Cannot reboot by the keyboard controller...\n" );
	printk( KERN_EMERG "Switch back to real-mode...\n" );

	machine_real_exec( jump_to_bios_reboot, sizeof(jump_to_bios_reboot) );
}

//! \brief Halt the system down.
void poweroff()
{
	// System close operations.
	system_close();

	// Disable all interrupts.
	disable();

	// Switch-back into real-mode to shutdown.
	printk( KERN_EMERG "Switch back to real-mode...\n" );
	printk( KERN_EMERG "Power down.\n" );
	machine_real_exec( apm_real_mode_poweroff, sizeof(apm_real_mode_poweroff) );
}

//! \brief Report a critical error to the console.
//! \param msg The message you want to display.
//! \warning
//!	If this routine is invoked by a task it will be killed.
//! \warning
//!	PANIC: if this routine is invoked by the kernel
//!	the system will be halted!!!
void error(const byte *msg)
{
	disable();
	beep();
	set_color(LIGHT_RED);
	printk( KERN_ERR "ERROR: %s\n", msg );
	set_color(DEFAULT_COLOR);
	printk( KERN_ERR
		"Task [%s] (%i) killed!\n", get_pname(), get_pid() );

	sched_leave_critical_region();

	// If the kernel has generated this error HALT the system!
	if( get_pid() )
		auto_kill( 1 );
	for(;;)
		halt();
}