Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=136f1e7a8cb7d17ff91706518549697071640ae4
Commit:     136f1e7a8cb7d17ff91706518549697071640ae4
Parent:     a9622f6219ce58faba1417743bf3078501eb3434
Author:     Ingo Molnar <[EMAIL PROTECTED]>
AuthorDate: Wed Dec 20 11:53:32 2006 +0100
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Thu Dec 21 00:08:28 2006 -0800

    [PATCH] x86_64: fix boot time hang in detect_calgary()
    
    if CONFIG_CALGARY_IOMMU is built into the kernel via
    CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT, or is enabled via the
    iommu=calgary boot option, then the detect_calgary() function runs to
    detect the presence of a Calgary IOMMU.
    
    detect_calgary() first searches the BIOS EBDA area for a "rio_table_hdr"
    BIOS table. It has this parsing algorithm for the EBDA:
    
        while (offset) {
                ...
                /* The next offset is stored in the 1st word. 0 means no more */
                offset = *((unsigned short *)(ptr + offset));
        }
    
    got that? Lets repeat it slowly: we've got a BIOS-supplied data
    structure, plus Linux kernel code that will only break out of an
    infinite parsing loop once the BIOS gives a zero offset. Ok?
    
    Translation: what an excellent opportunity for BIOS writers to lock up
    the Linux boot process in an utterly hard to debug place! Indeed the
    BIOS jumped on that opportunity on my box, which has the following EBDA
    chaining layout:
    
      384, 65282, 65535, 65535, 65535, 65535, 65535, 65535 ...
    
    see the pattern? So my, definitely non-Calgary system happily locks up
    in detect_calgary()!
    
    the patch below fixes the boot hang by trusting the BIOS-supplied data
    structure a bit less: the parser always has to make forward progress,
    and if it doesnt, we break out of the loop and i get the expected kernel
    message:
    
      Calgary: Unable to locate Rio Grande Table in EBDA - bailing!
    
    Signed-off-by: Ingo Molnar <[EMAIL PROTECTED]>
    Acked-by: Muli Ben-Yehuda <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 arch/x86_64/kernel/pci-calgary.c |   11 ++++++++---
 1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/arch/x86_64/kernel/pci-calgary.c b/arch/x86_64/kernel/pci-calgary.c
index 3215675..87d90cb 100644
--- a/arch/x86_64/kernel/pci-calgary.c
+++ b/arch/x86_64/kernel/pci-calgary.c
@@ -1052,7 +1052,7 @@ void __init detect_calgary(void)
        void *tbl;
        int calgary_found = 0;
        unsigned long ptr;
-       int offset;
+       unsigned int offset, prev_offset;
        int ret;
 
        /*
@@ -1071,15 +1071,20 @@ void __init detect_calgary(void)
        ptr = (unsigned long)phys_to_virt(get_bios_ebda());
 
        rio_table_hdr = NULL;
+       prev_offset = 0;
        offset = 0x180;
-       while (offset) {
+       /*
+        * The next offset is stored in the 1st word.
+        * Only parse up until the offset increases:
+        */
+       while (offset > prev_offset) {
                /* The block id is stored in the 2nd word */
                if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){
                        /* set the pointer past the offset & block id */
                        rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 
4);
                        break;
                }
-               /* The next offset is stored in the 1st word. 0 means no more */
+               prev_offset = offset;
                offset = *((unsigned short *)(ptr + offset));
        }
        if (!rio_table_hdr) {
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to