[PATCH] reset VGA adapters via BIOS on resume... (non-fbdev/con)

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

 



Hi all,
	At OLS at lot of people were giving out about cards not resuming,
so using a patch from Michael Marineau and help from lots of people
sitting around in a circle at OLS I've gotten a patch that restores video
on my laptop by going into real mode and re-posting the BIOS during
resume,

The current code can try to repost the BIOS in wakeup.S but this won't
work as ths PCI bus isn't alive enough, this patch waits until the pci
resume code is being called and it if finds a card with no driver loaded
it calls the reset code. This won't work if you are using radeonfb or any
fb, I've also written this code with Benh for radeonfb but the card
doesn't come up perfectly and we can crash later on...

Issues with this patch:
1. It uses CONFIG_X86 in C file, this should really be done with an arch
hook but I want to make sure that it solves the issues that people are
seeing and on what setups it breaks... I'd like this in -mm just to get
testing of it .. but I believe more discussion would be needed before
mainline could get it.
2. It traps back to realmode in kernel space.. (before reboot and apm bios
did this but to be honest a lot of people may not like this)
3. It traps back to realmode in kernel space (just in case you missed it)
4. See 2 and 3
5. Andi said he has code to do that trap on x86-64 systems, I might
integrate it in future versions...

My laptop still doesn't resume due to SATA no resuming properly on it..
but that is my next issue....

Dave.

-- 
David Airlie, Software Engineer
http://www.skynet.ie/~airlied / airlied at skynet.ie
Linux kernel - DRI, VAX / pam_smb / ILUG
diff --git a/arch/i386/kernel/acpi/sleep.c b/arch/i386/kernel/acpi/sleep.c
--- a/arch/i386/kernel/acpi/sleep.c
+++ b/arch/i386/kernel/acpi/sleep.c
@@ -5,15 +5,18 @@
  *  Copyright (C) 2001-2003 Pavel Machek <[email protected]>
  */
 
+#include <linux/module.h>
 #include <linux/acpi.h>
 #include <linux/bootmem.h>
 #include <linux/dmi.h>
+#include <linux/pci.h>
 #include <asm/smp.h>
 #include <asm/tlbflush.h>
 
 /* address in low memory of the wakeup routine. */
 unsigned long acpi_wakeup_address = 0;
 unsigned long acpi_video_flags;
+unsigned long acpi_video_devnum;
 extern char wakeup_start, wakeup_end;
 
 extern void zap_low_mappings(void);
@@ -56,6 +59,33 @@ void acpi_restore_state_mem (void)
 	zap_low_mappings();
 }
 
+/*
+ * acpi_vgapost
+ */
+
+extern void do_vgapost_lowlevel (unsigned long);
+
+void acpi_vgapost (struct pci_dev *pci_dev)
+{
+  unsigned long flags, saved_video_flags = acpi_video_flags;
+
+  acpi_video_devnum = (pci_dev->bus->number<<8) | (pci_dev->devfn);
+  acpi_video_flags = 1;
+  /* Map low memory and copy information */
+  init_low_mapping(swapper_pg_dir, USER_PTRS_PER_PGD);
+  memcpy((void *) acpi_wakeup_address, &wakeup_start, &wakeup_end - &wakeup_start);
+  acpi_copy_wakeup_routine(acpi_wakeup_address);
+  /* Tunnel thru real mode */
+  local_irq_save(flags);
+  do_vgapost_lowlevel(acpi_wakeup_address);
+  local_irq_restore(flags);
+  /* Restore mapping etc */
+  zap_low_mappings();
+  acpi_video_flags = saved_video_flags;
+}
+
+EXPORT_SYMBOL (acpi_vgapost);
+
 /**
  * acpi_reserve_bootmem - do _very_ early ACPI initialisation
  *
diff --git a/arch/i386/kernel/acpi/wakeup.S b/arch/i386/kernel/acpi/wakeup.S
--- a/arch/i386/kernel/acpi/wakeup.S
+++ b/arch/i386/kernel/acpi/wakeup.S
@@ -43,6 +43,7 @@ wakeup_code:
 
 	testl	$1, video_flags - wakeup_code
 	jz	1f
+	movl    video_devnum - wakeup_code, %eax
 	lcall   $0xc000,$3
 	movw	%cs, %ax
 	movw	%ax, %ds					# Bios might have played with that
@@ -98,6 +99,7 @@ real_save_cr4:	.long 0
 real_magic:	.long 0
 video_mode:	.long 0
 video_flags:	.long 0
+video_devnum:	.long 0
 real_efer_save_restore:	.long 0
 real_save_efer_edx: 	.long 0
 real_save_efer_eax: 	.long 0
@@ -171,6 +173,32 @@ check_vesa:
 
 _setbad: jmp setbad
 
+#
+#	Real mode switch - verbatim from reboot.c
+#
+go_real:
+	movl	%cr0, %eax
+	andl	$0x00000011, %eax
+	orl	$0x60000000, %eax
+	movl	%eax, %cr0
+	movl	%eax, %cr3
+	movl	%cr0, %ebx
+	andl	$0x60000000, %ebx
+	jz	1f
+	wbinvd
+1:	andb	$0x10, %al
+	movl	%eax, %cr0
+go_real_jmp:		.byte	0xea
+go_real_jmp_off:	.word	0x0000
+go_real_jmp_seg:	.word	0x0000
+#
+# 	Real mode descriptor table
+#
+		.align	8
+go_real_desc:	.quad	0x0000000000000000
+go_real_cseg:	.quad	0x00009a000000ffff
+go_real_dseg:	.quad	0x000092000000ffff
+
 	.code32
 	ALIGN
 
@@ -261,6 +289,8 @@ ENTRY(acpi_copy_wakeup_routine)
 	movl	%edx, video_mode - wakeup_start (%eax)
 	movl	acpi_video_flags, %edx
 	movl	%edx, video_flags - wakeup_start (%eax)
+	movl	acpi_video_devnum, %edx
+	movl	%edx, video_devnum - wakeup_start (%eax)
 	movl	$0x12345678, real_magic - wakeup_start (%eax)
 	movl	$0x12345678, saved_magic
 	ret
@@ -310,6 +340,59 @@ ENTRY(do_suspend_lowlevel_s4bios)
 	call acpi_enter_sleep_state_s4bios
 	ret
 
+ENTRY(do_vgapost_lowlevel)
+	# Convert target offset to physical address
+	movl	%eax, %ecx
+	subl	$__PAGE_OFFSET, %ecx
+	# Fixup GDT pointer
+	movl	%ecx, %edx
+	addl	$go_real_desc - wakeup_start, %edx
+	movl	%edx, go_real_gdt + 2
+	# Fixup 16-bit CS descriptor
+	movl	%ecx, %edx
+	movw	%dx, go_real_cseg - wakeup_start + 2 (%eax)
+	shrl	$16, %edx
+	movb	%dl, go_real_cseg - wakeup_start + 4 (%eax)
+	movb	%dh, go_real_cseg - wakeup_start + 7 (%eax)
+	# Fixup 16-bit jump
+	movl	%ecx, %edx
+	shrl	$4, %edx
+	movw	%dx, go_real_jmp_seg - wakeup_start (%eax)
+	# Save state and registers
+	call	save_processor_state
+	call	save_registers
+	# Reload page table with low mapping
+	movl	$swapper_pg_dir-__PAGE_OFFSET, %eax
+	movl	%eax, %cr3
+	# Load IDTR and GDTR for real mode
+	lidt	go_real_idt
+	lgdt	go_real_gdt
+	# Load DS & al
+	movl	$0x0010, %eax
+	movl	%eax, %ss
+	movl	%eax, %ds
+	movl	%eax, %es
+	movl	%eax, %fs
+	movl	%eax, %gs
+	# Load CS
+	call	$0x0008, $(go_real - wakeup_start)
+	# Phony return code
+	call	restore_registers
+	call	restore_processor_state
+	ret
+	
+	.align	4
+	.word	0
+go_real_idt:
+	.word	0x3ff
+	.long	0
+	
+	.align	4
+	.word	0
+go_real_gdt:
+	.word	3*8-1
+	.long	0
+
 ALIGN
 # saved registers
 saved_gdt:	.long	0,0
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -242,6 +242,9 @@ static int pci_device_suspend(struct dev
 	return i;
 }
 
+#ifdef CONFIG_X86
+void acpi_vgapost(struct pci_dev *);
+#endif
 
 /* 
  * Default resume method for devices that have no driver provided resume,
@@ -257,6 +260,13 @@ static void pci_default_resume(struct pc
 	/* if the device was busmaster before the suspend, make it busmaster again */
 	if (pci_dev->is_busmaster)
 		pci_set_master(pci_dev);
+
+#ifdef CONFIG_X86
+	if ((pci_dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
+		if (pci_dev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
+			acpi_vgapost(pci_dev);
+	}
+#endif
 }
 
 static int pci_device_resume(struct device * dev)

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]
  Powered by Linux