diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /roms/seabios/src/fw | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip | |
Diffstat (limited to 'roms/seabios/src/fw')
32 files changed, 7431 insertions, 0 deletions
| diff --git a/roms/seabios/src/fw/acpi-dsdt-cpu-hotplug.dsl b/roms/seabios/src/fw/acpi-dsdt-cpu-hotplug.dsl new file mode 100644 index 00000000..0f3e83b1 --- /dev/null +++ b/roms/seabios/src/fw/acpi-dsdt-cpu-hotplug.dsl @@ -0,0 +1,78 @@ +/**************************************************************** + * CPU hotplug + ****************************************************************/ + +Scope(\_SB) { +    /* Objects filled in by run-time generated SSDT */ +    External(NTFY, MethodObj) +    External(CPON, PkgObj) + +    /* Methods called by run-time generated SSDT Processor objects */ +    Method(CPMA, 1, NotSerialized) { +        // _MAT method - create an madt apic buffer +        // Arg0 = Processor ID = Local APIC ID +        // Local0 = CPON flag for this cpu +        Store(DerefOf(Index(CPON, Arg0)), Local0) +        // Local1 = Buffer (in madt apic form) to return +        Store(Buffer(8) {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}, Local1) +        // Update the processor id, lapic id, and enable/disable status +        Store(Arg0, Index(Local1, 2)) +        Store(Arg0, Index(Local1, 3)) +        Store(Local0, Index(Local1, 4)) +        Return (Local1) +    } +    Method(CPST, 1, NotSerialized) { +        // _STA method - return ON status of cpu +        // Arg0 = Processor ID = Local APIC ID +        // Local0 = CPON flag for this cpu +        Store(DerefOf(Index(CPON, Arg0)), Local0) +        If (Local0) { +            Return (0xF) +        } Else { +            Return (0x0) +        } +    } +    Method(CPEJ, 2, NotSerialized) { +        // _EJ0 method - eject callback +        Sleep(200) +    } + +    /* CPU hotplug notify method */ +    OperationRegion(PRST, SystemIO, 0xaf00, 32) +    Field(PRST, ByteAcc, NoLock, Preserve) { +        PRS, 256 +    } +    Method(PRSC, 0) { +        // Local5 = active cpu bitmap +        Store(PRS, Local5) +        // Local2 = last read byte from bitmap +        Store(Zero, Local2) +        // Local0 = Processor ID / APIC ID iterator +        Store(Zero, Local0) +        While (LLess(Local0, SizeOf(CPON))) { +            // Local1 = CPON flag for this cpu +            Store(DerefOf(Index(CPON, Local0)), Local1) +            If (And(Local0, 0x07)) { +                // Shift down previously read bitmap byte +                ShiftRight(Local2, 1, Local2) +            } Else { +                // Read next byte from cpu bitmap +                Store(DerefOf(Index(Local5, ShiftRight(Local0, 3))), Local2) +            } +            // Local3 = active state for this cpu +            Store(And(Local2, 1), Local3) + +            If (LNotEqual(Local1, Local3)) { +                // State change - update CPON with new state +                Store(Local3, Index(CPON, Local0)) +                // Do CPU notify +                If (LEqual(Local3, 1)) { +                    NTFY(Local0, 1) +                } Else { +                    NTFY(Local0, 3) +                } +            } +            Increment(Local0) +        } +    } +} diff --git a/roms/seabios/src/fw/acpi-dsdt-dbug.dsl b/roms/seabios/src/fw/acpi-dsdt-dbug.dsl new file mode 100644 index 00000000..276321f6 --- /dev/null +++ b/roms/seabios/src/fw/acpi-dsdt-dbug.dsl @@ -0,0 +1,26 @@ +/**************************************************************** + * Debugging + ****************************************************************/ + +Scope(\) { +    /* Debug Output */ +    OperationRegion(DBG, SystemIO, 0x0402, 0x01) +    Field(DBG, ByteAcc, NoLock, Preserve) { +        DBGB,   8, +    } + +    /* Debug method - use this method to send output to the QEMU +     * BIOS debug port.  This method handles strings, integers, +     * and buffers.  For example: DBUG("abc") DBUG(0x123) */ +    Method(DBUG, 1) { +        ToHexString(Arg0, Local0) +        ToBuffer(Local0, Local0) +        Subtract(SizeOf(Local0), 1, Local1) +        Store(Zero, Local2) +        While (LLess(Local2, Local1)) { +            Store(DerefOf(Index(Local0, Local2)), DBGB) +            Increment(Local2) +        } +        Store(0x0A, DBGB) +    } +} diff --git a/roms/seabios/src/fw/acpi-dsdt-hpet.dsl b/roms/seabios/src/fw/acpi-dsdt-hpet.dsl new file mode 100644 index 00000000..f33e5279 --- /dev/null +++ b/roms/seabios/src/fw/acpi-dsdt-hpet.dsl @@ -0,0 +1,36 @@ +/**************************************************************** + * HPET + ****************************************************************/ + +Scope(\_SB) { +    Device(HPET) { +        Name(_HID, EISAID("PNP0103")) +        Name(_UID, 0) +        OperationRegion(HPTM, SystemMemory, 0xFED00000, 0x400) +        Field(HPTM, DWordAcc, Lock, Preserve) { +            VEND, 32, +            PRD, 32, +        } +        Method(_STA, 0, NotSerialized) { +            Store(VEND, Local0) +            Store(PRD, Local1) +            ShiftRight(Local0, 16, Local0) +            If (LOr(LEqual(Local0, 0), LEqual(Local0, 0xffff))) { +                Return (0x0) +            } +            If (LOr(LEqual(Local1, 0), LGreater(Local1, 100000000))) { +                Return (0x0) +            } +            Return (0x0F) +        } +        Name(_CRS, ResourceTemplate() { +#if 0       /* This makes WinXP BSOD for not yet figured reasons. */ +            IRQNoFlags() {2, 8} +#endif +            Memory32Fixed(ReadOnly, +                0xFED00000,         // Address Base +                0x00000400,         // Address Length +                ) +        }) +    } +} diff --git a/roms/seabios/src/fw/acpi-dsdt-isa.dsl b/roms/seabios/src/fw/acpi-dsdt-isa.dsl new file mode 100644 index 00000000..23761dbb --- /dev/null +++ b/roms/seabios/src/fw/acpi-dsdt-isa.dsl @@ -0,0 +1,102 @@ +/* Common legacy ISA style devices. */ +Scope(\_SB.PCI0.ISA) { + +    Device(RTC) { +        Name(_HID, EisaId("PNP0B00")) +        Name(_CRS, ResourceTemplate() { +            IO(Decode16, 0x0070, 0x0070, 0x10, 0x02) +            IRQNoFlags() { 8 } +            IO(Decode16, 0x0072, 0x0072, 0x02, 0x06) +        }) +    } + +    Device(KBD) { +        Name(_HID, EisaId("PNP0303")) +        Method(_STA, 0, NotSerialized) { +            Return (0x0f) +        } +        Name(_CRS, ResourceTemplate() { +            IO(Decode16, 0x0060, 0x0060, 0x01, 0x01) +            IO(Decode16, 0x0064, 0x0064, 0x01, 0x01) +            IRQNoFlags() { 1 } +        }) +    } + +    Device(MOU) { +        Name(_HID, EisaId("PNP0F13")) +        Method(_STA, 0, NotSerialized) { +            Return (0x0f) +        } +        Name(_CRS, ResourceTemplate() { +            IRQNoFlags() { 12 } +        }) +    } + +    Device(FDC0) { +        Name(_HID, EisaId("PNP0700")) +        Method(_STA, 0, NotSerialized) { +            Store(FDEN, Local0) +            If (LEqual(Local0, 0)) { +                Return (0x00) +            } Else { +                Return (0x0F) +            } +        } +        Name(_CRS, ResourceTemplate() { +            IO(Decode16, 0x03F2, 0x03F2, 0x00, 0x04) +            IO(Decode16, 0x03F7, 0x03F7, 0x00, 0x01) +            IRQNoFlags() { 6 } +            DMA(Compatibility, NotBusMaster, Transfer8) { 2 } +        }) +    } + +    Device(LPT) { +        Name(_HID, EisaId("PNP0400")) +        Method(_STA, 0, NotSerialized) { +            Store(LPEN, Local0) +            If (LEqual(Local0, 0)) { +                Return (0x00) +            } Else { +                Return (0x0F) +            } +        } +        Name(_CRS, ResourceTemplate() { +            IO(Decode16, 0x0378, 0x0378, 0x08, 0x08) +            IRQNoFlags() { 7 } +        }) +    } + +    Device(COM1) { +        Name(_HID, EisaId("PNP0501")) +        Name(_UID, 0x01) +        Method(_STA, 0, NotSerialized) { +            Store(CAEN, Local0) +            If (LEqual(Local0, 0)) { +                Return (0x00) +            } Else { +                Return (0x0F) +            } +        } +        Name(_CRS, ResourceTemplate() { +            IO(Decode16, 0x03F8, 0x03F8, 0x00, 0x08) +            IRQNoFlags() { 4 } +        }) +    } + +    Device(COM2) { +        Name(_HID, EisaId("PNP0501")) +        Name(_UID, 0x02) +        Method(_STA, 0, NotSerialized) { +            Store(CBEN, Local0) +            If (LEqual(Local0, 0)) { +                Return (0x00) +            } Else { +                Return (0x0F) +            } +        } +        Name(_CRS, ResourceTemplate() { +            IO(Decode16, 0x02F8, 0x02F8, 0x00, 0x08) +            IRQNoFlags() { 3 } +        }) +    } +} diff --git a/roms/seabios/src/fw/acpi-dsdt-pci-crs.dsl b/roms/seabios/src/fw/acpi-dsdt-pci-crs.dsl new file mode 100644 index 00000000..d4218914 --- /dev/null +++ b/roms/seabios/src/fw/acpi-dsdt-pci-crs.dsl @@ -0,0 +1,90 @@ +/* PCI CRS (current resources) definition. */ +Scope(\_SB.PCI0) { + +    Name(CRES, ResourceTemplate() { +        WordBusNumber(ResourceProducer, MinFixed, MaxFixed, PosDecode, +            0x0000,             // Address Space Granularity +            0x0000,             // Address Range Minimum +            0x00FF,             // Address Range Maximum +            0x0000,             // Address Translation Offset +            0x0100,             // Address Length +            ,, ) +        IO(Decode16, +            0x0CF8,             // Address Range Minimum +            0x0CF8,             // Address Range Maximum +            0x01,               // Address Alignment +            0x08,               // Address Length +            ) +        WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, +            0x0000,             // Address Space Granularity +            0x0000,             // Address Range Minimum +            0x0CF7,             // Address Range Maximum +            0x0000,             // Address Translation Offset +            0x0CF8,             // Address Length +            ,, , TypeStatic) +        WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, +            0x0000,             // Address Space Granularity +            0x0D00,             // Address Range Minimum +            0xFFFF,             // Address Range Maximum +            0x0000,             // Address Translation Offset +            0xF300,             // Address Length +            ,, , TypeStatic) +        DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, +            0x00000000,         // Address Space Granularity +            0x000A0000,         // Address Range Minimum +            0x000BFFFF,         // Address Range Maximum +            0x00000000,         // Address Translation Offset +            0x00020000,         // Address Length +            ,, , AddressRangeMemory, TypeStatic) +        DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, +            0x00000000,         // Address Space Granularity +            0xE0000000,         // Address Range Minimum +            0xFEBFFFFF,         // Address Range Maximum +            0x00000000,         // Address Translation Offset +            0x1EC00000,         // Address Length +            ,, PW32, AddressRangeMemory, TypeStatic) +    }) + +    Name(CR64, ResourceTemplate() { +        QWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, +            0x00000000,          // Address Space Granularity +            0x8000000000,        // Address Range Minimum +            0xFFFFFFFFFF,        // Address Range Maximum +            0x00000000,          // Address Translation Offset +            0x8000000000,        // Address Length +            ,, PW64, AddressRangeMemory, TypeStatic) +    }) + +    Method(_CRS, 0) { +        /* Fields provided by dynamically created ssdt */ +        External(P0S, IntObj) +        External(P0E, IntObj) +        External(P1V, IntObj) +        External(P1S, BuffObj) +        External(P1E, BuffObj) +        External(P1L, BuffObj) + +        /* fixup 32bit pci io window */ +        CreateDWordField(CRES, \_SB.PCI0.PW32._MIN, PS32) +        CreateDWordField(CRES, \_SB.PCI0.PW32._MAX, PE32) +        CreateDWordField(CRES, \_SB.PCI0.PW32._LEN, PL32) +        Store(P0S, PS32) +        Store(P0E, PE32) +        Store(Add(Subtract(P0E, P0S), 1), PL32) + +        If (LEqual(P1V, Zero)) { +            Return (CRES) +        } + +        /* fixup 64bit pci io window */ +        CreateQWordField(CR64, \_SB.PCI0.PW64._MIN, PS64) +        CreateQWordField(CR64, \_SB.PCI0.PW64._MAX, PE64) +        CreateQWordField(CR64, \_SB.PCI0.PW64._LEN, PL64) +        Store(P1S, PS64) +        Store(P1E, PE64) +        Store(P1L, PL64) +        /* add window and return result */ +        ConcatenateResTemplate(CRES, CR64, Local0) +        Return (Local0) +    } +} diff --git a/roms/seabios/src/fw/acpi-dsdt.dsl b/roms/seabios/src/fw/acpi-dsdt.dsl new file mode 100644 index 00000000..3556dcaf --- /dev/null +++ b/roms/seabios/src/fw/acpi-dsdt.dsl @@ -0,0 +1,342 @@ +/* + * Bochs/QEMU ACPI DSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +ACPI_EXTRACT_ALL_CODE AmlCode + +DefinitionBlock ( +    "acpi-dsdt.aml",    // Output Filename +    "DSDT",             // Signature +    0x01,               // DSDT Compliance Revision +    "BXPC",             // OEMID +    "BXDSDT",           // TABLE ID +    0x1                 // OEM Revision +    ) +{ + +#include "acpi-dsdt-dbug.dsl" + + +/**************************************************************** + * PCI Bus definition + ****************************************************************/ + +    Scope(\_SB) { +        Device(PCI0) { +            Name(_HID, EisaId("PNP0A03")) +            Name(_ADR, 0x00) +            Name(_UID, 1) +        } +    } + +#include "acpi-dsdt-pci-crs.dsl" +#include "acpi-dsdt-hpet.dsl" + + +/**************************************************************** + * VGA + ****************************************************************/ + +    Scope(\_SB.PCI0) { +        Device(VGA) { +            Name(_ADR, 0x00020000) +            OperationRegion(PCIC, PCI_Config, Zero, 0x4) +            Field(PCIC, DWordAcc, NoLock, Preserve) { +                VEND, 32 +            } +            Method(_S1D, 0, NotSerialized) { +                Return (0x00) +            } +            Method(_S2D, 0, NotSerialized) { +                Return (0x00) +            } +            Method(_S3D, 0, NotSerialized) { +                If (LEqual(VEND, 0x1001b36)) { +                    Return (0x03)           // QXL +                } Else { +                    Return (0x00) +                } +            } +        } +    } + + +/**************************************************************** + * PIIX4 PM + ****************************************************************/ + +    Scope(\_SB.PCI0) { +        Device(PX13) { +            Name(_ADR, 0x00010003) +            OperationRegion(P13C, PCI_Config, 0x00, 0xff) +        } +    } + + +/**************************************************************** + * PIIX3 ISA bridge + ****************************************************************/ + +    Scope(\_SB.PCI0) { +        Device(ISA) { +            Name(_ADR, 0x00010000) + +            /* PIIX PCI to ISA irq remapping */ +            OperationRegion(P40C, PCI_Config, 0x60, 0x04) + +            /* enable bits */ +            Field(\_SB.PCI0.PX13.P13C, AnyAcc, NoLock, Preserve) { +                Offset(0x5f), +                , 7, +                LPEN, 1,         // LPT +                Offset(0x67), +                , 3, +                CAEN, 1,         // COM1 +                , 3, +                CBEN, 1,         // COM2 +            } +            Name(FDEN, 1) +        } +    } + +#include "acpi-dsdt-isa.dsl" + + +/**************************************************************** + * PCI hotplug + ****************************************************************/ + +    Scope(\_SB.PCI0) { +        OperationRegion(PCST, SystemIO, 0xae00, 0x08) +        Field(PCST, DWordAcc, NoLock, WriteAsZeros) { +            PCIU, 32, +            PCID, 32, +        } + +        OperationRegion(SEJ, SystemIO, 0xae08, 0x04) +        Field(SEJ, DWordAcc, NoLock, WriteAsZeros) { +            B0EJ, 32, +        } + +        /* Methods called by bulk generated PCI devices below */ + +        /* Methods called by hotplug devices */ +        Method(PCEJ, 1, NotSerialized) { +            // _EJ0 method - eject callback +            Store(ShiftLeft(1, Arg0), B0EJ) +        } + +        /* Hotplug notification method supplied by SSDT */ +        External(\_SB.PCI0.PCNT, MethodObj) + +        /* PCI hotplug notify method */ +        Method(PCNF, 0) { +            // Local0 = iterator +            Store(Zero, Local0) +            While (LLess(Local0, 31)) { +                Increment(Local0) +                If (And(PCIU, ShiftLeft(1, Local0))) { +                    PCNT(Local0, 1) +                } +                If (And(PCID, ShiftLeft(1, Local0))) { +                    PCNT(Local0, 3) +                } +            } +        } +    } + + +/**************************************************************** + * PCI IRQs + ****************************************************************/ + +    Scope(\_SB) { +        Scope(PCI0) { +            Name(_PRT, Package() { +                /* PCI IRQ routing table, example from ACPI 2.0a specification, +                   section 6.2.8.1 */ +                /* Note: we provide the same info as the PCI routing +                   table of the Bochs BIOS */ + +#define prt_slot(nr, lnk0, lnk1, lnk2, lnk3) \ +    Package() { nr##ffff, 0, lnk0, 0 }, \ +    Package() { nr##ffff, 1, lnk1, 0 }, \ +    Package() { nr##ffff, 2, lnk2, 0 }, \ +    Package() { nr##ffff, 3, lnk3, 0 } + +#define prt_slot0(nr) prt_slot(nr, LNKD, LNKA, LNKB, LNKC) +#define prt_slot1(nr) prt_slot(nr, LNKA, LNKB, LNKC, LNKD) +#define prt_slot2(nr) prt_slot(nr, LNKB, LNKC, LNKD, LNKA) +#define prt_slot3(nr) prt_slot(nr, LNKC, LNKD, LNKA, LNKB) + +                prt_slot0(0x0000), +                /* Device 1 is power mgmt device, and can only use irq 9 */ +                prt_slot(0x0001, LNKS, LNKB, LNKC, LNKD), +                prt_slot2(0x0002), +                prt_slot3(0x0003), +                prt_slot0(0x0004), +                prt_slot1(0x0005), +                prt_slot2(0x0006), +                prt_slot3(0x0007), +                prt_slot0(0x0008), +                prt_slot1(0x0009), +                prt_slot2(0x000a), +                prt_slot3(0x000b), +                prt_slot0(0x000c), +                prt_slot1(0x000d), +                prt_slot2(0x000e), +                prt_slot3(0x000f), +                prt_slot0(0x0010), +                prt_slot1(0x0011), +                prt_slot2(0x0012), +                prt_slot3(0x0013), +                prt_slot0(0x0014), +                prt_slot1(0x0015), +                prt_slot2(0x0016), +                prt_slot3(0x0017), +                prt_slot0(0x0018), +                prt_slot1(0x0019), +                prt_slot2(0x001a), +                prt_slot3(0x001b), +                prt_slot0(0x001c), +                prt_slot1(0x001d), +                prt_slot2(0x001e), +                prt_slot3(0x001f), +            }) +        } + +        Field(PCI0.ISA.P40C, ByteAcc, NoLock, Preserve) { +            PRQ0,   8, +            PRQ1,   8, +            PRQ2,   8, +            PRQ3,   8 +        } + +        Method(IQST, 1, NotSerialized) { +            // _STA method - get status +            If (And(0x80, Arg0)) { +                Return (0x09) +            } +            Return (0x0B) +        } +        Method(IQCR, 1, Serialized) { +            // _CRS method - get current settings +            Name(PRR0, ResourceTemplate() { +                Interrupt(, Level, ActiveHigh, Shared) { 0 } +            }) +            CreateDWordField(PRR0, 0x05, PRRI) +            If (LLess(Arg0, 0x80)) { +                Store(Arg0, PRRI) +            } +            Return (PRR0) +        } + +#define define_link(link, uid, reg)                             \ +        Device(link) {                                          \ +            Name(_HID, EISAID("PNP0C0F"))                       \ +            Name(_UID, uid)                                     \ +            Name(_PRS, ResourceTemplate() {                     \ +                Interrupt(, Level, ActiveHigh, Shared) {        \ +                    5, 10, 11                                   \ +                }                                               \ +            })                                                  \ +            Method(_STA, 0, NotSerialized) {                    \ +                Return (IQST(reg))                              \ +            }                                                   \ +            Method(_DIS, 0, NotSerialized) {                    \ +                Or(reg, 0x80, reg)                              \ +            }                                                   \ +            Method(_CRS, 0, NotSerialized) {                    \ +                Return (IQCR(reg))                              \ +            }                                                   \ +            Method(_SRS, 1, NotSerialized) {                    \ +                CreateDWordField(Arg0, 0x05, PRRI)              \ +                Store(PRRI, reg)                                \ +            }                                                   \ +        } + +        define_link(LNKA, 0, PRQ0) +        define_link(LNKB, 1, PRQ1) +        define_link(LNKC, 2, PRQ2) +        define_link(LNKD, 3, PRQ3) + +        Device(LNKS) { +            Name(_HID, EISAID("PNP0C0F")) +            Name(_UID, 4) +            Name(_PRS, ResourceTemplate() { +                Interrupt(, Level, ActiveHigh, Shared) { 9 } +            }) + +            // The SCI cannot be disabled and is always attached to GSI 9, +            // so these are no-ops.  We only need this link to override the +            // polarity to active high and match the content of the MADT. +            Method(_STA, 0, NotSerialized) { Return (0x0b) } +            Method(_DIS, 0, NotSerialized) { } +            Method(_CRS, 0, NotSerialized) { Return (_PRS) } +            Method(_SRS, 1, NotSerialized) { } +        } +    } + +#include "acpi-dsdt-cpu-hotplug.dsl" + + +/**************************************************************** + * General purpose events + ****************************************************************/ + +    Scope(\_GPE) { +        Name(_HID, "ACPI0006") + +        Method(_L00) { +        } +        Method(_E01) { +            // PCI hotplug event +            \_SB.PCI0.PCNF() +        } +        Method(_E02) { +            // CPU hotplug event +            \_SB.PRSC() +        } +        Method(_L03) { +        } +        Method(_L04) { +        } +        Method(_L05) { +        } +        Method(_L06) { +        } +        Method(_L07) { +        } +        Method(_L08) { +        } +        Method(_L09) { +        } +        Method(_L0A) { +        } +        Method(_L0B) { +        } +        Method(_L0C) { +        } +        Method(_L0D) { +        } +        Method(_L0E) { +        } +        Method(_L0F) { +        } +    } +} diff --git a/roms/seabios/src/fw/acpi.c b/roms/seabios/src/fw/acpi.c new file mode 100644 index 00000000..ecd1adcf --- /dev/null +++ b/roms/seabios/src/fw/acpi.c @@ -0,0 +1,685 @@ +// Support for generating ACPI tables (on emulators) +// DO NOT ADD NEW FEATURES HERE.  (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008-2010  Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // cpu_to_le16 +#include "config.h" // CONFIG_* +#include "dev-q35.h" +#include "dev-piix.h" +#include "hw/pci.h" // pci_find_init_device +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_INTERRUPT_LINE +#include "malloc.h" // free +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "romfile.h" // romfile_loadint +#include "std/acpi.h" // struct rsdp_descriptor +#include "string.h" // memset +#include "util.h" // MaxCountCPUs +#include "x86.h" // readl + +#include "src/fw/acpi-dsdt.hex" + +static void +build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev) +{ +    h->signature = cpu_to_le32(sig); +    h->length = cpu_to_le32(len); +    h->revision = rev; +    memcpy(h->oem_id, BUILD_APPNAME6, 6); +    memcpy(h->oem_table_id, BUILD_APPNAME4, 4); +    memcpy(h->oem_table_id + 4, (void*)&sig, 4); +    h->oem_revision = cpu_to_le32(1); +    memcpy(h->asl_compiler_id, BUILD_APPNAME4, 4); +    h->asl_compiler_revision = cpu_to_le32(1); +    h->checksum -= checksum(h, len); +} + +static void piix4_fadt_setup(struct pci_device *pci, void *arg) +{ +    struct fadt_descriptor_rev1 *fadt = arg; + +    fadt->model = 1; +    fadt->reserved1 = 0; +    fadt->sci_int = cpu_to_le16(PIIX_PM_INTRRUPT); +    fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); +    fadt->acpi_enable = PIIX_ACPI_ENABLE; +    fadt->acpi_disable = PIIX_ACPI_DISABLE; +    fadt->pm1a_evt_blk = cpu_to_le32(acpi_pm_base); +    fadt->pm1a_cnt_blk = cpu_to_le32(acpi_pm_base + 0x04); +    fadt->pm_tmr_blk = cpu_to_le32(acpi_pm_base + 0x08); +    fadt->gpe0_blk = cpu_to_le32(PIIX_GPE0_BLK); +    fadt->pm1_evt_len = 4; +    fadt->pm1_cnt_len = 2; +    fadt->pm_tmr_len = 4; +    fadt->gpe0_blk_len = PIIX_GPE0_BLK_LEN; +    fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported +    fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported +    fadt->flags = cpu_to_le32(ACPI_FADT_F_WBINVD | +                              ACPI_FADT_F_PROC_C1 | +                              ACPI_FADT_F_SLP_BUTTON | +                              ACPI_FADT_F_RTC_S4 | +                              ACPI_FADT_F_USE_PLATFORM_CLOCK); +} + +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +static void ich9_lpc_fadt_setup(struct pci_device *dev, void *arg) +{ +    struct fadt_descriptor_rev1 *fadt = arg; + +    fadt->model = 1; +    fadt->reserved1 = 0; +    fadt->sci_int = cpu_to_le16(9); +    fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); +    fadt->acpi_enable = ICH9_ACPI_ENABLE; +    fadt->acpi_disable = ICH9_ACPI_DISABLE; +    fadt->pm1a_evt_blk = cpu_to_le32(acpi_pm_base); +    fadt->pm1a_cnt_blk = cpu_to_le32(acpi_pm_base + 0x04); +    fadt->pm_tmr_blk = cpu_to_le32(acpi_pm_base + 0x08); +    fadt->gpe0_blk = cpu_to_le32(acpi_pm_base + ICH9_PMIO_GPE0_STS); +    fadt->pm1_evt_len = 4; +    fadt->pm1_cnt_len = 2; +    fadt->pm_tmr_len = 4; +    fadt->gpe0_blk_len = ICH9_PMIO_GPE0_BLK_LEN; +    fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported +    fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported +    fadt->flags = cpu_to_le32(ACPI_FADT_F_WBINVD | +                              ACPI_FADT_F_PROC_C1 | +                              ACPI_FADT_F_SLP_BUTTON | +                              ACPI_FADT_F_RTC_S4 | +                              ACPI_FADT_F_USE_PLATFORM_CLOCK); +} + +static const struct pci_device_id fadt_init_tbl[] = { +    /* PIIX4 Power Management device (for ACPI) */ +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, +               piix4_fadt_setup), +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, +               ich9_lpc_fadt_setup), +    PCI_DEVICE_END +}; + +static void fill_dsdt(struct fadt_descriptor_rev1 *fadt, void *dsdt) +{ +    if (fadt->dsdt) { +        free((void *)le32_to_cpu(fadt->dsdt)); +    } +    fadt->dsdt = cpu_to_le32((u32)dsdt); +    fadt->checksum -= checksum(fadt, sizeof(*fadt)); +    dprintf(1, "ACPI DSDT=%p\n", dsdt); +} + +static void * +build_fadt(struct pci_device *pci) +{ +    struct fadt_descriptor_rev1 *fadt = malloc_high(sizeof(*fadt)); +    struct facs_descriptor_rev1 *facs = memalign_high(64, sizeof(*facs)); + +    if (!fadt || !facs) { +        warn_noalloc(); +        return NULL; +    } + +    /* FACS */ +    memset(facs, 0, sizeof(*facs)); +    facs->signature = cpu_to_le32(FACS_SIGNATURE); +    facs->length = cpu_to_le32(sizeof(*facs)); + +    /* FADT */ +    memset(fadt, 0, sizeof(*fadt)); +    fadt->firmware_ctrl = cpu_to_le32((u32)facs); +    fadt->dsdt = 0;  /* dsdt will be filled later in acpi_setup() +                        by fill_dsdt() */ +    pci_init_device(fadt_init_tbl, pci, fadt); + +    build_header((void*)fadt, FACP_SIGNATURE, sizeof(*fadt), 1); + +    return fadt; +} + +static void* +build_madt(void) +{ +    int madt_size = (sizeof(struct multiple_apic_table) +                     + sizeof(struct madt_processor_apic) * MaxCountCPUs +                     + sizeof(struct madt_io_apic) +                     + sizeof(struct madt_intsrcovr) * 16 +                     + sizeof(struct madt_local_nmi)); + +    struct multiple_apic_table *madt = malloc_high(madt_size); +    if (!madt) { +        warn_noalloc(); +        return NULL; +    } +    memset(madt, 0, madt_size); +    madt->local_apic_address = cpu_to_le32(BUILD_APIC_ADDR); +    madt->flags = cpu_to_le32(1); +    struct madt_processor_apic *apic = (void*)&madt[1]; +    int i; +    for (i=0; i<MaxCountCPUs; i++) { +        apic->type = APIC_PROCESSOR; +        apic->length = sizeof(*apic); +        apic->processor_id = i; +        apic->local_apic_id = i; +        if (apic_id_is_present(apic->local_apic_id)) +            apic->flags = cpu_to_le32(1); +        else +            apic->flags = cpu_to_le32(0); +        apic++; +    } +    struct madt_io_apic *io_apic = (void*)apic; +    io_apic->type = APIC_IO; +    io_apic->length = sizeof(*io_apic); +    io_apic->io_apic_id = BUILD_IOAPIC_ID; +    io_apic->address = cpu_to_le32(BUILD_IOAPIC_ADDR); +    io_apic->interrupt = cpu_to_le32(0); + +    struct madt_intsrcovr *intsrcovr = (void*)&io_apic[1]; +    if (romfile_loadint("etc/irq0-override", 0)) { +        memset(intsrcovr, 0, sizeof(*intsrcovr)); +        intsrcovr->type   = APIC_XRUPT_OVERRIDE; +        intsrcovr->length = sizeof(*intsrcovr); +        intsrcovr->source = 0; +        intsrcovr->gsi    = cpu_to_le32(2); +        intsrcovr->flags  = cpu_to_le16(0); /* conforms to bus specifications */ +        intsrcovr++; +    } +    for (i = 1; i < 16; i++) { +        if (!(BUILD_PCI_IRQS & (1 << i))) +            /* No need for a INT source override structure. */ +            continue; +        memset(intsrcovr, 0, sizeof(*intsrcovr)); +        intsrcovr->type   = APIC_XRUPT_OVERRIDE; +        intsrcovr->length = sizeof(*intsrcovr); +        intsrcovr->source = i; +        intsrcovr->gsi    = cpu_to_le32(i); +        intsrcovr->flags  = cpu_to_le16(0xd); /* active high, level triggered */ +        intsrcovr++; +    } + +    struct madt_local_nmi *local_nmi = (void*)intsrcovr; +    local_nmi->type         = APIC_LOCAL_NMI; +    local_nmi->length       = sizeof(*local_nmi); +    local_nmi->processor_id = 0xff; /* all processors */ +    local_nmi->flags        = cpu_to_le16(0); +    local_nmi->lint         = 1; /* LINT1 */ +    local_nmi++; + +    build_header((void*)madt, APIC_SIGNATURE, (void*)local_nmi - (void*)madt, 1); +    return madt; +} + +// Encode a hex value +static inline char getHex(u32 val) { +    val &= 0x0f; +    return (val <= 9) ? ('0' + val) : ('A' + val - 10); +} + +// Encode a length in an SSDT. +static u8 * +encodeLen(u8 *ssdt_ptr, int length, int bytes) +{ +    switch (bytes) { +    default: +    case 4: ssdt_ptr[3] = ((length >> 20) & 0xff); +    case 3: ssdt_ptr[2] = ((length >> 12) & 0xff); +    case 2: ssdt_ptr[1] = ((length >> 4) & 0xff); +            ssdt_ptr[0] = (((bytes-1) & 0x3) << 6) | (length & 0x0f); +            break; +    case 1: ssdt_ptr[0] = length & 0x3f; +    } +    return ssdt_ptr + bytes; +} + +#include "src/fw/ssdt-proc.hex" + +/* 0x5B 0x83 ProcessorOp PkgLength NameString ProcID */ +#define PROC_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2) +#define PROC_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4) +#define PROC_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start) +#define PROC_SIZEOF (*ssdt_proc_end - *ssdt_proc_start) +#define PROC_AML (ssdp_proc_aml + *ssdt_proc_start) + +/* 0x5B 0x82 DeviceOp PkgLength NameString */ +#define PCIHP_OFFSET_HEX (*ssdt_pcihp_name - *ssdt_pcihp_start + 1) +#define PCIHP_OFFSET_ID (*ssdt_pcihp_id - *ssdt_pcihp_start) +#define PCIHP_OFFSET_ADR (*ssdt_pcihp_adr - *ssdt_pcihp_start) +#define PCIHP_OFFSET_EJ0 (*ssdt_pcihp_ej0 - *ssdt_pcihp_start) +#define PCIHP_SIZEOF (*ssdt_pcihp_end - *ssdt_pcihp_start) +#define PCIHP_AML (ssdp_pcihp_aml + *ssdt_pcihp_start) +#define PCI_SLOTS 32 + +#define SSDT_SIGNATURE 0x54445353 // SSDT +#define SSDT_HEADER_LENGTH 36 + +#include "src/fw/ssdt-misc.hex" +#include "src/fw/ssdt-pcihp.hex" + +#define PCI_RMV_BASE 0xae0c + +static u8* +build_notify(u8 *ssdt_ptr, const char *name, int skip, int count, +             const char *target, int ofs) +{ +    count -= skip; + +    *(ssdt_ptr++) = 0x14; // MethodOp +    ssdt_ptr = encodeLen(ssdt_ptr, 2+5+(12*count), 2); +    memcpy(ssdt_ptr, name, 4); +    ssdt_ptr += 4; +    *(ssdt_ptr++) = 0x02; // MethodOp + +    int i; +    for (i = skip; count-- > 0; i++) { +        *(ssdt_ptr++) = 0xA0; // IfOp +        ssdt_ptr = encodeLen(ssdt_ptr, 11, 1); +        *(ssdt_ptr++) = 0x93; // LEqualOp +        *(ssdt_ptr++) = 0x68; // Arg0Op +        *(ssdt_ptr++) = 0x0A; // BytePrefix +        *(ssdt_ptr++) = i; +        *(ssdt_ptr++) = 0x86; // NotifyOp +        memcpy(ssdt_ptr, target, 4); +        ssdt_ptr[ofs] = getHex(i >> 4); +        ssdt_ptr[ofs + 1] = getHex(i); +        ssdt_ptr += 4; +        *(ssdt_ptr++) = 0x69; // Arg1Op +    } +    return ssdt_ptr; +} + +static void patch_pcihp(int slot, u8 *ssdt_ptr, u32 eject) +{ +    ssdt_ptr[PCIHP_OFFSET_HEX] = getHex(slot >> 4); +    ssdt_ptr[PCIHP_OFFSET_HEX+1] = getHex(slot); +    ssdt_ptr[PCIHP_OFFSET_ID] = slot; +    ssdt_ptr[PCIHP_OFFSET_ADR + 2] = slot; + +    /* Runtime patching of EJ0: to disable hotplug for a slot, +     * replace the method name: _EJ0 by EJ0_. */ +    /* Sanity check */ +    if (memcmp(ssdt_ptr + PCIHP_OFFSET_EJ0, "_EJ0", 4)) { +        warn_internalerror(); +    } +    if (!eject) { +        memcpy(ssdt_ptr + PCIHP_OFFSET_EJ0, "EJ0_", 4); +    } +} + +static void* +build_ssdt(void) +{ +    int acpi_cpus = MaxCountCPUs > 0xff ? 0xff : MaxCountCPUs; +    int length = (sizeof(ssdp_misc_aml)                     // _S3_ / _S4_ / _S5_ +                  + (1+3+4)                                 // Scope(_SB_) +                  + (acpi_cpus * PROC_SIZEOF)               // procs +                  + (1+2+5+(12*acpi_cpus))                  // NTFY +                  + (6+2+1+(1*acpi_cpus))                   // CPON +                  + (1+3+4)                                 // Scope(PCI0) +                  + ((PCI_SLOTS - 1) * PCIHP_SIZEOF)        // slots +                  + (1+2+5+(12*(PCI_SLOTS - 1))));          // PCNT +    u8 *ssdt = malloc_high(length); +    if (! ssdt) { +        warn_noalloc(); +        return NULL; +    } +    u8 *ssdt_ptr = ssdt; + +    // Copy header and encode fwcfg values in the S3_ / S4_ / S5_ packages +    int sys_state_size; +    char *sys_states = romfile_loadfile("etc/system-states", &sys_state_size); +    if (!sys_states || sys_state_size != 6) +        sys_states = (char[]){128, 0, 0, 129, 128, 128}; + +    memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml)); +    if (!(sys_states[3] & 128)) +        ssdt_ptr[acpi_s3_name[0]] = 'X'; +    if (!(sys_states[4] & 128)) +        ssdt_ptr[acpi_s4_name[0]] = 'X'; +    else +        ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt[acpi_s4_pkg[0] + 3] = sys_states[4] & 127; + +    // store pci io windows +    *(u32*)&ssdt_ptr[acpi_pci32_start[0]] = cpu_to_le32(pcimem_start); +    *(u32*)&ssdt_ptr[acpi_pci32_end[0]] = cpu_to_le32(pcimem_end - 1); +    if (pcimem64_start) { +        ssdt_ptr[acpi_pci64_valid[0]] = 1; +        *(u64*)&ssdt_ptr[acpi_pci64_start[0]] = cpu_to_le64(pcimem64_start); +        *(u64*)&ssdt_ptr[acpi_pci64_end[0]] = cpu_to_le64(pcimem64_end - 1); +        *(u64*)&ssdt_ptr[acpi_pci64_length[0]] = cpu_to_le64( +            pcimem64_end - pcimem64_start); +    } else { +        ssdt_ptr[acpi_pci64_valid[0]] = 0; +    } + +    int pvpanic_port = romfile_loadint("etc/pvpanic-port", 0x0); +    *(u16 *)(ssdt_ptr + *ssdt_isa_pest) = pvpanic_port; + +    ssdt_ptr += sizeof(ssdp_misc_aml); + +    // build Scope(_SB_) header +    *(ssdt_ptr++) = 0x10; // ScopeOp +    ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3); +    *(ssdt_ptr++) = '_'; +    *(ssdt_ptr++) = 'S'; +    *(ssdt_ptr++) = 'B'; +    *(ssdt_ptr++) = '_'; + +    // build Processor object for each processor +    int i; +    for (i=0; i<acpi_cpus; i++) { +        memcpy(ssdt_ptr, PROC_AML, PROC_SIZEOF); +        ssdt_ptr[PROC_OFFSET_CPUHEX] = getHex(i >> 4); +        ssdt_ptr[PROC_OFFSET_CPUHEX+1] = getHex(i); +        ssdt_ptr[PROC_OFFSET_CPUID1] = i; +        ssdt_ptr[PROC_OFFSET_CPUID2] = i; +        ssdt_ptr += PROC_SIZEOF; +    } + +    // build "Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}" +    // Arg0 = Processor ID = APIC ID +    ssdt_ptr = build_notify(ssdt_ptr, "NTFY", 0, acpi_cpus, "CP00", 2); + +    // build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" +    *(ssdt_ptr++) = 0x08; // NameOp +    *(ssdt_ptr++) = 'C'; +    *(ssdt_ptr++) = 'P'; +    *(ssdt_ptr++) = 'O'; +    *(ssdt_ptr++) = 'N'; +    *(ssdt_ptr++) = 0x12; // PackageOp +    ssdt_ptr = encodeLen(ssdt_ptr, 2+1+(1*acpi_cpus), 2); +    *(ssdt_ptr++) = acpi_cpus; +    for (i=0; i<acpi_cpus; i++) +        *(ssdt_ptr++) = (apic_id_is_present(i)) ? 0x01 : 0x00; + +    // build Scope(PCI0) opcode +    *(ssdt_ptr++) = 0x10; // ScopeOp +    ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3); +    *(ssdt_ptr++) = 'P'; +    *(ssdt_ptr++) = 'C'; +    *(ssdt_ptr++) = 'I'; +    *(ssdt_ptr++) = '0'; + +    // build Device object for each slot +    u32 rmvc_pcrm = inl(PCI_RMV_BASE); +    for (i=1; i<PCI_SLOTS; i++) { +        u32 eject = rmvc_pcrm & (0x1 << i); +        memcpy(ssdt_ptr, PCIHP_AML, PCIHP_SIZEOF); +        patch_pcihp(i, ssdt_ptr, eject != 0); +        ssdt_ptr += PCIHP_SIZEOF; +    } + +    ssdt_ptr = build_notify(ssdt_ptr, "PCNT", 1, PCI_SLOTS, "S00_", 1); + +    build_header((void*)ssdt, SSDT_SIGNATURE, ssdt_ptr - ssdt, 1); + +    //hexdump(ssdt, ssdt_ptr - ssdt); + +    return ssdt; +} + +#define HPET_ID         0x000 +#define HPET_PERIOD     0x004 + +static void* +build_hpet(void) +{ +    struct acpi_20_hpet *hpet; +    const void *hpet_base = (void *)BUILD_HPET_ADDRESS; +    u32 hpet_vendor = readl(hpet_base + HPET_ID) >> 16; +    u32 hpet_period = readl(hpet_base + HPET_PERIOD); + +    if (hpet_vendor == 0 || hpet_vendor == 0xffff || +        hpet_period == 0 || hpet_period > 100000000) +        return NULL; + +    hpet = malloc_high(sizeof(*hpet)); +    if (!hpet) { +        warn_noalloc(); +        return NULL; +    } + +    memset(hpet, 0, sizeof(*hpet)); +    /* Note timer_block_id value must be kept in sync with value advertised by +     * emulated hpet +     */ +    hpet->timer_block_id = cpu_to_le32(0x8086a201); +    hpet->addr.address = cpu_to_le64(BUILD_HPET_ADDRESS); +    build_header((void*)hpet, HPET_SIGNATURE, sizeof(*hpet), 1); + +    return hpet; +} + +static void +acpi_build_srat_memory(struct srat_memory_affinity *numamem, +                       u64 base, u64 len, int node, int enabled) +{ +    numamem->type = SRAT_MEMORY; +    numamem->length = sizeof(*numamem); +    memset(numamem->proximity, 0, 4); +    numamem->proximity[0] = node; +    numamem->flags = cpu_to_le32(!!enabled); +    numamem->base_addr = cpu_to_le64(base); +    numamem->range_length = cpu_to_le64(len); +} + +static void * +build_srat(void) +{ +    int numadatasize, numacpusize; +    u64 *numadata = romfile_loadfile("etc/numa-nodes", &numadatasize); +    u64 *numacpumap = romfile_loadfile("etc/numa-cpu-map", &numacpusize); +    if (!numadata || !numacpumap) +        goto fail; +    int max_cpu = numacpusize / sizeof(u64); +    int nb_numa_nodes = numadatasize / sizeof(u64); + +    struct system_resource_affinity_table *srat; +    int srat_size = sizeof(*srat) + +        sizeof(struct srat_processor_affinity) * max_cpu + +        sizeof(struct srat_memory_affinity) * (nb_numa_nodes + 2); + +    srat = malloc_high(srat_size); +    if (!srat) { +        warn_noalloc(); +        goto fail; +    } + +    memset(srat, 0, srat_size); +    srat->reserved1=cpu_to_le32(1); +    struct srat_processor_affinity *core = (void*)(srat + 1); +    int i; +    u64 curnode; + +    for (i = 0; i < max_cpu; ++i) { +        core->type = SRAT_PROCESSOR; +        core->length = sizeof(*core); +        core->local_apic_id = i; +        curnode = *numacpumap++; +        core->proximity_lo = curnode; +        memset(core->proximity_hi, 0, 3); +        core->local_sapic_eid = 0; +        if (apic_id_is_present(i)) +            core->flags = cpu_to_le32(1); +        else +            core->flags = cpu_to_le32(0); +        core++; +    } + + +    /* the memory map is a bit tricky, it contains at least one hole +     * from 640k-1M and possibly another one from 3.5G-4G. +     */ +    struct srat_memory_affinity *numamem = (void*)core; +    int slots = 0; +    u64 mem_len, mem_base, next_base = 0; + +    acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1); +    next_base = 1024 * 1024; +    numamem++; +    slots++; +    for (i = 1; i < nb_numa_nodes + 1; ++i) { +        mem_base = next_base; +        mem_len = *numadata++; +        if (i == 1) +            mem_len -= 1024 * 1024; +        next_base = mem_base + mem_len; + +        /* Cut out the PCI hole */ +        if (mem_base <= RamSize && next_base > RamSize) { +            mem_len -= next_base - RamSize; +            if (mem_len > 0) { +                acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); +                numamem++; +                slots++; +            } +            mem_base = 1ULL << 32; +            mem_len = next_base - RamSize; +            next_base += (1ULL << 32) - RamSize; +        } +        acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); +        numamem++; +        slots++; +    } +    for (; slots < nb_numa_nodes + 2; slots++) { +        acpi_build_srat_memory(numamem, 0, 0, 0, 0); +        numamem++; +    } + +    build_header((void*)srat, SRAT_SIGNATURE, srat_size, 1); + +    free(numadata); +    free(numacpumap); +    return srat; +fail: +    free(numadata); +    free(numacpumap); +    return NULL; +} + +static void * +build_mcfg_q35(void) +{ +    struct acpi_table_mcfg *mcfg; + +    int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); +    mcfg = malloc_high(len); +    if (!mcfg) { +        warn_noalloc(); +        return NULL; +    } +    memset(mcfg, 0, len); +    mcfg->allocation[0].address = cpu_to_le64(Q35_HOST_BRIDGE_PCIEXBAR_ADDR); +    mcfg->allocation[0].pci_segment = cpu_to_le16(Q35_HOST_PCIE_PCI_SEGMENT); +    mcfg->allocation[0].start_bus_number = Q35_HOST_PCIE_START_BUS_NUMBER; +    mcfg->allocation[0].end_bus_number = Q35_HOST_PCIE_END_BUS_NUMBER; + +    build_header((void *)mcfg, MCFG_SIGNATURE, len, 1); +    return mcfg; +} + +static const struct pci_device_id acpi_find_tbl[] = { +    /* PIIX4 Power Management device. */ +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL), +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, NULL), +    PCI_DEVICE_END, +}; + +#define MAX_ACPI_TABLES 20 +void +acpi_setup(void) +{ +    if (! CONFIG_ACPI) +        return; + +    dprintf(3, "init ACPI tables\n"); + +    // This code is hardcoded for PIIX4 Power Management device. +    struct pci_device *pci = pci_find_init_device(acpi_find_tbl, NULL); +    if (!pci) +        // Device not found +        return; + +    // Build ACPI tables +    u32 tables[MAX_ACPI_TABLES], tbl_idx = 0; + +#define ACPI_INIT_TABLE(X)                                   \ +    do {                                                     \ +        tables[tbl_idx] = cpu_to_le32((u32)(X));             \ +        if (le32_to_cpu(tables[tbl_idx]))                    \ +            tbl_idx++;                                       \ +    } while(0) + +    struct fadt_descriptor_rev1 *fadt = build_fadt(pci); +    ACPI_INIT_TABLE(fadt); +    ACPI_INIT_TABLE(build_ssdt()); +    ACPI_INIT_TABLE(build_madt()); +    ACPI_INIT_TABLE(build_hpet()); +    ACPI_INIT_TABLE(build_srat()); +    if (pci->device == PCI_DEVICE_ID_INTEL_ICH9_LPC) +        ACPI_INIT_TABLE(build_mcfg_q35()); + +    struct romfile_s *file = NULL; +    for (;;) { +        file = romfile_findprefix("acpi/", file); +        if (!file) +            break; +        struct acpi_table_header *table = malloc_high(file->size); +        if (!table) { +            warn_noalloc(); +            continue; +        } +        int ret = file->copy(file, table, file->size); +        if (ret <= sizeof(*table)) +            continue; +        if (table->signature == DSDT_SIGNATURE) { +            if (fadt) { +                fill_dsdt(fadt, table); +            } +        } else { +            ACPI_INIT_TABLE(table); +        } +        if (tbl_idx == MAX_ACPI_TABLES) { +            warn_noalloc(); +            break; +        } +    } + +    if (CONFIG_ACPI_DSDT && fadt && !fadt->dsdt) { +        /* default DSDT */ +        struct acpi_table_header *dsdt = malloc_high(sizeof(AmlCode)); +        if (!dsdt) { +            warn_noalloc(); +            return; +        } +        memcpy(dsdt, AmlCode, sizeof(AmlCode)); +        fill_dsdt(fadt, dsdt); +        /* Strip out compiler-generated header if any */ +        memset(dsdt, 0, sizeof *dsdt); +        build_header(dsdt, DSDT_SIGNATURE, sizeof(AmlCode), 1); +    } + +    // Build final rsdt table +    struct rsdt_descriptor_rev1 *rsdt; +    size_t rsdt_len = sizeof(*rsdt) + sizeof(u32) * tbl_idx; +    rsdt = malloc_high(rsdt_len); +    if (!rsdt) { +        warn_noalloc(); +        return; +    } +    memset(rsdt, 0, rsdt_len); +    memcpy(rsdt->table_offset_entry, tables, sizeof(u32) * tbl_idx); +    build_header((void*)rsdt, RSDT_SIGNATURE, rsdt_len, 1); + +    // Build rsdp pointer table +    struct rsdp_descriptor rsdp; +    memset(&rsdp, 0, sizeof(rsdp)); +    rsdp.signature = cpu_to_le64(RSDP_SIGNATURE); +    memcpy(rsdp.oem_id, BUILD_APPNAME6, 6); +    rsdp.rsdt_physical_address = cpu_to_le32((u32)rsdt); +    rsdp.checksum -= checksum(&rsdp, 20); +    copy_acpi_rsdp(&rsdp); +} diff --git a/roms/seabios/src/fw/biostables.c b/roms/seabios/src/fw/biostables.c new file mode 100644 index 00000000..50a891be --- /dev/null +++ b/roms/seabios/src/fw/biostables.c @@ -0,0 +1,461 @@ +// Support for manipulating bios tables (pir, mptable, acpi, smbios). +// +// Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // le32_to_cpu +#include "config.h" // CONFIG_* +#include "malloc.h" // malloc_fseg +#include "output.h" // dprintf +#include "hw/pci.h" // pci_config_writeb +#include "std/acpi.h" // struct rsdp_descriptor +#include "std/mptable.h" // MPTABLE_SIGNATURE +#include "std/pirtable.h" // struct pir_header +#include "std/smbios.h" // struct smbios_entry_point +#include "romfile.h" +#include "string.h" // memcpy +#include "util.h" // copy_table +#include "x86.h" // outb + +struct pir_header *PirAddr VARFSEG; + +void +copy_pir(void *pos) +{ +    struct pir_header *p = pos; +    if (p->signature != PIR_SIGNATURE) +        return; +    if (PirAddr) +        return; +    if (p->size < sizeof(*p)) +        return; +    if (checksum(pos, p->size) != 0) +        return; +    void *newpos = malloc_fseg(p->size); +    if (!newpos) { +        warn_noalloc(); +        return; +    } +    dprintf(1, "Copying PIR from %p to %p\n", pos, newpos); +    memcpy(newpos, pos, p->size); +    PirAddr = newpos; +} + +void +copy_mptable(void *pos) +{ +    struct mptable_floating_s *p = pos; +    if (p->signature != MPTABLE_SIGNATURE) +        return; +    if (!p->physaddr) +        return; +    if (checksum(pos, sizeof(*p)) != 0) +        return; +    u32 length = p->length * 16; +    u16 mpclength = ((struct mptable_config_s *)p->physaddr)->length; +    // Allocate final memory location.  (In theory the config +    // structure can go in high memory, but Linux kernels before +    // v2.6.30 crash with that.) +    struct mptable_floating_s *newpos = malloc_fseg(length + mpclength); +    if (!newpos) { +        warn_noalloc(); +        return; +    } +    dprintf(1, "Copying MPTABLE from %p/%x to %p\n", pos, p->physaddr, newpos); +    memcpy(newpos, pos, length); +    newpos->physaddr = (u32)newpos + length; +    newpos->checksum -= checksum(newpos, sizeof(*newpos)); +    memcpy((void*)newpos + length, (void*)p->physaddr, mpclength); +} + + +/**************************************************************** + * ACPI + ****************************************************************/ + +static int +get_acpi_rsdp_length(void *pos, unsigned size) +{ +    struct rsdp_descriptor *p = pos; +    if (p->signature != RSDP_SIGNATURE) +        return -1; +    u32 length = 20; +    if (length > size) +        return -1; +    if (checksum(pos, length) != 0) +        return -1; +    if (p->revision > 1) { +        length = p->length; +        if (length > size) +            return -1; +        if (checksum(pos, length) != 0) +            return -1; +    } +    return length; +} + +struct rsdp_descriptor *RsdpAddr; + +void +copy_acpi_rsdp(void *pos) +{ +    if (RsdpAddr) +        return; +    int length = get_acpi_rsdp_length(pos, -1); +    if (length < 0) +        return; +    void *newpos = malloc_fseg(length); +    if (!newpos) { +        warn_noalloc(); +        return; +    } +    dprintf(1, "Copying ACPI RSDP from %p to %p\n", pos, newpos); +    memcpy(newpos, pos, length); +    RsdpAddr = newpos; +} + +void *find_acpi_rsdp(void) +{ +    extern u8 zonefseg_start[], zonefseg_end[]; +    unsigned long start = (unsigned long)zonefseg_start; +    unsigned long end = (unsigned long)zonefseg_end; +    unsigned long pos; + +    for (pos = ALIGN(start, 0x10); pos <= ALIGN_DOWN(end, 0x10); pos += 0x10) +        if (get_acpi_rsdp_length((void *)pos, end - pos) >= 0) +            return (void *)pos; + +    return NULL; +} + +static struct fadt_descriptor_rev1 * +find_fadt(void) +{ +    dprintf(4, "rsdp=%p\n", RsdpAddr); +    if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE) +        return NULL; +    struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address; +    dprintf(4, "rsdt=%p\n", rsdt); +    if (!rsdt || rsdt->signature != RSDT_SIGNATURE) +        return NULL; +    void *end = (void*)rsdt + rsdt->length; +    int i; +    for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) { +        struct fadt_descriptor_rev1 *fadt = (void*)rsdt->table_offset_entry[i]; +        if (!fadt || fadt->signature != FACP_SIGNATURE) +            continue; +        dprintf(4, "fadt=%p\n", fadt); +        return fadt; +    } +    dprintf(4, "no fadt found\n"); +    return NULL; +} + +u32 +find_resume_vector(void) +{ +    struct fadt_descriptor_rev1 *fadt = find_fadt(); +    if (!fadt) +        return 0; +    struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl; +    dprintf(4, "facs=%p\n", facs); +    if (! facs || facs->signature != FACS_SIGNATURE) +        return 0; +    // Found it. +    dprintf(4, "resume addr=%d\n", facs->firmware_waking_vector); +    return facs->firmware_waking_vector; +} + +static struct acpi_20_generic_address acpi_reset_reg; +static u8 acpi_reset_val; +u32 acpi_pm1a_cnt VARFSEG; +u16 acpi_pm_base = 0xb000; + +#define acpi_ga_to_bdf(addr) pci_to_bdf(0, (addr >> 32) & 0xffff, (addr >> 16) & 0xffff) + +void +acpi_reboot(void) +{ +    // Check it passed the sanity checks in acpi_set_reset_reg() and was set +    if (acpi_reset_reg.register_bit_width != 8) +        return; + +    u64 addr = le64_to_cpu(acpi_reset_reg.address); + +    dprintf(1, "ACPI hard reset %d:%llx (%x)\n", +            acpi_reset_reg.address_space_id, addr, acpi_reset_val); + +    switch (acpi_reset_reg.address_space_id) { +    case 0: // System Memory +        writeb((void *)(u32)addr, acpi_reset_val); +        break; +    case 1: // System I/O +        outb(acpi_reset_val, addr); +        break; +    case 2: // PCI config space +        pci_config_writeb(acpi_ga_to_bdf(addr), addr & 0xffff, acpi_reset_val); +        break; +    } +} + +static void +acpi_set_reset_reg(struct acpi_20_generic_address *reg, u8 val) +{ +    if (!reg || reg->address_space_id > 2 || +        reg->register_bit_width != 8 || reg->register_bit_offset) +        return; + +    acpi_reset_reg = *reg; +    acpi_reset_val = val; +} + +void +find_acpi_features(void) +{ +    struct fadt_descriptor_rev1 *fadt = find_fadt(); +    if (!fadt) +        return; +    u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk); +    u32 pm1a_cnt = le32_to_cpu(fadt->pm1a_cnt_blk); +    dprintf(4, "pm_tmr_blk=%x\n", pm_tmr); +    if (pm_tmr) +        pmtimer_setup(pm_tmr); +    if (pm1a_cnt) +        acpi_pm1a_cnt = pm1a_cnt; + +    // Theoretically we should check the 'reset_reg_sup' flag, but Windows +    // doesn't and thus nobody seems to *set* it. If the table is large enough +    // to include it, let the sanity checks in acpi_set_reset_reg() suffice. +    if (fadt->length >= 129) { +        void *p = fadt; +        acpi_set_reset_reg(p + 116, *(u8 *)(p + 128)); +    } +} + + +/**************************************************************** + * SMBIOS + ****************************************************************/ + +// Iterator for each sub-table in the smbios blob. +void * +smbios_next(struct smbios_entry_point *smbios, void *prev) +{ +    if (!smbios) +        return NULL; +    void *start = (void*)smbios->structure_table_address; +    void *end = start + smbios->structure_table_length; + +    if (!prev) { +        prev = start; +    } else { +        struct smbios_structure_header *hdr = prev; +        if (prev + sizeof(*hdr) > end) +            return NULL; +        prev += hdr->length + 2; +        while (prev < end && (*(u8*)(prev-1) != '\0' || *(u8*)(prev-2) != '\0')) +            prev++; +    } +    struct smbios_structure_header *hdr = prev; +    if (prev >= end || prev + sizeof(*hdr) >= end || prev + hdr->length >= end) +        return NULL; +    return prev; +} + +struct smbios_entry_point *SMBiosAddr; + +void +copy_smbios(void *pos) +{ +    if (SMBiosAddr) +        return; +    struct smbios_entry_point *p = pos; +    if (memcmp(p->anchor_string, "_SM_", 4)) +        return; +    if (checksum(pos, 0x10) != 0) +        return; +    if (memcmp(p->intermediate_anchor_string, "_DMI_", 5)) +        return; +    if (checksum(pos+0x10, p->length-0x10) != 0) +        return; +    struct smbios_entry_point *newpos = malloc_fseg(p->length); +    if (!newpos) { +        warn_noalloc(); +        return; +    } +    dprintf(1, "Copying SMBIOS entry point from %p to %p\n", pos, newpos); +    memcpy(newpos, pos, p->length); +    SMBiosAddr = newpos; +} + +void +display_uuid(void) +{ +    struct smbios_type_1 *tbl = smbios_next(SMBiosAddr, NULL); +    int minlen = offsetof(struct smbios_type_1, uuid) + sizeof(tbl->uuid); +    for (; tbl; tbl = smbios_next(SMBiosAddr, tbl)) +        if (tbl->header.type == 1 && tbl->header.length >= minlen) { +            u8 *uuid = tbl->uuid; +            u8 empty_uuid[sizeof(tbl->uuid)] = { 0 }; +            if (memcmp(uuid, empty_uuid, sizeof(empty_uuid)) == 0) +                return; + +            printf("Machine UUID" +                   " %02x%02x%02x%02x" +                   "-%02x%02x" +                   "-%02x%02x" +                   "-%02x%02x" +                   "-%02x%02x%02x%02x%02x%02x\n" +                   , uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3] +                   , uuid[ 4], uuid[ 5] +                   , uuid[ 6], uuid[ 7] +                   , uuid[ 8], uuid[ 9] +                   , uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); +            return; +        } +} + +#define set_str_field_or_skip(type, field, value)                       \ +    do {                                                                \ +        int size = (value != NULL) ? strlen(value) + 1 : 0;             \ +        if (size > 1) {                                                 \ +            memcpy(end, value, size);                                   \ +            end += size;                                                \ +            p->field = ++str_index;                                     \ +        } else {                                                        \ +            p->field = 0;                                               \ +        }                                                               \ +    } while (0) + +static void * +smbios_new_type_0(void *start, +                  const char *vendor, const char *version, const char *date) +{ +    struct smbios_type_0 *p = (struct smbios_type_0 *)start; +    char *end = (char *)start + sizeof(struct smbios_type_0); +    int str_index = 0; + +    p->header.type = 0; +    p->header.length = sizeof(struct smbios_type_0); +    p->header.handle = 0; + +    set_str_field_or_skip(0, vendor_str, vendor); +    set_str_field_or_skip(0, bios_version_str, version); +    p->bios_starting_address_segment = 0xe800; +    set_str_field_or_skip(0, bios_release_date_str, date); + +    p->bios_rom_size = 0; /* FIXME */ + +    /* BIOS characteristics not supported */ +    memset(p->bios_characteristics, 0, 8); +    p->bios_characteristics[0] = 0x08; + +    /* Enable targeted content distribution (needed for SVVP) */ +    p->bios_characteristics_extension_bytes[0] = 0; +    p->bios_characteristics_extension_bytes[1] = 4; + +    p->system_bios_major_release = 0; +    p->system_bios_minor_release = 0; +    p->embedded_controller_major_release = 0xFF; +    p->embedded_controller_minor_release = 0xFF; + +    *end = 0; +    end++; +    if (!str_index) { +        *end = 0; +        end++; +    } + +    return end; +} + +#define BIOS_NAME "SeaBIOS" +#define BIOS_DATE "04/01/2014" + +static int +smbios_romfile_setup(void) +{ +    struct romfile_s *f_anchor = romfile_find("etc/smbios/smbios-anchor"); +    struct romfile_s *f_tables = romfile_find("etc/smbios/smbios-tables"); +    struct smbios_entry_point ep; +    struct smbios_type_0 *t0; +    u16 qtables_len, need_t0 = 1; +    u8 *qtables, *tables; + +    if (!f_anchor || !f_tables || f_anchor->size != sizeof(ep)) +        return 0; + +    f_anchor->copy(f_anchor, &ep, f_anchor->size); + +    if (f_tables->size != ep.structure_table_length) +        return 0; + +    qtables = malloc_tmphigh(f_tables->size); +    if (!qtables) { +        warn_noalloc(); +        return 0; +    } +    f_tables->copy(f_tables, qtables, f_tables->size); +    ep.structure_table_address = (u32)qtables; /* for smbios_next(), below */ + +    /* did we get a type 0 structure ? */ +    for (t0 = smbios_next(&ep, NULL); t0; t0 = smbios_next(&ep, t0)) +        if (t0->header.type == 0) { +            need_t0 = 0; +            break; +        } + +    qtables_len = ep.structure_table_length; +    if (need_t0) { +        /* common case: add our own type 0, with 3 strings and 4 '\0's */ +        u16 t0_len = sizeof(struct smbios_type_0) + strlen(BIOS_NAME) + +                     strlen(VERSION) + strlen(BIOS_DATE) + 4; +        ep.structure_table_length += t0_len; +        if (t0_len > ep.max_structure_size) +            ep.max_structure_size = t0_len; +        ep.number_of_structures++; +    } + +    /* allocate final blob and record its address in the entry point */ +    if (ep.structure_table_length > BUILD_MAX_SMBIOS_FSEG) +        tables = malloc_high(ep.structure_table_length); +    else +        tables = malloc_fseg(ep.structure_table_length); +    if (!tables) { +        warn_noalloc(); +        free(qtables); +        return 0; +    } +    ep.structure_table_address = (u32)tables; + +    /* populate final blob */ +    if (need_t0) +        tables = smbios_new_type_0(tables, BIOS_NAME, VERSION, BIOS_DATE); +    memcpy(tables, qtables, qtables_len); +    free(qtables); + +    /* finalize entry point */ +    ep.checksum -= checksum(&ep, 0x10); +    ep.intermediate_checksum -= checksum((void *)&ep + 0x10, ep.length - 0x10); + +    copy_smbios(&ep); +    return 1; +} + +void +smbios_setup(void) +{ +    if (smbios_romfile_setup()) +      return; +    smbios_legacy_setup(); +} + +void +copy_table(void *pos) +{ +    copy_pir(pos); +    copy_mptable(pos); +    copy_acpi_rsdp(pos); +    copy_smbios(pos); +} diff --git a/roms/seabios/src/fw/coreboot.c b/roms/seabios/src/fw/coreboot.c new file mode 100644 index 00000000..8fd84493 --- /dev/null +++ b/roms/seabios/src/fw/coreboot.c @@ -0,0 +1,553 @@ +// Coreboot interface support. +// +// Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // MAXDESCSIZE +#include "byteorder.h" // be32_to_cpu +#include "config.h" // CONFIG_* +#include "hw/pci.h" // pci_probe_devices +#include "lzmadecode.h" // LzmaDecode +#include "malloc.h" // free +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // PlatformRunningOn +#include "romfile.h" // romfile_findprefix +#include "stacks.h" // yield +#include "string.h" // memset +#include "util.h" // coreboot_preinit + + +/**************************************************************** + * Memory map + ****************************************************************/ + +struct cb_header { +    u32 signature; +    u32 header_bytes; +    u32 header_checksum; +    u32 table_bytes; +    u32 table_checksum; +    u32 table_entries; +}; + +#define CB_SIGNATURE 0x4f49424C // "LBIO" + +struct cb_memory_range { +    u64 start; +    u64 size; +    u32 type; +}; + +#define CB_MEM_TABLE    16 + +struct cb_memory { +    u32 tag; +    u32 size; +    struct cb_memory_range map[0]; +}; + +#define CB_TAG_MEMORY 0x01 + +#define MEM_RANGE_COUNT(_rec) \ +        (((_rec)->size - sizeof(*(_rec))) / sizeof((_rec)->map[0])) + +struct cb_mainboard { +    u32 tag; +    u32 size; +    u8  vendor_idx; +    u8  part_idx; +    char  strings[0]; +}; + +#define CB_TAG_MAINBOARD 0x0003 + +struct cb_forward { +    u32 tag; +    u32 size; +    u64 forward; +}; + +#define CB_TAG_FORWARD 0x11 + +struct cb_cbmem_ref { +    u32 tag; +    u32 size; +    u64 cbmem_addr; +}; + +#define CB_TAG_CBMEM_CONSOLE 0x17 + +struct cbmem_console { +    u32 buffer_size; +    u32 buffer_cursor; +    u8  buffer_body[0]; +} PACKED; +static struct cbmem_console *cbcon = NULL; + +static u16 +ipchksum(char *buf, int count) +{ +    u16 *p = (u16*)buf; +    u32 sum = 0; +    while (count > 1) { +        sum += GET_FARVAR(0, *p); +        p++; +        count -= 2; +    } +    if (count) +        sum += GET_FARVAR(0, *(u8*)p); +    sum = (sum >> 16) + (sum & 0xffff); +    sum += (sum >> 16); +    return ~sum; +} + +// Try to locate the coreboot header in a given address range. +static struct cb_header * +find_cb_header(u32 addr, int len) +{ +    u32 end = addr + len; +    for (; addr < end; addr += 16) { +        struct cb_header *cbh = (void*)addr; +        if (GET_FARVAR(0, cbh->signature) != CB_SIGNATURE) +            continue; +        u32 tsize = GET_FARVAR(0, cbh->table_bytes); +        if (! tsize) +            continue; +        if (ipchksum((void*)addr, sizeof(*cbh)) != 0) +            continue; +        if (ipchksum((void*)addr + sizeof(*cbh), tsize) +            != GET_FARVAR(0, cbh->table_checksum)) +            continue; +        return cbh; +    } +    return NULL; +} + +// Try to find the coreboot memory table in the given coreboot table. +void * +find_cb_subtable(struct cb_header *cbh, u32 tag) +{ +    char *tbl = (char *)cbh + sizeof(*cbh); +    u32 count = GET_FARVAR(0, cbh->table_entries); +    int i; +    for (i=0; i<count; i++) { +        struct cb_memory *cbm = (void*)tbl; +        tbl += GET_FARVAR(0, cbm->size); +        if (GET_FARVAR(0, cbm->tag) == tag) +            return cbm; +    } +    return NULL; +} + +struct cb_header * +find_cb_table(void) +{ +    struct cb_header *cbh = find_cb_header(0, 0x1000); +    if (!cbh) +        return NULL; +    struct cb_forward *cbf = find_cb_subtable(cbh, CB_TAG_FORWARD); +    if (cbf) { +        dprintf(3, "Found coreboot table forwarder.\n"); +        cbh = find_cb_header(GET_FARVAR(0, cbf->forward), 0x100); +        if (!cbh) +            return NULL; +    } +    return cbh; +} + +static struct cb_memory *CBMemTable; +const char *CBvendor = "", *CBpart = ""; + +// Populate max ram and e820 map info by scanning for a coreboot table. +void +coreboot_preinit(void) +{ +    if (!CONFIG_COREBOOT) +        return; + +    dprintf(3, "Attempting to find coreboot table\n"); + +    // Find coreboot table. +    struct cb_header *cbh = find_cb_table(); +    if (!cbh) +        goto fail; +    dprintf(3, "Now attempting to find coreboot memory map\n"); +    struct cb_memory *cbm = CBMemTable = find_cb_subtable(cbh, CB_TAG_MEMORY); +    if (!cbm) +        goto fail; + +    int i, count = MEM_RANGE_COUNT(cbm); +    for (i=0; i<count; i++) { +        struct cb_memory_range *m = &cbm->map[i]; +        u32 type = m->type; +        if (type == CB_MEM_TABLE) +            type = E820_RESERVED; +        add_e820(m->start, m->size, type); +    } + +    // Ughh - coreboot likes to set a map at 0x0000-0x1000, but this +    // confuses grub.  So, override it. +    add_e820(0, 16*1024, E820_RAM); + +    struct cb_cbmem_ref *cbref = find_cb_subtable(cbh, CB_TAG_CBMEM_CONSOLE); +    if (cbref) { +        cbcon = (void*)(u32)cbref->cbmem_addr; +        debug_banner(); +        dprintf(1, "Found coreboot cbmem console @ %llx\n", cbref->cbmem_addr); +    } + +    struct cb_mainboard *cbmb = find_cb_subtable(cbh, CB_TAG_MAINBOARD); +    if (cbmb) { +        CBvendor = &cbmb->strings[cbmb->vendor_idx]; +        CBpart = &cbmb->strings[cbmb->part_idx]; +        dprintf(1, "Found mainboard %s %s\n", CBvendor, CBpart); +    } + +    return; + +fail: +    // No table found..  Use 16Megs as a dummy value. +    dprintf(1, "Unable to find coreboot table!\n"); +    add_e820(0, 16*1024*1024, E820_RAM); +    return; +} + +void coreboot_debug_putc(char c) +{ +    if (!CONFIG_DEBUG_COREBOOT) +        return; +    if (!cbcon) +        return; +    u32 cursor = cbcon->buffer_cursor++; +    if (cursor < cbcon->buffer_size) +        cbcon->buffer_body[cursor] = c; +} + +/**************************************************************** + * BIOS table copying + ****************************************************************/ + +// Attempt to find (and relocate) any standard bios tables found in a +// given address range. +static void +scan_tables(u32 start, u32 size) +{ +    void *p = (void*)ALIGN(start, 16); +    void *end = (void*)start + size; +    for (; p<end; p += 16) +        copy_table(p); +} + +void +coreboot_platform_setup(void) +{ +    if (!CONFIG_COREBOOT) +        return; +    pci_probe_devices(); + +    struct cb_memory *cbm = CBMemTable; +    if (!cbm) +        return; + +    dprintf(3, "Relocating coreboot bios tables\n"); + +    // Scan CB_MEM_TABLE areas for bios tables. +    int i, count = MEM_RANGE_COUNT(cbm); +    for (i=0; i<count; i++) { +        struct cb_memory_range *m = &cbm->map[i]; +        if (m->type == CB_MEM_TABLE) +            scan_tables(m->start, m->size); +    } + +    find_acpi_features(); +} + + +/**************************************************************** + * ulzma + ****************************************************************/ + +// Uncompress data in flash to an area of memory. +static int +ulzma(u8 *dst, u32 maxlen, const u8 *src, u32 srclen) +{ +    dprintf(3, "Uncompressing data %d@%p to %d@%p\n", srclen, src, maxlen, dst); +    CLzmaDecoderState state; +    int ret = LzmaDecodeProperties(&state.Properties, src, LZMA_PROPERTIES_SIZE); +    if (ret != LZMA_RESULT_OK) { +        dprintf(1, "LzmaDecodeProperties error - %d\n", ret); +        return -1; +    } +    u8 scratch[15980]; +    int need = (LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); +    if (need > sizeof(scratch)) { +        dprintf(1, "LzmaDecode need %d have %d\n", need, (unsigned int)sizeof(scratch)); +        return -1; +    } +    state.Probs = (CProb *)scratch; + +    u32 dstlen = *(u32*)(src + LZMA_PROPERTIES_SIZE); +    if (dstlen > maxlen) { +        dprintf(1, "LzmaDecode too large (max %d need %d)\n", maxlen, dstlen); +        return -1; +    } +    u32 inProcessed, outProcessed; +    ret = LzmaDecode(&state, src + LZMA_PROPERTIES_SIZE + 8, srclen +                     , &inProcessed, dst, dstlen, &outProcessed); +    if (ret) { +        dprintf(1, "LzmaDecode returned %d\n", ret); +        return -1; +    } +    return dstlen; +} + + +/**************************************************************** + * Coreboot flash format + ****************************************************************/ + +#define CBFS_HEADER_MAGIC 0x4F524243 +#define CBFS_VERSION1 0x31313131 + +struct cbfs_header { +    u32 magic; +    u32 version; +    u32 romsize; +    u32 bootblocksize; +    u32 align; +    u32 offset; +    u32 pad[2]; +} PACKED; + +#define CBFS_FILE_MAGIC 0x455649484352414cLL // LARCHIVE + +struct cbfs_file { +    u64 magic; +    u32 len; +    u32 type; +    u32 checksum; +    u32 offset; +    char filename[0]; +} PACKED; + +struct cbfs_romfile_s { +    struct romfile_s file; +    struct cbfs_file *fhdr; +    void *data; +    u32 rawsize, flags; +}; + +// Copy a file to memory (uncompressing if necessary) +static int +cbfs_copyfile(struct romfile_s *file, void *dst, u32 maxlen) +{ +    if (!CONFIG_COREBOOT_FLASH) +        return -1; + +    struct cbfs_romfile_s *cfile; +    cfile = container_of(file, struct cbfs_romfile_s, file); +    u32 size = cfile->rawsize; +    void *src = cfile->data; +    if (cfile->flags) { +        // Compressed - copy to temp ram and uncompress it. +        void *temp = malloc_tmphigh(size); +        if (!temp) { +            warn_noalloc(); +            return -1; +        } +        iomemcpy(temp, src, size); +        int ret = ulzma(dst, maxlen, temp, size); +        yield(); +        free(temp); +        return ret; +    } + +    // Not compressed. +    dprintf(3, "Copying data %d@%p to %d@%p\n", size, src, maxlen, dst); +    if (size > maxlen) { +        warn_noalloc(); +        return -1; +    } +    iomemcpy(dst, src, size); +    return size; +} + +// Process CBFS links file.  The links file is a newline separated +// file where each line has a "link name" and a "destination name" +// separated by a space character. +static void +process_links_file(void) +{ +    char *links = romfile_loadfile("links", NULL), *next = links; +    while (next) { +        // Parse out linkname and destname +        char *linkname = next; +        next = strchr(linkname, '\n'); +        if (next) +            *next++ = '\0'; +        char *comment = strchr(linkname, '#'); +        if (comment) +            *comment = '\0'; +        linkname = nullTrailingSpace(linkname); +        char *destname = strchr(linkname, ' '); +        if (!destname) +            continue; +        *destname++ = '\0'; +        destname = nullTrailingSpace(destname); +        // Lookup destname and create new romfile entry for linkname +        struct romfile_s *ufile = romfile_find(destname); +        if (!ufile) +            continue; +        struct cbfs_romfile_s *cufile +            = container_of(ufile, struct cbfs_romfile_s, file); +        struct cbfs_romfile_s *cfile = malloc_tmp(sizeof(*cfile)); +        if (!cfile) { +            warn_noalloc(); +            break; +        } +        memcpy(cfile, cufile, sizeof(*cfile)); +        strtcpy(cfile->file.name, linkname, sizeof(cfile->file.name)); +        romfile_add(&cfile->file); +    } +    free(links); +} + +void +coreboot_cbfs_init(void) +{ +    if (!CONFIG_COREBOOT_FLASH) +        return; + +    struct cbfs_header *hdr = *(void **)(CONFIG_CBFS_LOCATION - 4); +    if (hdr->magic != cpu_to_be32(CBFS_HEADER_MAGIC)) { +        dprintf(1, "Unable to find CBFS (ptr=%p; got %x not %x)\n" +                , hdr, hdr->magic, cpu_to_be32(CBFS_HEADER_MAGIC)); +        return; +    } +    dprintf(1, "Found CBFS header at %p\n", hdr); + +    u32 romsize = be32_to_cpu(hdr->romsize); +    u32 romstart = CONFIG_CBFS_LOCATION - romsize; +    struct cbfs_file *fhdr = (void*)romstart + be32_to_cpu(hdr->offset); +    for (;;) { +        if ((u32)fhdr - romstart > romsize) +            break; +        u64 magic = fhdr->magic; +        if (magic != CBFS_FILE_MAGIC) +            break; +        struct cbfs_romfile_s *cfile = malloc_tmp(sizeof(*cfile)); +        if (!cfile) { +            warn_noalloc(); +            break; +        } +        memset(cfile, 0, sizeof(*cfile)); +        strtcpy(cfile->file.name, fhdr->filename, sizeof(cfile->file.name)); +        cfile->file.size = cfile->rawsize = be32_to_cpu(fhdr->len); +        cfile->fhdr = fhdr; +        cfile->file.copy = cbfs_copyfile; +        cfile->data = (void*)fhdr + be32_to_cpu(fhdr->offset); +        int len = strlen(cfile->file.name); +        if (len > 5 && strcmp(&cfile->file.name[len-5], ".lzma") == 0) { +            // Using compression. +            cfile->flags = 1; +            cfile->file.name[len-5] = '\0'; +            cfile->file.size = *(u32*)(cfile->data + LZMA_PROPERTIES_SIZE); +        } +        romfile_add(&cfile->file); + +        fhdr = (void*)ALIGN((u32)cfile->data + cfile->rawsize +                            , be32_to_cpu(hdr->align)); +    } + +    process_links_file(); +} + +struct cbfs_payload_segment { +    u32 type; +    u32 compression; +    u32 offset; +    u64 load_addr; +    u32 len; +    u32 mem_len; +} PACKED; + +#define PAYLOAD_SEGMENT_BSS    0x20535342 +#define PAYLOAD_SEGMENT_ENTRY  0x52544E45 + +#define CBFS_COMPRESS_NONE  0 +#define CBFS_COMPRESS_LZMA  1 + +struct cbfs_payload { +    struct cbfs_payload_segment segments[1]; +}; + +void +cbfs_run_payload(struct cbfs_file *fhdr) +{ +    if (!CONFIG_COREBOOT_FLASH || !fhdr) +        return; +    dprintf(1, "Run %s\n", fhdr->filename); +    struct cbfs_payload *pay = (void*)fhdr + be32_to_cpu(fhdr->offset); +    struct cbfs_payload_segment *seg = pay->segments; +    for (;;) { +        void *src = (void*)pay + be32_to_cpu(seg->offset); +        void *dest = (void*)(u32)be64_to_cpu(seg->load_addr); +        u32 src_len = be32_to_cpu(seg->len); +        u32 dest_len = be32_to_cpu(seg->mem_len); +        switch (seg->type) { +        case PAYLOAD_SEGMENT_BSS: +            dprintf(3, "BSS segment %d@%p\n", dest_len, dest); +            memset(dest, 0, dest_len); +            break; +        case PAYLOAD_SEGMENT_ENTRY: { +            dprintf(1, "Calling addr %p\n", dest); +            void (*func)() = dest; +            func(); +            return; +        } +        default: +            dprintf(3, "Segment %x %d@%p -> %d@%p\n" +                    , seg->type, src_len, src, dest_len, dest); +            if (seg->compression == cpu_to_be32(CBFS_COMPRESS_NONE)) { +                if (src_len > dest_len) +                    src_len = dest_len; +                memcpy(dest, src, src_len); +            } else if (CONFIG_LZMA +                       && seg->compression == cpu_to_be32(CBFS_COMPRESS_LZMA)) { +                int ret = ulzma(dest, dest_len, src, src_len); +                if (ret < 0) +                    return; +                src_len = ret; +            } else { +                dprintf(1, "No support for compression type %x\n" +                        , seg->compression); +                return; +            } +            if (dest_len > src_len) +                memset(dest + src_len, 0, dest_len - src_len); +            break; +        } +        seg++; +    } +} + +// Register payloads in "img/" directory with boot system. +void +cbfs_payload_setup(void) +{ +    if (!CONFIG_COREBOOT_FLASH) +        return; +    struct romfile_s *file = NULL; +    for (;;) { +        file = romfile_findprefix("img/", file); +        if (!file) +            break; +        struct cbfs_romfile_s *cfile; +        cfile = container_of(file, struct cbfs_romfile_s, file); +        const char *filename = file->name; +        char *desc = znprintf(MAXDESCSIZE, "Payload [%s]", &filename[4]); +        boot_add_cbfs(cfile->fhdr, desc, bootprio_find_named_rom(filename, 0)); +    } +} diff --git a/roms/seabios/src/fw/csm.c b/roms/seabios/src/fw/csm.c new file mode 100644 index 00000000..7cdb398f --- /dev/null +++ b/roms/seabios/src/fw/csm.c @@ -0,0 +1,344 @@ +// Compatibility Support Module (CSM) for UEFI / EDK-II +// +// Copyright © 2013 Intel Corporation +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" +#include "config.h" // CONFIG_* +#include "farptr.h" // MAKE_FLATPTR +#include "hw/pci.h" +#include "hw/pic.h" +#include "malloc.h" // csm_malloc_preinit +#include "memmap.h" +#include "output.h" // dprintf +#include "stacks.h" // wait_threads +#include "std/acpi.h" // RSDP_SIGNATURE +#include "std/bda.h" // struct bios_data_area_s +#include "std/optionrom.h" // struct rom_header +#include "util.h" // copy_smbios +#include "paravirt.h" // qemu_preinit + +#define UINT8 u8 +#define UINT16 u16 +#define UINT32 u32 +#include "std/LegacyBios.h" + +struct rsdp_descriptor csm_rsdp VARFSEG __aligned(16); + +EFI_COMPATIBILITY16_TABLE csm_compat_table VARFSEG __aligned(16) = { +    .Signature = 0x24454649, +    .TableChecksum = 0 /* Filled in by checkrom.py */, +    .TableLength = sizeof(csm_compat_table), +    .Compatibility16CallSegment = SEG_BIOS, +    .Compatibility16CallOffset = 0 /* Filled in by checkrom.py */, +    .OemIdStringPointer = (u32)"SeaBIOS", +    .AcpiRsdPtrPointer = (u32)&csm_rsdp, +}; + +EFI_TO_COMPATIBILITY16_INIT_TABLE *csm_init_table; +EFI_TO_COMPATIBILITY16_BOOT_TABLE *csm_boot_table; + +static u16 PICMask = PIC_IRQMASK_DEFAULT; + +extern void __csm_return(struct bregs *regs) __noreturn; + +static void +csm_return(struct bregs *regs) +{ +    u32 rommax = rom_get_max(); +    extern u8 final_readonly_start[]; + +    dprintf(3, "handle_csm returning AX=%04x\n", regs->ax); + +    csm_compat_table.UmaAddress = rommax; +    csm_compat_table.UmaSize = (u32)final_readonly_start - rommax; + +    PICMask = pic_irqmask_read(); +    __csm_return(regs); +} + +static void +csm_maininit(struct bregs *regs) +{ +    interface_init(); +    pci_probe_devices(); + +    csm_compat_table.PnPInstallationCheckSegment = SEG_BIOS; +    csm_compat_table.PnPInstallationCheckOffset = get_pnp_offset(); + +    regs->ax = 0; + +    csm_return(regs); +} + +/* Legacy16InitializeYourself */ +static void +handle_csm_0000(struct bregs *regs) +{ +    qemu_preinit(); + +    dprintf(3, "Legacy16InitializeYourself table %04x:%04x\n", regs->es, +            regs->bx); + +    csm_init_table = MAKE_FLATPTR(regs->es, regs->bx); + +    dprintf(3, "BiosLessThan1MB %08x\n", csm_init_table->BiosLessThan1MB); +    dprintf(3, "HiPmmMemory     %08x\n", csm_init_table->HiPmmMemory); +    dprintf(3, "HiPmmMemorySize %08x\n", csm_init_table->HiPmmMemorySizeInBytes); +    dprintf(3, "ReverseThunk    %04x:%04x\n", csm_init_table->ReverseThunkCallSegment, +            csm_init_table->ReverseThunkCallOffset); +    dprintf(3, "NumE820Entries  %08x\n", csm_init_table->NumberE820Entries); +    dprintf(3, "OsMemoryAbove1M %08x\n", csm_init_table->OsMemoryAbove1Mb); +    dprintf(3, "ThunkStart      %08x\n", csm_init_table->ThunkStart); +    dprintf(3, "ThunkSize       %08x\n", csm_init_table->ThunkSizeInBytes); +    dprintf(3, "LoPmmMemory     %08x\n", csm_init_table->LowPmmMemory); +    dprintf(3, "LoPmmMemorySize %08x\n", csm_init_table->LowPmmMemorySizeInBytes); + +    csm_malloc_preinit(csm_init_table->LowPmmMemory, +                       csm_init_table->LowPmmMemorySizeInBytes, +                       csm_init_table->HiPmmMemory, +                       csm_init_table->HiPmmMemorySizeInBytes); +    reloc_preinit(csm_maininit, regs); +} + +/* Legacy16UpdateBbs */ +static void +handle_csm_0001(struct bregs *regs) +{ +    if (!CONFIG_BOOT) { +        regs->ax = 1; +        return; +    } + +    dprintf(3, "Legacy16UpdateBbs table %04x:%04x\n", regs->es, regs->bx); + +    csm_boot_table = MAKE_FLATPTR(regs->es, regs->bx); +    dprintf(3, "MajorVersion %04x\n", csm_boot_table->MajorVersion); +    dprintf(3, "MinorVersion %04x\n", csm_boot_table->MinorVersion); +    dprintf(3, "AcpiTable %08x\n", csm_boot_table->AcpiTable); +    dprintf(3, "SmbiosTable %08x\n", csm_boot_table->SmbiosTable); +    dprintf(3, "SmbiosTableLength %08x\n", csm_boot_table->SmbiosTableLength); +//    dprintf(3, "SioData %08x\n", csm_boot_table->SioData); +    dprintf(3, "DevicePathType %04x\n", csm_boot_table->DevicePathType); +    dprintf(3, "PciIrqMask %04x\n", csm_boot_table->PciIrqMask); +    dprintf(3, "NumberE820Entries %08x\n", csm_boot_table->NumberE820Entries); +//    dprintf(3, "HddInfo %08x\n", csm_boot_table->HddInfo); +    dprintf(3, "NumberBbsEntries %08x\n", csm_boot_table->NumberBbsEntries); +    dprintf(3, "BBsTable %08x\n", csm_boot_table->BbsTable); +    dprintf(3, "SmmTable %08x\n", csm_boot_table->SmmTable); +    dprintf(3, "OsMemoryAbove1Mb %08x\n", csm_boot_table->OsMemoryAbove1Mb); +    dprintf(3, "UnconventionalDeviceTable %08x\n", csm_boot_table->UnconventionalDeviceTable); + +    regs->ax = 0; +} + +/* PrepareToBoot */ +static void +handle_csm_0002(struct bregs *regs) +{ +    if (!CONFIG_BOOT) { +        regs->ax = 1; +        return; +    } + +    dprintf(3, "PrepareToBoot table %04x:%04x\n", regs->es, regs->bx); + +    struct e820entry *p = (void *)csm_compat_table.E820Pointer; +    int i; +    for (i=0; i < csm_compat_table.E820Length / sizeof(struct e820entry); i++) +        add_e820(p[i].start, p[i].size, p[i].type); + +    if (csm_init_table->HiPmmMemorySizeInBytes > BUILD_MAX_HIGHTABLE) { +        u32 hi_pmm_end = csm_init_table->HiPmmMemory + csm_init_table->HiPmmMemorySizeInBytes; +        add_e820(hi_pmm_end - BUILD_MAX_HIGHTABLE, BUILD_MAX_HIGHTABLE, E820_RESERVED); +    } + +    // For PCIBIOS 1ab10e +    if (csm_compat_table.IrqRoutingTablePointer && +        csm_compat_table.IrqRoutingTableLength) { +        PirAddr = (void *)csm_compat_table.IrqRoutingTablePointer; +        dprintf(3, "CSM PIRQ table at %p\n", PirAddr); +    } + +    // For find_resume_vector()... and find_acpi_features() +    if (csm_rsdp.signature == RSDP_SIGNATURE) { +        RsdpAddr = &csm_rsdp; +        dprintf(3, "CSM ACPI RSDP at %p\n", RsdpAddr); + +        find_acpi_features(); +    } + +    // SMBIOS table needs to be copied into the f-seg +    // XX: OVMF doesn't seem to set SmbiosTableLength so don't check it +    if (csm_boot_table->SmbiosTable && !SMBiosAddr) +        copy_smbios((void *)csm_boot_table->SmbiosTable); + +    // MPTABLE is just there; we don't care where. + +    // EFI may have reinitialised the video using its *own* driver. +    enable_vga_console(); + +    // EFI fills this in for us. Zero it for now... +    struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); +    bda->hdcount = 0; + +    mathcp_setup(); +    timer_setup(); +    clock_setup(); +    device_hardware_setup(); +    wait_threads(); +    interactive_bootmenu(); + +    prepareboot(); + +    regs->ax = 0; +} + +/* Boot */ +static void +handle_csm_0003(struct bregs *regs) +{ +    if (!CONFIG_BOOT) { +        regs->ax = 1; +        return; +    } + +    dprintf(3, "Boot\n"); + +    startBoot(); + +    regs->ax = 1; +} + +/* Legacy16DispatchOprom */ +static void +handle_csm_0005(struct bregs *regs) +{ +    EFI_DISPATCH_OPROM_TABLE *table = MAKE_FLATPTR(regs->es, regs->bx); +    struct rom_header *rom; +    u16 bdf; + +    if (!CONFIG_OPTIONROMS) { +        regs->ax = 1; +        return; +    } + +    dprintf(3, "Legacy16DispatchOprom rom %p\n", table); + +    dprintf(3, "OpromSegment   %04x\n", table->OpromSegment); +    dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment); +    dprintf(3, "PnPInstallationCheck %04x:%04x\n", +            table->PnPInstallationCheckSegment, +            table->PnPInstallationCheckOffset); +    dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment); + +    rom = MAKE_FLATPTR(table->OpromSegment, 0); +    bdf = pci_bus_devfn_to_bdf(table->PciBus, table->PciDeviceFunction); + +    rom_reserve(rom->size * 512); + +    // XX PnP seg/ofs should never be other than default +    callrom(rom, bdf); + +    rom_confirm(rom->size * 512); + +    regs->bx = 0; // FIXME +    regs->ax = 0; +} + +/* Legacy16GetTableAddress */ +static void +handle_csm_0006(struct bregs *regs) +{ +    u16 size = regs->cx; +    u16 align = regs->dx; +    u16 region = regs->bx; // (1 for F000 seg, 2 for E000 seg, 0 for either) +    void *chunk = NULL; + +    if (!region) +        region = 3; + +    dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n", +        size, align, region); + +    if (region & 2) +        chunk = _malloc(&ZoneLow, size, align); +    if (!chunk && (region & 1)) +        chunk = _malloc(&ZoneFSeg, size, align); + +    dprintf(3, "Legacy16GetTableAddress size %x align %x region %d yields %p\n", +        size, align, region, chunk); +    if (chunk) { +        regs->ds = FLATPTR_TO_SEG(chunk); +        regs->bx = FLATPTR_TO_OFFSET(chunk); +        regs->ax = 0; +    } else { +        regs->ax = 1; +    } +} + +void VISIBLE32INIT +handle_csm(struct bregs *regs) +{ +    ASSERT32FLAT(); + +    if (!CONFIG_CSM) +        return; + +    dprintf(3, "handle_csm regs %p AX=%04x\n", regs, regs->ax); + +    pic_irqmask_write(PICMask); + +    switch(regs->ax) { +    case 0000: handle_csm_0000(regs); break; +    case 0001: handle_csm_0001(regs); break; +    case 0002: handle_csm_0002(regs); break; +    case 0003: handle_csm_0003(regs); break; +//    case 0004: handle_csm_0004(regs); break; +    case 0005: handle_csm_0005(regs); break; +    case 0006: handle_csm_0006(regs); break; +//    case 0007: handle_csm_0007(regs); break; +//    case 0008: hamdle_csm_0008(regs); break; +    default: regs->al = 1; +    } + +    csm_return(regs); +} + +int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave) +{ +    if (!csm_boot_table) +        return -1; +    BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; +    int index = 1 + (chanid * 2) + slave; +    dprintf(3, "CSM bootprio for ATA%d,%d (index %d) is %d\n", chanid, slave, +            index, bbs[index].BootPriority); +    return bbs[index].BootPriority; +} + +int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid) +{ +    if (!csm_boot_table) +        return -1; +    BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; +    dprintf(3, "CSM bootprio for FDC is %d\n", bbs[0].BootPriority); +    return bbs[0].BootPriority; +} + +int csm_bootprio_pci(struct pci_device *pci) +{ +    if (!csm_boot_table) +        return -1; +    BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; +    int i; + +    for (i = 5; i < csm_boot_table->NumberBbsEntries; i++) { +        if (pci->bdf == pci_to_bdf(bbs[i].Bus, bbs[i].Device, bbs[i].Function)) { +            dprintf(3, "CSM bootprio for PCI(%d,%d,%d) is %d\n", bbs[i].Bus, +                    bbs[i].Device, bbs[i].Function, bbs[i].BootPriority); +            return bbs[i].BootPriority; +        } +    } +    return -1; +} diff --git a/roms/seabios/src/fw/dev-piix.h b/roms/seabios/src/fw/dev-piix.h new file mode 100644 index 00000000..c389f171 --- /dev/null +++ b/roms/seabios/src/fw/dev-piix.h @@ -0,0 +1,29 @@ +#ifndef __DEV_PIIX_H +#define __DEV_PIIX_H + +#define I440FX_PAM0               0x59 +#define I440FX_SMRAM              0x72 + +#define PIIX_PMBASE               0x40 +#define PIIX_PMREGMISC            0x80 +#define PIIX_SMBHSTBASE           0x90 +#define PIIX_SMBHSTCFG            0xd2 +#define PIIX_DEVACTB              0x58 +#define PIIX_DEVACTB_APMC_EN      (1 << 25) + +#define PIIX_PORT_ELCR1           0x4d0 +#define PIIX_PORT_ELCR2           0x4d1 + +/* ICH9 PM I/O registers */ +#define PIIX_GPE0_BLK            0xafe0 +#define PIIX_GPE0_BLK_LEN        4 +#define PIIX_PMIO_GLBCTL         0x28 +#define PIIX_PMIO_GLBCTL_SMI_EN  1 + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define PIIX_ACPI_ENABLE         0xf1 +#define PIIX_ACPI_DISABLE        0xf0 + +#define PIIX_PM_INTRRUPT         9       // irq 9 + +#endif // dev-piix.h diff --git a/roms/seabios/src/fw/dev-q35.h b/roms/seabios/src/fw/dev-q35.h new file mode 100644 index 00000000..c6f8bd9e --- /dev/null +++ b/roms/seabios/src/fw/dev-q35.h @@ -0,0 +1,49 @@ +#ifndef __DEV_Q35_H +#define __DEV_Q35_H + +#include "types.h"      // u16 + +#define PCI_DEVICE_ID_INTEL_Q35_MCH     0x29c0 +#define Q35_HOST_BRIDGE_PAM0            0x90 +#define Q35_HOST_BRIDGE_SMRAM           0x9d +#define Q35_HOST_BRIDGE_PCIEXBAR        0x60 +#define Q35_HOST_BRIDGE_PCIEXBAR_SIZE   (256 * 1024 * 1024) +#define Q35_HOST_BRIDGE_PCIEXBAR_ADDR   0xb0000000 +#define Q35_HOST_BRIDGE_PCIEXBAREN      ((u64)1) +#define Q35_HOST_PCIE_PCI_SEGMENT       0 +#define Q35_HOST_PCIE_START_BUS_NUMBER  0 +#define Q35_HOST_PCIE_END_BUS_NUMBER    255 + +#define PCI_DEVICE_ID_INTEL_ICH9_LPC    0x2918 +#define ICH9_LPC_PMBASE                 0x40 +#define ICH9_LPC_PMBASE_RTE             0x1 + +#define ICH9_LPC_ACPI_CTRL             0x44 +#define ICH9_LPC_ACPI_CTRL_ACPI_EN     0x80 +#define ICH9_LPC_PIRQA_ROUT            0x60 +#define ICH9_LPC_PIRQE_ROUT            0x68 +#define ICH9_LPC_PIRQ_ROUT_IRQEN       0x80 +#define ICH9_LPC_GEN_PMCON_1           0xa0 +#define ICH9_LPC_GEN_PMCON_1_SMI_LOCK  (1 << 4) +#define ICH9_LPC_PORT_ELCR1            0x4d0 +#define ICH9_LPC_PORT_ELCR2            0x4d1 +#define PCI_DEVICE_ID_INTEL_ICH9_SMBUS 0x2930 +#define ICH9_SMB_SMB_BASE              0x20 +#define ICH9_SMB_HOSTC                 0x40 +#define ICH9_SMB_HOSTC_HST_EN          0x01 + +#define ICH9_ACPI_ENABLE               0x2 +#define ICH9_ACPI_DISABLE              0x3 + +/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */ +#define ICH9_PMIO_GPE0_STS             0x20 +#define ICH9_PMIO_GPE0_BLK_LEN         0x10 +#define ICH9_PMIO_SMI_EN               0x30 +#define ICH9_PMIO_SMI_EN_APMC_EN       (1 << 5) +#define ICH9_PMIO_SMI_EN_GLB_SMI_EN    (1 << 0) + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define ICH9_APM_ACPI_ENABLE           0x2 +#define ICH9_APM_ACPI_DISABLE          0x3 + +#endif // dev-q35.h diff --git a/roms/seabios/src/fw/lzmadecode.c b/roms/seabios/src/fw/lzmadecode.c new file mode 100644 index 00000000..65819b53 --- /dev/null +++ b/roms/seabios/src/fw/lzmadecode.c @@ -0,0 +1,398 @@ +/* +  LzmaDecode.c +  LZMA Decoder (optimized for Speed version) +   +  LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) +  http://www.7-zip.org/ + +  LZMA SDK is licensed under two licenses: +  1) GNU Lesser General Public License (GNU LGPL) +  2) Common Public License (CPL) +  It means that you can select one of these two licenses and  +  follow rules of that license. + +  SPECIAL EXCEPTION: +  Igor Pavlov, as the author of this Code, expressly permits you to  +  statically or dynamically link your Code (or bind by name) to the  +  interfaces of this file without subjecting your linked Code to the  +  terms of the CPL or GNU LGPL. Any modifications or additions  +  to this file, however, are subject to the LGPL or CPL terms. +*/ + +#include "lzmadecode.h" + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*Buffer++) + +#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \ +  { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }} + + +#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; } + +#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2 +  + +#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; } + +#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound) +#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits; +#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits; + +#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \ +  { UpdateBit0(p); mi <<= 1; A0; } else \ +  { UpdateBit1(p); mi = (mi + mi) + 1; A1; }  +   +#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)                + +#define RangeDecoderBitTreeDecode(probs, numLevels, res) \ +  { int i = numLevels; res = 1; \ +  do { CProb *cp = probs + res; RC_GET_BIT(cp, res) } while(--i != 0); \ +  res -= (1 << numLevels); } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols)  + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size) +{ +  unsigned char prop0; +  if (size < LZMA_PROPERTIES_SIZE) +    return LZMA_RESULT_DATA_ERROR; +  prop0 = propsData[0]; +  if (prop0 >= (9 * 5 * 5)) +    return LZMA_RESULT_DATA_ERROR; +  { +    for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5)); +    for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9); +    propsRes->lc = prop0; +    /* +    unsigned char remainder = (unsigned char)(prop0 / 9); +    propsRes->lc = prop0 % 9; +    propsRes->pb = remainder / 5; +    propsRes->lp = remainder % 5; +    */ +  } + +  return LZMA_RESULT_OK; +} + +#define kLzmaStreamWasFinishedId (-1) + +int LzmaDecode(CLzmaDecoderState *vs, +    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, +    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed) +{ +  CProb *p = vs->Probs; +  SizeT nowPos = 0; +  Byte previousByte = 0; +  UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1; +  UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1; +  int lc = vs->Properties.lc; + + +  int state = 0; +  UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; +  int len = 0; +  const Byte *Buffer; +  const Byte *BufferLim; +  UInt32 Range; +  UInt32 Code; + +  *inSizeProcessed = 0; +  *outSizeProcessed = 0; + +  { +    UInt32 i; +    UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); +    for (i = 0; i < numProbs; i++) +      p[i] = kBitModelTotal >> 1; +  } +   +  RC_INIT(inStream, inSize); + + +  while(nowPos < outSize) +  { +    CProb *prob; +    UInt32 bound; +    int posState = (int)( +        (nowPos  +        ) +        & posStateMask); + +    prob = p + IsMatch + (state << kNumPosBitsMax) + posState; +    IfBit0(prob) +    { +      int symbol = 1; +      UpdateBit0(prob) +      prob = p + Literal + (LZMA_LIT_SIZE *  +        ((( +        (nowPos  +        ) +        & literalPosMask) << lc) + (previousByte >> (8 - lc)))); + +      if (state >= kNumLitStates) +      { +        int matchByte; +        matchByte = outStream[nowPos - rep0]; +        do +        { +          int bit; +          CProb *probLit; +          matchByte <<= 1; +          bit = (matchByte & 0x100); +          probLit = prob + 0x100 + bit + symbol; +          RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break) +        } +        while (symbol < 0x100); +      } +      while (symbol < 0x100) +      { +        CProb *probLit = prob + symbol; +        RC_GET_BIT(probLit, symbol) +      } +      previousByte = (Byte)symbol; + +      outStream[nowPos++] = previousByte; +      if (state < 4) state = 0; +      else if (state < 10) state -= 3; +      else state -= 6; +    } +    else              +    { +      UpdateBit1(prob); +      prob = p + IsRep + state; +      IfBit0(prob) +      { +        UpdateBit0(prob); +        rep3 = rep2; +        rep2 = rep1; +        rep1 = rep0; +        state = state < kNumLitStates ? 0 : 3; +        prob = p + LenCoder; +      } +      else +      { +        UpdateBit1(prob); +        prob = p + IsRepG0 + state; +        IfBit0(prob) +        { +          UpdateBit0(prob); +          prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState; +          IfBit0(prob) +          { +            UpdateBit0(prob); +             +            if (nowPos == 0) +              return LZMA_RESULT_DATA_ERROR; +             +            state = state < kNumLitStates ? 9 : 11; +            previousByte = outStream[nowPos - rep0]; +            outStream[nowPos++] = previousByte; + +            continue; +          } +          else +          { +            UpdateBit1(prob); +          } +        } +        else +        { +          UInt32 distance; +          UpdateBit1(prob); +          prob = p + IsRepG1 + state; +          IfBit0(prob) +          { +            UpdateBit0(prob); +            distance = rep1; +          } +          else  +          { +            UpdateBit1(prob); +            prob = p + IsRepG2 + state; +            IfBit0(prob) +            { +              UpdateBit0(prob); +              distance = rep2; +            } +            else +            { +              UpdateBit1(prob); +              distance = rep3; +              rep3 = rep2; +            } +            rep2 = rep1; +          } +          rep1 = rep0; +          rep0 = distance; +        } +        state = state < kNumLitStates ? 8 : 11; +        prob = p + RepLenCoder; +      } +      { +        int numBits, offset; +        CProb *probLen = prob + LenChoice; +        IfBit0(probLen) +        { +          UpdateBit0(probLen); +          probLen = prob + LenLow + (posState << kLenNumLowBits); +          offset = 0; +          numBits = kLenNumLowBits; +        } +        else +        { +          UpdateBit1(probLen); +          probLen = prob + LenChoice2; +          IfBit0(probLen) +          { +            UpdateBit0(probLen); +            probLen = prob + LenMid + (posState << kLenNumMidBits); +            offset = kLenNumLowSymbols; +            numBits = kLenNumMidBits; +          } +          else +          { +            UpdateBit1(probLen); +            probLen = prob + LenHigh; +            offset = kLenNumLowSymbols + kLenNumMidSymbols; +            numBits = kLenNumHighBits; +          } +        } +        RangeDecoderBitTreeDecode(probLen, numBits, len); +        len += offset; +      } + +      if (state < 4) +      { +        int posSlot; +        state += kNumLitStates; +        prob = p + PosSlot + +            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<  +            kNumPosSlotBits); +        RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot); +        if (posSlot >= kStartPosModelIndex) +        { +          int numDirectBits = ((posSlot >> 1) - 1); +          rep0 = (2 | ((UInt32)posSlot & 1)); +          if (posSlot < kEndPosModelIndex) +          { +            rep0 <<= numDirectBits; +            prob = p + SpecPos + rep0 - posSlot - 1; +          } +          else +          { +            numDirectBits -= kNumAlignBits; +            do +            { +              RC_NORMALIZE +              Range >>= 1; +              rep0 <<= 1; +              if (Code >= Range) +              { +                Code -= Range; +                rep0 |= 1; +              } +            } +            while (--numDirectBits != 0); +            prob = p + Align; +            rep0 <<= kNumAlignBits; +            numDirectBits = kNumAlignBits; +          } +          { +            int i = 1; +            int mi = 1; +            do +            { +              CProb *prob3 = prob + mi; +              RC_GET_BIT2(prob3, mi, ; , rep0 |= i); +              i <<= 1; +            } +            while(--numDirectBits != 0); +          } +        } +        else +          rep0 = posSlot; +        if (++rep0 == (UInt32)(0)) +        { +          /* it's for stream version */ +          len = kLzmaStreamWasFinishedId; +          break; +        } +      } + +      len += kMatchMinLen; +      if (rep0 > nowPos) +        return LZMA_RESULT_DATA_ERROR; + + +      do +      { +        previousByte = outStream[nowPos - rep0]; +        len--; +        outStream[nowPos++] = previousByte; +      } +      while(len != 0 && nowPos < outSize); +    } +  } +  RC_NORMALIZE; + + +  *inSizeProcessed = (SizeT)(Buffer - inStream); +  *outSizeProcessed = nowPos; +  return LZMA_RESULT_OK; +} diff --git a/roms/seabios/src/fw/lzmadecode.h b/roms/seabios/src/fw/lzmadecode.h new file mode 100644 index 00000000..dedde0de --- /dev/null +++ b/roms/seabios/src/fw/lzmadecode.h @@ -0,0 +1,67 @@ +/*  +  LzmaDecode.h +  LZMA Decoder interface + +  LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) +  http://www.7-zip.org/ + +  LZMA SDK is licensed under two licenses: +  1) GNU Lesser General Public License (GNU LGPL) +  2) Common Public License (CPL) +  It means that you can select one of these two licenses and  +  follow rules of that license. + +  SPECIAL EXCEPTION: +  Igor Pavlov, as the author of this code, expressly permits you to  +  statically or dynamically link your code (or bind by name) to the  +  interfaces of this file without subjecting your linked code to the  +  terms of the CPL or GNU LGPL. Any modifications or additions  +  to this file, however, are subject to the LGPL or CPL terms. +*/ + +#ifndef __LZMADECODE_H +#define __LZMADECODE_H + +typedef unsigned char Byte; +typedef unsigned short UInt16; +typedef unsigned int UInt32; +typedef UInt32 SizeT; + +#define CProb UInt16 + +#define LZMA_RESULT_OK 0 +#define LZMA_RESULT_DATA_ERROR 1 + + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LZMA_PROPERTIES_SIZE 5 + +typedef struct _CLzmaProperties +{ +  int lc; +  int lp; +  int pb; +}CLzmaProperties; + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size); + +#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp))) + +#define kLzmaNeedInitId (-2) + +typedef struct _CLzmaDecoderState +{ +  CLzmaProperties Properties; +  CProb *Probs; + + +} CLzmaDecoderState; + + +int LzmaDecode(CLzmaDecoderState *vs, +    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, +    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed); + +#endif diff --git a/roms/seabios/src/fw/mptable.c b/roms/seabios/src/fw/mptable.c new file mode 100644 index 00000000..8e01e004 --- /dev/null +++ b/roms/seabios/src/fw/mptable.c @@ -0,0 +1,196 @@ +// MPTable generation (on emulators) +// DO NOT ADD NEW FEATURES HERE.  (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008-2010  Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "hw/pci.h" +#include "hw/pci_regs.h" +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "std/mptable.h" // MPTABLE_SIGNATURE +#include "string.h" // memset +#include "util.h" // MaxCountCPUs +#include "x86.h" // cpuid + +void +mptable_setup(void) +{ +    if (! CONFIG_MPTABLE) +        return; + +    dprintf(3, "init MPTable\n"); + +    // Config structure in temp area. +    struct mptable_config_s *config = malloc_tmp(32*1024); +    if (!config) { +        warn_noalloc(); +        return; +    } +    memset(config, 0, sizeof(*config)); +    config->signature = MPCONFIG_SIGNATURE; +    config->spec = 4; +    memcpy(config->oemid, BUILD_CPUNAME8, sizeof(config->oemid)); +    memcpy(config->productid, "0.1         ", sizeof(config->productid)); +    config->lapic = BUILD_APIC_ADDR; + +    // Detect cpu info +    u32 cpuid_signature, ebx, ecx, cpuid_features; +    cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features); +    if (! cpuid_signature) { +        // Use default values. +        cpuid_signature = 0x600; +        cpuid_features = 0x201; +    } +    int pkgcpus = 1; +    if (cpuid_features & (1 << 28)) { +        /* Only populate the MPS tables with the first logical CPU in +           each package */ +        pkgcpus = (ebx >> 16) & 0xff; +        pkgcpus = 1 << (__fls(pkgcpus - 1) + 1); /* round up to power of 2 */ +    } +    u8 apic_version = readl((u8*)BUILD_APIC_ADDR + 0x30) & 0xff; + +    // CPU definitions. +    struct mpt_cpu *cpus = (void*)&config[1], *cpu = cpus; +    int i; +    for (i = 0; i < MaxCountCPUs; i+=pkgcpus) { +        memset(cpu, 0, sizeof(*cpu)); +        cpu->type = MPT_TYPE_CPU; +        cpu->apicid = i; +        cpu->apicver = apic_version; +        /* cpu flags: enabled, bootstrap cpu */ +        cpu->cpuflag = (apic_id_is_present(i) ? 0x01 : 0x00) | ((i==0) ? 0x02 : 0x00); +        cpu->cpusignature = cpuid_signature; +        cpu->featureflag = cpuid_features; +        cpu++; +    } +    int entrycount = cpu - cpus; + +    // PCI bus +    struct mpt_bus *buses = (void*)cpu, *bus = buses; +    if (!hlist_empty(&PCIDevices)) { +        memset(bus, 0, sizeof(*bus)); +        bus->type = MPT_TYPE_BUS; +        bus->busid = 0; +        memcpy(bus->bustype, "PCI   ", sizeof(bus->bustype)); +        bus++; +        entrycount++; +    } + +    /* isa bus */ +    int isabusid = bus - buses; +    memset(bus, 0, sizeof(*bus)); +    bus->type = MPT_TYPE_BUS; +    bus->busid = isabusid; +    memcpy(bus->bustype, "ISA   ", sizeof(bus->bustype)); +    bus++; +    entrycount++; + +    /* ioapic */ +    u8 ioapic_id = BUILD_IOAPIC_ID; +    struct mpt_ioapic *ioapic = (void*)bus; +    memset(ioapic, 0, sizeof(*ioapic)); +    ioapic->type = MPT_TYPE_IOAPIC; +    ioapic->apicid = ioapic_id; +    ioapic->apicver = 0x11; +    ioapic->flags = 1; // enable +    ioapic->apicaddr = BUILD_IOAPIC_ADDR; +    entrycount++; + +    /* irqs */ +    struct mpt_intsrc *intsrcs = (void*)&ioapic[1], *intsrc = intsrcs; +    int dev = -1; +    unsigned short pinmask = 0; + +    struct pci_device *pci; +    foreachpci(pci) { +        u16 bdf = pci->bdf; +        if (pci_bdf_to_bus(bdf) != 0) +            break; +        int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); +        int irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); +        if (pin == 0) +            continue; +        if (dev != pci_bdf_to_busdev(bdf)) { +            dev = pci_bdf_to_busdev(bdf); +            pinmask = 0; +        } +        if (pinmask & (1 << pin)) /* pin was seen already */ +            continue; +        pinmask |= (1 << pin); +        memset(intsrc, 0, sizeof(*intsrc)); +        intsrc->type = MPT_TYPE_INTSRC; +        intsrc->irqtype = 0; /* INT */ +        intsrc->irqflag = 1; /* active high */ +        intsrc->srcbus = pci_bdf_to_bus(bdf); /* PCI bus */ +        intsrc->srcbusirq = (pci_bdf_to_dev(bdf) << 2) | (pin - 1); +        intsrc->dstapic = ioapic_id; +        intsrc->dstirq = irq; +        intsrc++; +    } + +    int irq0_override = romfile_loadint("etc/irq0-override", 0); +    for (i = 0; i < 16; i++) { +        memset(intsrc, 0, sizeof(*intsrc)); +        if (BUILD_PCI_IRQS & (1 << i)) +            continue; +        intsrc->type = MPT_TYPE_INTSRC; +        intsrc->irqtype = 0; /* INT */ +        intsrc->irqflag = 0; /* conform to bus spec */ +        intsrc->srcbus = isabusid; /* ISA bus */ +        intsrc->srcbusirq = i; +        intsrc->dstapic = ioapic_id; +        intsrc->dstirq = i; +        if (irq0_override) { +            /* Destination 2 is covered by irq0->inti2 override (i == +               0). Source IRQ 2 is unused */ +            if (i == 0) +                intsrc->dstirq = 2; +            else if (i == 2) +                intsrc--; +        } +        intsrc++; +    } + +    /* Local interrupt assignment */ +    intsrc->type = MPT_TYPE_LOCAL_INT; +    intsrc->irqtype = 3; /* ExtINT */ +    intsrc->irqflag = 0; /* PO, EL default */ +    intsrc->srcbus = isabusid; /* ISA */ +    intsrc->srcbusirq = 0; +    intsrc->dstapic = 0; /* BSP == APIC #0 */ +    intsrc->dstirq = 0; /* LINTIN0 */ +    intsrc++; + +    intsrc->type = MPT_TYPE_LOCAL_INT; +    intsrc->irqtype = 1; /* NMI */ +    intsrc->irqflag = 0; /* PO, EL default */ +    intsrc->srcbus = isabusid; /* ISA */ +    intsrc->srcbusirq = 0; +    intsrc->dstapic = 0xff; /* to all local APICs */ +    intsrc->dstirq = 1; /* LINTIN1 */ +    intsrc++; +    entrycount += intsrc - intsrcs; + +    // Finalize config structure. +    int length = (void*)intsrc - (void*)config; +    config->entrycount = entrycount; +    config->length = length; +    config->checksum -= checksum(config, length); + +    // floating pointer structure +    struct mptable_floating_s floating; +    memset(&floating, 0, sizeof(floating)); +    floating.signature = MPTABLE_SIGNATURE; +    floating.physaddr = (u32)config; +    floating.length = 1; +    floating.spec_rev = 4; +    floating.checksum -= checksum(&floating, sizeof(floating)); +    copy_mptable(&floating); +    free(config); +} diff --git a/roms/seabios/src/fw/mtrr.c b/roms/seabios/src/fw/mtrr.c new file mode 100644 index 00000000..913580e7 --- /dev/null +++ b/roms/seabios/src/fw/mtrr.c @@ -0,0 +1,106 @@ +// Initialize MTRRs - mostly useful on KVM. +// +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "hw/pci.h" // pcimem_start +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "util.h" // mtrr_setup +#include "x86.h" // cpuid + +#define MSR_MTRRcap                    0x000000fe +#define MSR_MTRRfix64K_00000           0x00000250 +#define MSR_MTRRfix16K_80000           0x00000258 +#define MSR_MTRRfix16K_A0000           0x00000259 +#define MSR_MTRRfix4K_C0000            0x00000268 +#define MSR_MTRRfix4K_C8000            0x00000269 +#define MSR_MTRRfix4K_D0000            0x0000026a +#define MSR_MTRRfix4K_D8000            0x0000026b +#define MSR_MTRRfix4K_E0000            0x0000026c +#define MSR_MTRRfix4K_E8000            0x0000026d +#define MSR_MTRRfix4K_F0000            0x0000026e +#define MSR_MTRRfix4K_F8000            0x0000026f +#define MSR_MTRRdefType                0x000002ff + +#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) +#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) + +#define MTRR_MEMTYPE_UC 0 +#define MTRR_MEMTYPE_WC 1 +#define MTRR_MEMTYPE_WT 4 +#define MTRR_MEMTYPE_WP 5 +#define MTRR_MEMTYPE_WB 6 + +void mtrr_setup(void) +{ +    if (!CONFIG_MTRR_INIT) +        return; + +    u32 eax, ebx, ecx, edx, cpuid_features; +    cpuid(1, &eax, &ebx, &ecx, &cpuid_features); +    if (!(cpuid_features & CPUID_MTRR)) +        return; +    if (!(cpuid_features & CPUID_MSR)) +        return; + +    dprintf(3, "init mtrr\n"); + +    u32 mtrr_cap = rdmsr(MSR_MTRRcap); +    int vcnt = mtrr_cap & 0xff; +    int fix = mtrr_cap & 0x100; +    if (!vcnt || !fix) +       return; + +    // Disable MTRRs +    wrmsr_smp(MSR_MTRRdefType, 0); + +    // Set fixed MTRRs +    union u64b { +        u8 valb[8]; +        u64 val; +    } u; +    u.val = 0; +    int i; +    for (i = 0; i < 8; i++) +        if (RamSize >= 65536 * (i + 1)) +            u.valb[i] = MTRR_MEMTYPE_WB; +    wrmsr_smp(MSR_MTRRfix64K_00000, u.val); +    u.val = 0; +    for (i = 0; i < 8; i++) +        if (RamSize >= 0x80000 + 16384 * (i + 1)) +            u.valb[i] = MTRR_MEMTYPE_WB; +    wrmsr_smp(MSR_MTRRfix16K_80000, u.val); +    wrmsr_smp(MSR_MTRRfix16K_A0000, 0);   // 0xA0000-0xC0000 is uncached +    int j; +    for (j = 0; j < 8; j++) { +        u.val = 0; +        for (i = 0; i < 8; i++) +            if (RamSize >= 0xC0000 + j * 0x8000 + 4096 * (i + 1)) +                u.valb[i] = MTRR_MEMTYPE_WP; +        wrmsr_smp(MSR_MTRRfix4K_C0000 + j, u.val); +    } + +    // Set variable MTRRs +    int phys_bits = 36; +    cpuid(0x80000000u, &eax, &ebx, &ecx, &edx); +    if (eax >= 0x80000008) { +        /* Get physical bits from leaf 0x80000008 (if available) */ +        cpuid(0x80000008u, &eax, &ebx, &ecx, &edx); +        phys_bits = eax & 0xff; +    } +    u64 phys_mask = ((1ull << phys_bits) - 1); +    for (i=0; i<vcnt; i++) { +        wrmsr_smp(MTRRphysBase_MSR(i), 0); +        wrmsr_smp(MTRRphysMask_MSR(i), 0); +    } +    /* Mark 3.5-4GB as UC, anything not specified defaults to WB */ +    wrmsr_smp(MTRRphysBase_MSR(0), pcimem_start | MTRR_MEMTYPE_UC); +    wrmsr_smp(MTRRphysMask_MSR(0) +              , (-((1ull<<32)-pcimem_start) & phys_mask) | 0x800); + +    // Enable fixed and variable MTRRs; set default type. +    wrmsr_smp(MSR_MTRRdefType, 0xc00 | MTRR_MEMTYPE_WB); +} diff --git a/roms/seabios/src/fw/paravirt.c b/roms/seabios/src/fw/paravirt.c new file mode 100644 index 00000000..db22ae8f --- /dev/null +++ b/roms/seabios/src/fw/paravirt.c @@ -0,0 +1,448 @@ +// Paravirtualization support. +// +// Copyright (C) 2013  Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2009 Red Hat Inc. +// +// Authors: +//  Gleb Natapov <gnatapov@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // be32_to_cpu +#include "config.h" // CONFIG_QEMU +#include "hw/pci.h" // create_pirtable +#include "hw/pci_regs.h" // PCI_DEVICE_ID +#include "hw/rtc.h" // CMOS_* +#include "malloc.h" // malloc_tmp +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // qemu_cfg_preinit +#include "romfile.h" // romfile_loadint +#include "romfile_loader.h" // romfile_loader_execute +#include "string.h" // memset +#include "util.h" // pci_setup +#include "x86.h" // cpuid +#include "xen.h" // xen_biostable_setup + +// Amount of continuous ram under 4Gig +u32 RamSize; +// Amount of continuous ram >4Gig +u64 RamSizeOver4G; +// Type of emulator platform. +int PlatformRunningOn VARFSEG; + +/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx.  It + * should be used to determine that a VM is running under KVM. + */ +#define KVM_CPUID_SIGNATURE     0x40000000 + +static void kvm_detect(void) +{ +    unsigned int eax, ebx, ecx, edx; +    char signature[13]; + +    cpuid(KVM_CPUID_SIGNATURE, &eax, &ebx, &ecx, &edx); +    memcpy(signature + 0, &ebx, 4); +    memcpy(signature + 4, &ecx, 4); +    memcpy(signature + 8, &edx, 4); +    signature[12] = 0; + +    if (strcmp(signature, "KVMKVMKVM") == 0) { +        dprintf(1, "Running on KVM\n"); +        PlatformRunningOn |= PF_KVM; +    } +} + +static void qemu_detect(void) +{ +    if (!CONFIG_QEMU_HARDWARE) +        return; + +    // check northbridge @ 00:00.0 +    u16 v = pci_config_readw(0, PCI_VENDOR_ID); +    if (v == 0x0000 || v == 0xffff) +        return; +    u16 d = pci_config_readw(0, PCI_DEVICE_ID); +    u16 sv = pci_config_readw(0, PCI_SUBSYSTEM_VENDOR_ID); +    u16 sd = pci_config_readw(0, PCI_SUBSYSTEM_ID); + +    if (sv != 0x1af4 || /* Red Hat, Inc */ +        sd != 0x1100)   /* Qemu virtual machine */ +        return; + +    PlatformRunningOn |= PF_QEMU; +    switch (d) { +    case 0x1237: +        dprintf(1, "Running on QEMU (i440fx)\n"); +        break; +    case 0x29c0: +        dprintf(1, "Running on QEMU (q35)\n"); +        break; +    default: +        dprintf(1, "Running on QEMU (unknown nb: %04x:%04x)\n", v, d); +        break; +    } +    kvm_detect(); +} + +void +qemu_preinit(void) +{ +    qemu_detect(); + +    if (!CONFIG_QEMU) +        return; + +    if (runningOnXen()) { +        xen_ramsize_preinit(); +        return; +    } + +    if (!runningOnQEMU()) { +        dprintf(1, "Warning: No QEMU Northbridge found (isapc?)\n"); +        PlatformRunningOn |= PF_QEMU; +        kvm_detect(); +    } + +    // On emulators, get memory size from nvram. +    u32 rs = ((rtc_read(CMOS_MEM_EXTMEM2_LOW) << 16) +              | (rtc_read(CMOS_MEM_EXTMEM2_HIGH) << 24)); +    if (rs) +        rs += 16 * 1024 * 1024; +    else +        rs = (((rtc_read(CMOS_MEM_EXTMEM_LOW) << 10) +               | (rtc_read(CMOS_MEM_EXTMEM_HIGH) << 18)) +              + 1 * 1024 * 1024); +    RamSize = rs; +    add_e820(0, rs, E820_RAM); + +    /* reserve 256KB BIOS area at the end of 4 GB */ +    add_e820(0xfffc0000, 256*1024, E820_RESERVED); + +    dprintf(1, "RamSize: 0x%08x [cmos]\n", RamSize); +} + +void +qemu_platform_setup(void) +{ +    if (!CONFIG_QEMU) +        return; + +    if (runningOnXen()) { +        pci_probe_devices(); +        xen_hypercall_setup(); +        xen_biostable_setup(); +        return; +    } + +    // Initialize pci +    pci_setup(); +    smm_device_setup(); +    smm_setup(); + +    // Initialize mtrr and smp +    mtrr_setup(); +    smp_setup(); + +    // Create bios tables +    pirtable_setup(); +    mptable_setup(); +    smbios_setup(); + +    if (CONFIG_FW_ROMFILE_LOAD) { +        int loader_err; + +        dprintf(3, "load ACPI tables\n"); + +        loader_err = romfile_loader_execute("etc/table-loader"); + +        RsdpAddr = find_acpi_rsdp(); + +        if (RsdpAddr) +            return; + +        /* If present, loader should have installed an RSDP. +         * Not installed? We might still be able to continue +         * using the builtin RSDP. +         */ +        if (!loader_err) +            warn_internalerror(); +    } + +    acpi_setup(); +} + + +/**************************************************************** + * QEMU firmware config (fw_cfg) interface + ****************************************************************/ + +// List of QEMU fw_cfg entries.  DO NOT ADD MORE.  (All new content +// should be passed via the fw_cfg "file" interface.) +#define QEMU_CFG_SIGNATURE              0x00 +#define QEMU_CFG_ID                     0x01 +#define QEMU_CFG_UUID                   0x02 +#define QEMU_CFG_NUMA                   0x0d +#define QEMU_CFG_BOOT_MENU              0x0e +#define QEMU_CFG_MAX_CPUS               0x0f +#define QEMU_CFG_FILE_DIR               0x19 +#define QEMU_CFG_ARCH_LOCAL             0x8000 +#define QEMU_CFG_ACPI_TABLES            (QEMU_CFG_ARCH_LOCAL + 0) +#define QEMU_CFG_SMBIOS_ENTRIES         (QEMU_CFG_ARCH_LOCAL + 1) +#define QEMU_CFG_IRQ0_OVERRIDE          (QEMU_CFG_ARCH_LOCAL + 2) +#define QEMU_CFG_E820_TABLE             (QEMU_CFG_ARCH_LOCAL + 3) + +static void +qemu_cfg_select(u16 f) +{ +    outw(f, PORT_QEMU_CFG_CTL); +} + +static void +qemu_cfg_read(void *buf, int len) +{ +    insb(PORT_QEMU_CFG_DATA, buf, len); +} + +static void +qemu_cfg_skip(int len) +{ +    while (len--) +        inb(PORT_QEMU_CFG_DATA); +} + +static void +qemu_cfg_read_entry(void *buf, int e, int len) +{ +    qemu_cfg_select(e); +    qemu_cfg_read(buf, len); +} + +struct qemu_romfile_s { +    struct romfile_s file; +    int select, skip; +}; + +static int +qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen) +{ +    if (file->size > maxlen) +        return -1; +    struct qemu_romfile_s *qfile; +    qfile = container_of(file, struct qemu_romfile_s, file); +    qemu_cfg_select(qfile->select); +    qemu_cfg_skip(qfile->skip); +    qemu_cfg_read(dst, file->size); +    return file->size; +} + +static void +qemu_romfile_add(char *name, int select, int skip, int size) +{ +    struct qemu_romfile_s *qfile = malloc_tmp(sizeof(*qfile)); +    if (!qfile) { +        warn_noalloc(); +        return; +    } +    memset(qfile, 0, sizeof(*qfile)); +    strtcpy(qfile->file.name, name, sizeof(qfile->file.name)); +    qfile->file.size = size; +    qfile->select = select; +    qfile->skip = skip; +    qfile->file.copy = qemu_cfg_read_file; +    romfile_add(&qfile->file); +} + +struct e820_reservation { +    u64 address; +    u64 length; +    u32 type; +}; + +#define SMBIOS_FIELD_ENTRY 0 +#define SMBIOS_TABLE_ENTRY 1 + +struct qemu_smbios_header { +    u16 length; +    u8 headertype; +    u8 tabletype; +    u16 fieldoffset; +} PACKED; + +static void +qemu_cfg_e820(void) +{ +    struct e820_reservation *table; +    int i, size; + +    if (!CONFIG_QEMU) +        return; + +    // "etc/e820" has both ram and reservations +    table = romfile_loadfile("etc/e820", &size); +    if (table) { +        for (i = 0; i < size / sizeof(struct e820_reservation); i++) { +            switch (table[i].type) { +            case E820_RAM: +                dprintf(1, "RamBlock: addr 0x%016llx len 0x%016llx [e820]\n", +                        table[i].address, table[i].length); +                if (table[i].address < RamSize) +                    // ignore, preinit got it from cmos already and +                    // adding this again would ruin any reservations +                    // done so far +                    continue; +                if (table[i].address < 0x100000000LL) { +                    // below 4g -- adjust RamSize to mark highest lowram addr +                    if (RamSize < table[i].address + table[i].length) +                        RamSize = table[i].address + table[i].length; +                } else { +                    // above 4g -- adjust RamSizeOver4G to mark highest ram addr +                    if (0x100000000LL + RamSizeOver4G < table[i].address + table[i].length) +                        RamSizeOver4G = table[i].address + table[i].length - 0x100000000LL; +                } +                /* fall through */ +            case E820_RESERVED: +                add_e820(table[i].address, table[i].length, table[i].type); +                break; +            default: +                /* +                 * Qemu 1.7 uses RAM + RESERVED only.  Ignore +                 * everything else, so we have the option to +                 * extend this in the future without breakage. +                 */ +                break; +            } +        } +        return; +    } + +    // QEMU_CFG_E820_TABLE has reservations only +    u32 count32; +    qemu_cfg_read_entry(&count32, QEMU_CFG_E820_TABLE, sizeof(count32)); +    if (count32) { +        struct e820_reservation entry; +        int i; +        for (i = 0; i < count32; i++) { +            qemu_cfg_read(&entry, sizeof(entry)); +            add_e820(entry.address, entry.length, entry.type); +        } +    } else if (runningOnKVM()) { +        // Backwards compatibility - provide hard coded range. +        // 4 pages before the bios, 3 pages for vmx tss pages, the +        // other page for EPT real mode pagetable +        add_e820(0xfffbc000, 4*4096, E820_RESERVED); +    } + +    // Check for memory over 4Gig in cmos +    u64 high = ((rtc_read(CMOS_MEM_HIGHMEM_LOW) << 16) +                | ((u32)rtc_read(CMOS_MEM_HIGHMEM_MID) << 24) +                | ((u64)rtc_read(CMOS_MEM_HIGHMEM_HIGH) << 32)); +    RamSizeOver4G = high; +    add_e820(0x100000000ull, high, E820_RAM); +    dprintf(1, "RamSizeOver4G: 0x%016llx [cmos]\n", RamSizeOver4G); +} + +// Populate romfile entries for legacy fw_cfg ports (that predate the +// "file" interface). +static void +qemu_cfg_legacy(void) +{ +    if (!CONFIG_QEMU) +        return; + +    // Misc config items. +    qemu_romfile_add("etc/show-boot-menu", QEMU_CFG_BOOT_MENU, 0, 2); +    qemu_romfile_add("etc/irq0-override", QEMU_CFG_IRQ0_OVERRIDE, 0, 1); +    qemu_romfile_add("etc/max-cpus", QEMU_CFG_MAX_CPUS, 0, 2); + +    // NUMA data +    u64 numacount; +    qemu_cfg_read_entry(&numacount, QEMU_CFG_NUMA, sizeof(numacount)); +    int max_cpu = romfile_loadint("etc/max-cpus", 0); +    qemu_romfile_add("etc/numa-cpu-map", QEMU_CFG_NUMA, sizeof(numacount) +                     , max_cpu*sizeof(u64)); +    qemu_romfile_add("etc/numa-nodes", QEMU_CFG_NUMA +                     , sizeof(numacount) + max_cpu*sizeof(u64) +                     , numacount*sizeof(u64)); + +    // ACPI tables +    char name[128]; +    u16 cnt; +    qemu_cfg_read_entry(&cnt, QEMU_CFG_ACPI_TABLES, sizeof(cnt)); +    int i, offset = sizeof(cnt); +    for (i = 0; i < cnt; i++) { +        u16 len; +        qemu_cfg_read(&len, sizeof(len)); +        offset += sizeof(len); +        snprintf(name, sizeof(name), "acpi/table%d", i); +        qemu_romfile_add(name, QEMU_CFG_ACPI_TABLES, offset, len); +        qemu_cfg_skip(len); +        offset += len; +    } + +    // SMBIOS info +    qemu_cfg_read_entry(&cnt, QEMU_CFG_SMBIOS_ENTRIES, sizeof(cnt)); +    offset = sizeof(cnt); +    for (i = 0; i < cnt; i++) { +        struct qemu_smbios_header header; +        qemu_cfg_read(&header, sizeof(header)); +        if (header.headertype == SMBIOS_FIELD_ENTRY) { +            snprintf(name, sizeof(name), "smbios/field%d-%d" +                     , header.tabletype, header.fieldoffset); +            qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES +                             , offset + sizeof(header) +                             , header.length - sizeof(header)); +        } else { +            snprintf(name, sizeof(name), "smbios/table%d-%d" +                     , header.tabletype, i); +            qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES +                             , offset + 3, header.length - 3); +        } +        qemu_cfg_skip(header.length - sizeof(header)); +        offset += header.length; +    } +} + +struct QemuCfgFile { +    u32  size;        /* file size */ +    u16  select;      /* write this to 0x510 to read it */ +    u16  reserved; +    char name[56]; +}; + +void qemu_cfg_init(void) +{ +    if (!runningOnQEMU()) +        return; + +    // Detect fw_cfg interface. +    qemu_cfg_select(QEMU_CFG_SIGNATURE); +    char *sig = "QEMU"; +    int i; +    for (i = 0; i < 4; i++) +        if (inb(PORT_QEMU_CFG_DATA) != sig[i]) +            return; +    dprintf(1, "Found QEMU fw_cfg\n"); + +    // Populate romfiles for legacy fw_cfg entries +    qemu_cfg_legacy(); + +    // Load files found in the fw_cfg file directory +    u32 count; +    qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count)); +    count = be32_to_cpu(count); +    u32 e; +    for (e = 0; e < count; e++) { +        struct QemuCfgFile qfile; +        qemu_cfg_read(&qfile, sizeof(qfile)); +        qemu_romfile_add(qfile.name, be16_to_cpu(qfile.select) +                         , 0, be32_to_cpu(qfile.size)); +    } + +    qemu_cfg_e820(); + +    if (romfile_find("etc/table-loader")) { +        acpi_pm_base = 0x0600; +        dprintf(1, "Moving pm_base to 0x%x\n", acpi_pm_base); +    } +} diff --git a/roms/seabios/src/fw/paravirt.h b/roms/seabios/src/fw/paravirt.h new file mode 100644 index 00000000..95ffb92a --- /dev/null +++ b/roms/seabios/src/fw/paravirt.h @@ -0,0 +1,37 @@ +#ifndef __PV_H +#define __PV_H + +#include "config.h" // CONFIG_* +#include "biosvar.h" // GET_GLOBAL + +// Types of paravirtualized platforms. +#define PF_QEMU     (1<<0) +#define PF_XEN      (1<<1) +#define PF_KVM      (1<<2) + +extern u32 RamSize; +extern u64 RamSizeOver4G; +extern int PlatformRunningOn; + +static inline int runningOnQEMU(void) { +    return CONFIG_QEMU || ( +        CONFIG_QEMU_HARDWARE && GET_GLOBAL(PlatformRunningOn) & PF_QEMU); +} +static inline int runningOnXen(void) { +    return CONFIG_XEN && GET_GLOBAL(PlatformRunningOn) & PF_XEN; +} +static inline int runningOnKVM(void) { +    return CONFIG_QEMU && GET_GLOBAL(PlatformRunningOn) & PF_KVM; +} + +// Common paravirt ports. +#define PORT_SMI_CMD           0x00b2 +#define PORT_SMI_STATUS        0x00b3 +#define PORT_QEMU_CFG_CTL      0x0510 +#define PORT_QEMU_CFG_DATA     0x0511 + +void qemu_preinit(void); +void qemu_platform_setup(void); +void qemu_cfg_init(void); + +#endif diff --git a/roms/seabios/src/fw/pciinit.c b/roms/seabios/src/fw/pciinit.c new file mode 100644 index 00000000..46ae7090 --- /dev/null +++ b/roms/seabios/src/fw/pciinit.c @@ -0,0 +1,954 @@ +// Initialize PCI devices (on emulators) +// +// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // le64_to_cpu +#include "config.h" // CONFIG_* +#include "dev-q35.h" // Q35_HOST_BRIDGE_PCIEXBAR_ADDR +#include "dev-piix.h" // PIIX_* +#include "hw/ata.h" // PORT_ATA1_CMD_BASE +#include "hw/pci.h" // pci_config_readl +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_COMMAND +#include "list.h" // struct hlist_node +#include "malloc.h" // free +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "romfile.h" // romfile_loadint +#include "string.h" // memset +#include "util.h" // pci_setup +#include "x86.h" // outb + +#define PCI_DEVICE_MEM_MIN    (1<<12)  // 4k == page size +#define PCI_BRIDGE_MEM_MIN    (1<<21)  // 2M == hugepage size +#define PCI_BRIDGE_IO_MIN      0x1000  // mandated by pci bridge spec + +static const char *region_type_name[] = { +    [ PCI_REGION_TYPE_IO ]      = "io", +    [ PCI_REGION_TYPE_MEM ]     = "mem", +    [ PCI_REGION_TYPE_PREFMEM ] = "prefmem", +}; + +u64 pcimem_start   = BUILD_PCIMEM_START; +u64 pcimem_end     = BUILD_PCIMEM_END; +u64 pcimem64_start = BUILD_PCIMEM64_START; +u64 pcimem64_end   = BUILD_PCIMEM64_END; +u64 pci_io_low_end = 0xa000; + +struct pci_region_entry { +    struct pci_device *dev; +    int bar; +    u64 size; +    u64 align; +    int is64; +    enum pci_region_type type; +    struct hlist_node node; +}; + +struct pci_region { +    /* pci region assignments */ +    u64 base; +    struct hlist_head list; +}; + +struct pci_bus { +    struct pci_region r[PCI_REGION_TYPE_COUNT]; +    struct pci_device *bus_dev; +}; + +static u32 pci_bar(struct pci_device *pci, int region_num) +{ +    if (region_num != PCI_ROM_SLOT) { +        return PCI_BASE_ADDRESS_0 + region_num * 4; +    } + +#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 +    u8 type = pci->header_type & ~PCI_HEADER_TYPE_MULTI_FUNCTION; +    return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS; +} + +static void +pci_set_io_region_addr(struct pci_device *pci, int bar, u64 addr, int is64) +{ +    u32 ofs = pci_bar(pci, bar); +    pci_config_writel(pci->bdf, ofs, addr); +    if (is64) +        pci_config_writel(pci->bdf, ofs + 4, addr >> 32); +} + + +/**************************************************************** + * Misc. device init + ****************************************************************/ + +/* host irqs corresponding to PCI irqs A-D */ +const u8 pci_irqs[4] = { +    10, 10, 11, 11 +}; + +static int dummy_pci_slot_get_irq(struct pci_device *pci, int pin) +{ +    dprintf(1, "pci_slot_get_irq called with unknown routing\n"); + +    return 0xff; /* PCI defined "unknown" or "no connection" for x86 */ +} + +static int (*pci_slot_get_irq)(struct pci_device *pci, int pin) = +    dummy_pci_slot_get_irq; + +// Return the global irq number corresponding to a host bus device irq pin. +static int piix_pci_slot_get_irq(struct pci_device *pci, int pin) +{ +    int slot_addend = 0; + +    while (pci->parent != NULL) { +        slot_addend += pci_bdf_to_dev(pci->bdf); +        pci = pci->parent; +    } +    slot_addend += pci_bdf_to_dev(pci->bdf) - 1; +    return pci_irqs[(pin - 1 + slot_addend) & 3]; +} + +static int mch_pci_slot_get_irq(struct pci_device *pci, int pin) +{ +    int pin_addend = 0; +    while (pci->parent != NULL) { +        pin_addend += pci_bdf_to_dev(pci->bdf); +        pci = pci->parent; +    } +    u8 slot = pci_bdf_to_dev(pci->bdf); +    if (slot <= 24) +        /* Slots 0-24 rotate slot:pin mapping similar to piix above, but +           with a different starting index - see q35-acpi-dsdt.dsl */ +        return pci_irqs[(pin - 1 + pin_addend + slot) & 3]; +    /* Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H) */ +    return pci_irqs[(pin - 1 + pin_addend) & 3]; +} + +/* PIIX3/PIIX4 PCI to ISA bridge */ +static void piix_isa_bridge_setup(struct pci_device *pci, void *arg) +{ +    int i, irq; +    u8 elcr[2]; + +    elcr[0] = 0x00; +    elcr[1] = 0x00; +    for (i = 0; i < 4; i++) { +        irq = pci_irqs[i]; +        /* set to trigger level */ +        elcr[irq >> 3] |= (1 << (irq & 7)); +        /* activate irq remapping in PIIX */ +        pci_config_writeb(pci->bdf, 0x60 + i, irq); +    } +    outb(elcr[0], PIIX_PORT_ELCR1); +    outb(elcr[1], PIIX_PORT_ELCR2); +    dprintf(1, "PIIX3/PIIX4 init: elcr=%02x %02x\n", elcr[0], elcr[1]); +} + +/* ICH9 LPC PCI to ISA bridge */ +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +static void mch_isa_bridge_setup(struct pci_device *dev, void *arg) +{ +    u16 bdf = dev->bdf; +    int i, irq; +    u8 elcr[2]; + +    elcr[0] = 0x00; +    elcr[1] = 0x00; + +    for (i = 0; i < 4; i++) { +        irq = pci_irqs[i]; +        /* set to trigger level */ +        elcr[irq >> 3] |= (1 << (irq & 7)); + +        /* activate irq remapping in LPC */ + +        /* PIRQ[A-D] routing */ +        pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT + i, irq); +        /* PIRQ[E-H] routing */ +        pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT + i, irq); +    } +    outb(elcr[0], ICH9_LPC_PORT_ELCR1); +    outb(elcr[1], ICH9_LPC_PORT_ELCR2); +    dprintf(1, "Q35 LPC init: elcr=%02x %02x\n", elcr[0], elcr[1]); + +    /* pm io base */ +    pci_config_writel(bdf, ICH9_LPC_PMBASE, +                      acpi_pm_base | ICH9_LPC_PMBASE_RTE); + +    /* acpi enable, SCI: IRQ9 000b = irq9*/ +    pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_ACPI_EN); + +    acpi_pm1a_cnt = acpi_pm_base + 0x04; +    pmtimer_setup(acpi_pm_base + 0x08); +} + +static void storage_ide_setup(struct pci_device *pci, void *arg) +{ +    /* IDE: we map it as in ISA mode */ +    pci_set_io_region_addr(pci, 0, PORT_ATA1_CMD_BASE, 0); +    pci_set_io_region_addr(pci, 1, PORT_ATA1_CTRL_BASE, 0); +    pci_set_io_region_addr(pci, 2, PORT_ATA2_CMD_BASE, 0); +    pci_set_io_region_addr(pci, 3, PORT_ATA2_CTRL_BASE, 0); +} + +/* PIIX3/PIIX4 IDE */ +static void piix_ide_setup(struct pci_device *pci, void *arg) +{ +    u16 bdf = pci->bdf; +    pci_config_writew(bdf, 0x40, 0x8000); // enable IDE0 +    pci_config_writew(bdf, 0x42, 0x8000); // enable IDE1 +} + +static void pic_ibm_setup(struct pci_device *pci, void *arg) +{ +    /* PIC, IBM, MPIC & MPIC2 */ +    pci_set_io_region_addr(pci, 0, 0x80800000 + 0x00040000, 0); +} + +static void apple_macio_setup(struct pci_device *pci, void *arg) +{ +    /* macio bridge */ +    pci_set_io_region_addr(pci, 0, 0x80800000, 0); +} + +static void piix4_pm_config_setup(u16 bdf) +{ +    // acpi sci is hardwired to 9 +    pci_config_writeb(bdf, PCI_INTERRUPT_LINE, 9); + +    pci_config_writel(bdf, PIIX_PMBASE, acpi_pm_base | 1); +    pci_config_writeb(bdf, PIIX_PMREGMISC, 0x01); /* enable PM io space */ +    pci_config_writel(bdf, PIIX_SMBHSTBASE, (acpi_pm_base + 0x100) | 1); +    pci_config_writeb(bdf, PIIX_SMBHSTCFG, 0x09); /* enable SMBus io space */ +} + +static int PiixPmBDF = -1; + +/* PIIX4 Power Management device (for ACPI) */ +static void piix4_pm_setup(struct pci_device *pci, void *arg) +{ +    PiixPmBDF = pci->bdf; +    piix4_pm_config_setup(pci->bdf); + +    acpi_pm1a_cnt = acpi_pm_base + 0x04; +    pmtimer_setup(acpi_pm_base + 0x08); +} + +/* ICH9 SMBUS */ +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_SMBUS */ +static void ich9_smbus_setup(struct pci_device *dev, void *arg) +{ +    u16 bdf = dev->bdf; +    /* map smbus into io space */ +    pci_config_writel(bdf, ICH9_SMB_SMB_BASE, +                      (acpi_pm_base + 0x100) | PCI_BASE_ADDRESS_SPACE_IO); + +    /* enable SMBus */ +    pci_config_writeb(bdf, ICH9_SMB_HOSTC, ICH9_SMB_HOSTC_HST_EN); +} + +static const struct pci_device_id pci_device_tbl[] = { +    /* PIIX3/PIIX4 PCI to ISA bridge */ +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, +               piix_isa_bridge_setup), +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, +               piix_isa_bridge_setup), +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, +               mch_isa_bridge_setup), + +    /* STORAGE IDE */ +    PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, +                     PCI_CLASS_STORAGE_IDE, piix_ide_setup), +    PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, +                     PCI_CLASS_STORAGE_IDE, piix_ide_setup), +    PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE, +                     storage_ide_setup), + +    /* PIC, IBM, MIPC & MPIC2 */ +    PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0x0046, PCI_CLASS_SYSTEM_PIC, +                     pic_ibm_setup), +    PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0xFFFF, PCI_CLASS_SYSTEM_PIC, +                     pic_ibm_setup), + +    /* PIIX4 Power Management device (for ACPI) */ +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, +               piix4_pm_setup), +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_SMBUS, +               ich9_smbus_setup), + +    /* 0xff00 */ +    PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0017, 0xff00, apple_macio_setup), +    PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0022, 0xff00, apple_macio_setup), + +    PCI_DEVICE_END, +}; + +void pci_resume(void) +{ +    if (!CONFIG_QEMU) { +        return; +    } + +    if (PiixPmBDF >= 0) { +        piix4_pm_config_setup(PiixPmBDF); +    } +} + +static void pci_bios_init_device(struct pci_device *pci) +{ +    u16 bdf = pci->bdf; +    dprintf(1, "PCI: init bdf=%02x:%02x.%x id=%04x:%04x\n" +            , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf) +            , pci->vendor, pci->device); + +    /* map the interrupt */ +    int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); +    if (pin != 0) +        pci_config_writeb(bdf, PCI_INTERRUPT_LINE, pci_slot_get_irq(pci, pin)); + +    pci_init_device(pci_device_tbl, pci, NULL); + +    /* enable memory mappings */ +    pci_config_maskw(bdf, PCI_COMMAND, 0, +                     PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_SERR); +} + +static void pci_bios_init_devices(void) +{ +    struct pci_device *pci; +    foreachpci(pci) { +        pci_bios_init_device(pci); +    } +} + +static void pci_enable_default_vga(void) +{ +    struct pci_device *pci; + +    foreachpci(pci) { +        if (is_pci_vga(pci)) { +            dprintf(1, "PCI: Using %02x:%02x.%x for primary VGA\n", +                    pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), +                    pci_bdf_to_fn(pci->bdf)); +            return; +        } +    } + +    pci = pci_find_class(PCI_CLASS_DISPLAY_VGA); +    if (!pci) { +        dprintf(1, "PCI: No VGA devices found\n"); +        return; +    } + +    dprintf(1, "PCI: Enabling %02x:%02x.%x for primary VGA\n", +            pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), +            pci_bdf_to_fn(pci->bdf)); + +    pci_config_maskw(pci->bdf, PCI_COMMAND, 0, +                     PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + +    while (pci->parent) { +        pci = pci->parent; + +        dprintf(1, "PCI: Setting VGA enable on bridge %02x:%02x.%x\n", +                pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), +                pci_bdf_to_fn(pci->bdf)); + +        pci_config_maskw(pci->bdf, PCI_BRIDGE_CONTROL, 0, PCI_BRIDGE_CTL_VGA); +        pci_config_maskw(pci->bdf, PCI_COMMAND, 0, +                         PCI_COMMAND_IO | PCI_COMMAND_MEMORY); +    } +} + +/**************************************************************** + * Platform device initialization + ****************************************************************/ + +static void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) +{ +    if (RamSize <= 0x80000000) +        pcimem_start = 0x80000000; +    else if (RamSize <= 0xc0000000) +        pcimem_start = 0xc0000000; + +    pci_slot_get_irq = piix_pci_slot_get_irq; +} + +static void mch_mem_addr_setup(struct pci_device *dev, void *arg) +{ +    u64 addr = Q35_HOST_BRIDGE_PCIEXBAR_ADDR; +    u32 size = Q35_HOST_BRIDGE_PCIEXBAR_SIZE; + +    /* setup mmconfig */ +    u16 bdf = dev->bdf; +    u32 upper = addr >> 32; +    u32 lower = (addr & 0xffffffff) | Q35_HOST_BRIDGE_PCIEXBAREN; +    pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, 0); +    pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR + 4, upper); +    pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); +    add_e820(addr, size, E820_RESERVED); + +    /* setup pci i/o window (above mmconfig) */ +    pcimem_start = addr + size; + +    pci_slot_get_irq = mch_pci_slot_get_irq; + +    /* setup io address space */ +    if (acpi_pm_base < 0x1000) +        pci_io_low_end = 0x10000; +    else +        pci_io_low_end = acpi_pm_base; +} + +static const struct pci_device_id pci_platform_tbl[] = { +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, +               i440fx_mem_addr_setup), +    PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH, +               mch_mem_addr_setup), +    PCI_DEVICE_END +}; + +static void pci_bios_init_platform(void) +{ +    struct pci_device *pci; +    foreachpci(pci) { +        pci_init_device(pci_platform_tbl, pci, NULL); +    } +} + + +/**************************************************************** + * Bus initialization + ****************************************************************/ + +static void +pci_bios_init_bus_rec(int bus, u8 *pci_bus) +{ +    int bdf; +    u16 class; + +    dprintf(1, "PCI: %s bus = 0x%x\n", __func__, bus); + +    /* prevent accidental access to unintended devices */ +    foreachbdf(bdf, bus) { +        class = pci_config_readw(bdf, PCI_CLASS_DEVICE); +        if (class == PCI_CLASS_BRIDGE_PCI) { +            pci_config_writeb(bdf, PCI_SECONDARY_BUS, 255); +            pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 0); +        } +    } + +    foreachbdf(bdf, bus) { +        class = pci_config_readw(bdf, PCI_CLASS_DEVICE); +        if (class != PCI_CLASS_BRIDGE_PCI) { +            continue; +        } +        dprintf(1, "PCI: %s bdf = 0x%x\n", __func__, bdf); + +        u8 pribus = pci_config_readb(bdf, PCI_PRIMARY_BUS); +        if (pribus != bus) { +            dprintf(1, "PCI: primary bus = 0x%x -> 0x%x\n", pribus, bus); +            pci_config_writeb(bdf, PCI_PRIMARY_BUS, bus); +        } else { +            dprintf(1, "PCI: primary bus = 0x%x\n", pribus); +        } + +        u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); +        (*pci_bus)++; +        if (*pci_bus != secbus) { +            dprintf(1, "PCI: secondary bus = 0x%x -> 0x%x\n", +                    secbus, *pci_bus); +            secbus = *pci_bus; +            pci_config_writeb(bdf, PCI_SECONDARY_BUS, secbus); +        } else { +            dprintf(1, "PCI: secondary bus = 0x%x\n", secbus); +        } + +        /* set to max for access to all subordinate buses. +           later set it to accurate value */ +        u8 subbus = pci_config_readb(bdf, PCI_SUBORDINATE_BUS); +        pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 255); + +        pci_bios_init_bus_rec(secbus, pci_bus); + +        if (subbus != *pci_bus) { +            dprintf(1, "PCI: subordinate bus = 0x%x -> 0x%x\n", +                    subbus, *pci_bus); +            subbus = *pci_bus; +        } else { +            dprintf(1, "PCI: subordinate bus = 0x%x\n", subbus); +        } +        pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, subbus); +    } +} + +static void +pci_bios_init_bus(void) +{ +    u8 extraroots = romfile_loadint("etc/extra-pci-roots", 0); +    u8 pci_bus = 0; + +    pci_bios_init_bus_rec(0 /* host bus */, &pci_bus); + +    if (extraroots) { +        while (pci_bus < 0xff) { +            pci_bus++; +            pci_bios_init_bus_rec(pci_bus, &pci_bus); +        } +    } +} + + +/**************************************************************** + * Bus sizing + ****************************************************************/ + +static void +pci_bios_get_bar(struct pci_device *pci, int bar, +                 int *ptype, u64 *psize, int *pis64) +{ +    u32 ofs = pci_bar(pci, bar); +    u16 bdf = pci->bdf; +    u32 old = pci_config_readl(bdf, ofs); +    int is64 = 0, type = PCI_REGION_TYPE_MEM; +    u64 mask; + +    if (bar == PCI_ROM_SLOT) { +        mask = PCI_ROM_ADDRESS_MASK; +        pci_config_writel(bdf, ofs, mask); +    } else { +        if (old & PCI_BASE_ADDRESS_SPACE_IO) { +            mask = PCI_BASE_ADDRESS_IO_MASK; +            type = PCI_REGION_TYPE_IO; +        } else { +            mask = PCI_BASE_ADDRESS_MEM_MASK; +            if (old & PCI_BASE_ADDRESS_MEM_PREFETCH) +                type = PCI_REGION_TYPE_PREFMEM; +            is64 = ((old & PCI_BASE_ADDRESS_MEM_TYPE_MASK) +                    == PCI_BASE_ADDRESS_MEM_TYPE_64); +        } +        pci_config_writel(bdf, ofs, ~0); +    } +    u64 val = pci_config_readl(bdf, ofs); +    pci_config_writel(bdf, ofs, old); +    if (is64) { +        u32 hold = pci_config_readl(bdf, ofs + 4); +        pci_config_writel(bdf, ofs + 4, ~0); +        u32 high = pci_config_readl(bdf, ofs + 4); +        pci_config_writel(bdf, ofs + 4, hold); +        val |= ((u64)high << 32); +        mask |= ((u64)0xffffffff << 32); +        *psize = (~(val & mask)) + 1; +    } else { +        *psize = ((~(val & mask)) + 1) & 0xffffffff; +    } +    *ptype = type; +    *pis64 = is64; +} + +static int pci_bios_bridge_region_is64(struct pci_region *r, +                                 struct pci_device *pci, int type) +{ +    if (type != PCI_REGION_TYPE_PREFMEM) +        return 0; +    u32 pmem = pci_config_readl(pci->bdf, PCI_PREF_MEMORY_BASE); +    if (!pmem) { +        pci_config_writel(pci->bdf, PCI_PREF_MEMORY_BASE, 0xfff0fff0); +        pmem = pci_config_readl(pci->bdf, PCI_PREF_MEMORY_BASE); +        pci_config_writel(pci->bdf, PCI_PREF_MEMORY_BASE, 0x0); +    } +    if ((pmem & PCI_PREF_RANGE_TYPE_MASK) != PCI_PREF_RANGE_TYPE_64) +       return 0; +    struct pci_region_entry *entry; +    hlist_for_each_entry(entry, &r->list, node) { +        if (!entry->is64) +            return 0; +    } +    return 1; +} + +static u64 pci_region_align(struct pci_region *r) +{ +    struct pci_region_entry *entry; +    hlist_for_each_entry(entry, &r->list, node) { +        // The first entry in the sorted list has the largest alignment +        return entry->align; +    } +    return 1; +} + +static u64 pci_region_sum(struct pci_region *r) +{ +    u64 sum = 0; +    struct pci_region_entry *entry; +    hlist_for_each_entry(entry, &r->list, node) { +        sum += entry->size; +    } +    return sum; +} + +static void pci_region_migrate_64bit_entries(struct pci_region *from, +                                             struct pci_region *to) +{ +    struct hlist_node *n, **last = &to->list.first; +    struct pci_region_entry *entry; +    hlist_for_each_entry_safe(entry, n, &from->list, node) { +        if (!entry->is64) +            continue; +        if (entry->dev->class == PCI_CLASS_SERIAL_USB) +            continue; +        // Move from source list to destination list. +        hlist_del(&entry->node); +        hlist_add(&entry->node, last); +        last = &entry->node.next; +    } +} + +static struct pci_region_entry * +pci_region_create_entry(struct pci_bus *bus, struct pci_device *dev, +                        int bar, u64 size, u64 align, int type, int is64) +{ +    struct pci_region_entry *entry = malloc_tmp(sizeof(*entry)); +    if (!entry) { +        warn_noalloc(); +        return NULL; +    } +    memset(entry, 0, sizeof(*entry)); +    entry->dev = dev; +    entry->bar = bar; +    entry->size = size; +    entry->align = align; +    entry->is64 = is64; +    entry->type = type; +    // Insert into list in sorted order. +    struct hlist_node **pprev; +    struct pci_region_entry *pos; +    hlist_for_each_entry_pprev(pos, pprev, &bus->r[type].list, node) { +        if (pos->align < align || (pos->align == align && pos->size < size)) +            break; +    } +    hlist_add(&entry->node, pprev); +    return entry; +} + +static int pci_bus_hotplug_support(struct pci_bus *bus) +{ +    u8 pcie_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_EXP); +    u8 shpc_cap; + +    if (pcie_cap) { +        u16 pcie_flags = pci_config_readw(bus->bus_dev->bdf, +                                          pcie_cap + PCI_EXP_FLAGS); +        u8 port_type = ((pcie_flags & PCI_EXP_FLAGS_TYPE) >> +                       (__builtin_ffs(PCI_EXP_FLAGS_TYPE) - 1)); +        u8 downstream_port = (port_type == PCI_EXP_TYPE_DOWNSTREAM) || +                             (port_type == PCI_EXP_TYPE_ROOT_PORT); +        /* +         * PCI Express SPEC, 7.8.2: +         *   Slot Implemented – When Set, this bit indicates that the Link +         *   HwInit associated with this Port is connected to a slot (as +         *   compared to being connected to a system-integrated device or +         *   being disabled). +         *   This bit is valid for Downstream Ports. This bit is undefined +         *   for Upstream Ports. +         */ +        u16 slot_implemented = pcie_flags & PCI_EXP_FLAGS_SLOT; + +        return downstream_port && slot_implemented; +    } + +    shpc_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_SHPC); +    return !!shpc_cap; +} + +static int pci_bios_check_devices(struct pci_bus *busses) +{ +    dprintf(1, "PCI: check devices\n"); + +    // Calculate resources needed for regular (non-bus) devices. +    struct pci_device *pci; +    foreachpci(pci) { +        if (pci->class == PCI_CLASS_BRIDGE_PCI) +            busses[pci->secondary_bus].bus_dev = pci; + +        struct pci_bus *bus = &busses[pci_bdf_to_bus(pci->bdf)]; +        if (!bus->bus_dev) +            /* +             * Resources for all root busses go in busses[0] +             */ +            bus = &busses[0]; +        int i; +        for (i = 0; i < PCI_NUM_REGIONS; i++) { +            if ((pci->class == PCI_CLASS_BRIDGE_PCI) && +                (i >= PCI_BRIDGE_NUM_REGIONS && i < PCI_ROM_SLOT)) +                continue; +            int type, is64; +            u64 size; +            pci_bios_get_bar(pci, i, &type, &size, &is64); +            if (size == 0) +                continue; + +            if (type != PCI_REGION_TYPE_IO && size < PCI_DEVICE_MEM_MIN) +                size = PCI_DEVICE_MEM_MIN; +            struct pci_region_entry *entry = pci_region_create_entry( +                bus, pci, i, size, size, type, is64); +            if (!entry) +                return -1; + +            if (is64) +                i++; +        } +    } + +    // Propagate required bus resources to parent busses. +    int secondary_bus; +    for (secondary_bus=MaxPCIBus; secondary_bus>0; secondary_bus--) { +        struct pci_bus *s = &busses[secondary_bus]; +        if (!s->bus_dev) +            continue; +        struct pci_bus *parent = &busses[pci_bdf_to_bus(s->bus_dev->bdf)]; +        if (!parent->bus_dev) +            /* +             * Resources for all root busses go in busses[0] +             */ +            parent = &busses[0]; +        int type; +        int hotplug_support = pci_bus_hotplug_support(s); +        for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { +            u64 align = (type == PCI_REGION_TYPE_IO) ? +                PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN; +            if (!pci_bridge_has_region(s->bus_dev, type)) +                continue; +            if (pci_region_align(&s->r[type]) > align) +                 align = pci_region_align(&s->r[type]); +            u64 sum = pci_region_sum(&s->r[type]); +            if (!sum && hotplug_support) +                sum = align; /* reserve min size for hot-plug */ +            u64 size = ALIGN(sum, align); +            int is64 = pci_bios_bridge_region_is64(&s->r[type], +                                            s->bus_dev, type); +            // entry->bar is -1 if the entry represents a bridge region +            struct pci_region_entry *entry = pci_region_create_entry( +                parent, s->bus_dev, -1, size, align, type, is64); +            if (!entry) +                return -1; +            dprintf(1, "PCI: secondary bus %d size %08llx type %s\n", +                      entry->dev->secondary_bus, size, +                      region_type_name[entry->type]); +        } +    } +    return 0; +} + + +/**************************************************************** + * BAR assignment + ****************************************************************/ + +// Setup region bases (given the regions' size and alignment) +static int pci_bios_init_root_regions_io(struct pci_bus *bus) +{ +    /* +     * QEMU I/O address space usage: +     *   0000 - 0fff    legacy isa, pci config, pci root bus, ... +     *   1000 - 9fff    free +     *   a000 - afff    hotplug (cpu, pci via acpi, i440fx/piix only) +     *   b000 - bfff    power management (PORT_ACPI_PM_BASE) +     *                  [ qemu 1.4+ implements pci config registers +     *                    properly so guests can place the registers +     *                    where they want, on older versions its fixed ] +     *   c000 - ffff    free, traditionally used for pci io +     */ +    struct pci_region *r_io = &bus->r[PCI_REGION_TYPE_IO]; +    u64 sum = pci_region_sum(r_io); +    if (sum < 0x4000) { +        /* traditional region is big enougth, use it */ +        r_io->base = 0xc000; +    } else if (sum < pci_io_low_end - 0x1000) { +        /* use the larger region at 0x1000 */ +        r_io->base = 0x1000; +    } else { +        /* not enouth io address space -> error out */ +        return -1; +    } +    dprintf(1, "PCI: IO: %4llx - %4llx\n", r_io->base, r_io->base + sum - 1); +    return 0; +} + +static int pci_bios_init_root_regions_mem(struct pci_bus *bus) +{ +    struct pci_region *r_end = &bus->r[PCI_REGION_TYPE_PREFMEM]; +    struct pci_region *r_start = &bus->r[PCI_REGION_TYPE_MEM]; + +    if (pci_region_align(r_start) < pci_region_align(r_end)) { +        // Swap regions to improve alignment. +        r_end = r_start; +        r_start = &bus->r[PCI_REGION_TYPE_PREFMEM]; +    } +    u64 sum = pci_region_sum(r_end); +    u64 align = pci_region_align(r_end); +    r_end->base = ALIGN_DOWN((pcimem_end - sum), align); +    sum = pci_region_sum(r_start); +    align = pci_region_align(r_start); +    r_start->base = ALIGN_DOWN((r_end->base - sum), align); + +    if ((r_start->base < pcimem_start) || +         (r_start->base > pcimem_end)) +        // Memory range requested is larger than available. +        return -1; +    return 0; +} + +#define PCI_IO_SHIFT            8 +#define PCI_MEMORY_SHIFT        16 +#define PCI_PREF_MEMORY_SHIFT   16 + +static void +pci_region_map_one_entry(struct pci_region_entry *entry, u64 addr) +{ +    u16 bdf = entry->dev->bdf; +    if (entry->bar >= 0) { +        dprintf(1, "PCI: map device bdf=%02x:%02x.%x" +                "  bar %d, addr %08llx, size %08llx [%s]\n", +                pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf), +                entry->bar, addr, entry->size, region_type_name[entry->type]); + +        pci_set_io_region_addr(entry->dev, entry->bar, addr, entry->is64); +        return; +    } + +    u64 limit = addr + entry->size - 1; +    if (entry->type == PCI_REGION_TYPE_IO) { +        pci_config_writeb(bdf, PCI_IO_BASE, addr >> PCI_IO_SHIFT); +        pci_config_writew(bdf, PCI_IO_BASE_UPPER16, 0); +        pci_config_writeb(bdf, PCI_IO_LIMIT, limit >> PCI_IO_SHIFT); +        pci_config_writew(bdf, PCI_IO_LIMIT_UPPER16, 0); +    } +    if (entry->type == PCI_REGION_TYPE_MEM) { +        pci_config_writew(bdf, PCI_MEMORY_BASE, addr >> PCI_MEMORY_SHIFT); +        pci_config_writew(bdf, PCI_MEMORY_LIMIT, limit >> PCI_MEMORY_SHIFT); +    } +    if (entry->type == PCI_REGION_TYPE_PREFMEM) { +        pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, addr >> PCI_PREF_MEMORY_SHIFT); +        pci_config_writew(bdf, PCI_PREF_MEMORY_LIMIT, limit >> PCI_PREF_MEMORY_SHIFT); +        pci_config_writel(bdf, PCI_PREF_BASE_UPPER32, addr >> 32); +        pci_config_writel(bdf, PCI_PREF_LIMIT_UPPER32, limit >> 32); +    } +} + +static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r) +{ +    struct hlist_node *n; +    struct pci_region_entry *entry; +    hlist_for_each_entry_safe(entry, n, &r->list, node) { +        u64 addr = r->base; +        r->base += entry->size; +        if (entry->bar == -1) +            // Update bus base address if entry is a bridge region +            busses[entry->dev->secondary_bus].r[entry->type].base = addr; +        pci_region_map_one_entry(entry, addr); +        hlist_del(&entry->node); +        free(entry); +    } +} + +static void pci_bios_map_devices(struct pci_bus *busses) +{ +    if (pci_bios_init_root_regions_io(busses)) +        panic("PCI: out of I/O address space\n"); + +    dprintf(1, "PCI: 32: %016llx - %016llx\n", pcimem_start, pcimem_end); +    if (pci_bios_init_root_regions_mem(busses)) { +        struct pci_region r64_mem, r64_pref; +        r64_mem.list.first = NULL; +        r64_pref.list.first = NULL; +        pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_MEM], +                                         &r64_mem); +        pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_PREFMEM], +                                         &r64_pref); + +        if (pci_bios_init_root_regions_mem(busses)) +            panic("PCI: out of 32bit address space\n"); + +        u64 sum_mem = pci_region_sum(&r64_mem); +        u64 sum_pref = pci_region_sum(&r64_pref); +        u64 align_mem = pci_region_align(&r64_mem); +        u64 align_pref = pci_region_align(&r64_pref); + +        r64_mem.base = le64_to_cpu(romfile_loadint("etc/reserved-memory-end", 0)); +        if (r64_mem.base < 0x100000000LL + RamSizeOver4G) +            r64_mem.base = 0x100000000LL + RamSizeOver4G; +        r64_mem.base = ALIGN(r64_mem.base, align_mem); +        r64_mem.base = ALIGN(r64_mem.base, (1LL<<30));    // 1G hugepage +        r64_pref.base = r64_mem.base + sum_mem; +        r64_pref.base = ALIGN(r64_pref.base, align_pref); +        r64_pref.base = ALIGN(r64_pref.base, (1LL<<30));  // 1G hugepage +        pcimem64_start = r64_mem.base; +        pcimem64_end = r64_pref.base + sum_pref; +        pcimem64_end = ALIGN(pcimem64_end, (1LL<<30));    // 1G hugepage +        dprintf(1, "PCI: 64: %016llx - %016llx\n", pcimem64_start, pcimem64_end); + +        pci_region_map_entries(busses, &r64_mem); +        pci_region_map_entries(busses, &r64_pref); +    } else { +        // no bars mapped high -> drop 64bit window (see dsdt) +        pcimem64_start = 0; +    } +    // Map regions on each device. +    int bus; +    for (bus = 0; bus<=MaxPCIBus; bus++) { +        int type; +        for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) +            pci_region_map_entries(busses, &busses[bus].r[type]); +    } +} + + +/**************************************************************** + * Main setup code + ****************************************************************/ + +void +pci_setup(void) +{ +    if (!CONFIG_QEMU) +        return; + +    dprintf(3, "pci setup\n"); + +    dprintf(1, "=== PCI bus & bridge init ===\n"); +    if (pci_probe_host() != 0) { +        return; +    } +    pci_bios_init_bus(); + +    dprintf(1, "=== PCI device probing ===\n"); +    pci_probe_devices(); + +    pcimem_start = RamSize; +    pci_bios_init_platform(); + +    dprintf(1, "=== PCI new allocation pass #1 ===\n"); +    struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); +    if (!busses) { +        warn_noalloc(); +        return; +    } +    memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); +    if (pci_bios_check_devices(busses)) +        return; + +    dprintf(1, "=== PCI new allocation pass #2 ===\n"); +    pci_bios_map_devices(busses); + +    pci_bios_init_devices(); + +    free(busses); + +    pci_enable_default_vga(); +} diff --git a/roms/seabios/src/fw/pirtable.c b/roms/seabios/src/fw/pirtable.c new file mode 100644 index 00000000..a4944080 --- /dev/null +++ b/roms/seabios/src/fw/pirtable.c @@ -0,0 +1,103 @@ +// PIR table generation (for emulators) +// DO NOT ADD NEW FEATURES HERE.  (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002  MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "std/pirtable.h" // struct pir_header +#include "string.h" // checksum +#include "util.h" // PirAddr + +struct pir_table { +    struct pir_header pir; +    struct pir_slot slots[6]; +} PACKED; + +static struct pir_table PIR_TABLE = { +    .pir = { +        .version = 0x0100, +        .size = sizeof(struct pir_table), +        .router_devfunc = 0x08, +        .compatible_devid = 0x122e8086, +    }, +    .slots = { +        { +            // first slot entry PCI-to-ISA (embedded) +            .dev = 1<<3, +            .links = { +                {.link = 0x60, .bitmap = 0xdef8}, // INTA# +                {.link = 0x61, .bitmap = 0xdef8}, // INTB# +                {.link = 0x62, .bitmap = 0xdef8}, // INTC# +                {.link = 0x63, .bitmap = 0xdef8}, // INTD# +            }, +            .slot_nr = 0, // embedded +        }, { +            // second slot entry: 1st PCI slot +            .dev = 2<<3, +            .links = { +                {.link = 0x61, .bitmap = 0xdef8}, // INTA# +                {.link = 0x62, .bitmap = 0xdef8}, // INTB# +                {.link = 0x63, .bitmap = 0xdef8}, // INTC# +                {.link = 0x60, .bitmap = 0xdef8}, // INTD# +            }, +            .slot_nr = 1, +        }, { +            // third slot entry: 2nd PCI slot +            .dev = 3<<3, +            .links = { +                {.link = 0x62, .bitmap = 0xdef8}, // INTA# +                {.link = 0x63, .bitmap = 0xdef8}, // INTB# +                {.link = 0x60, .bitmap = 0xdef8}, // INTC# +                {.link = 0x61, .bitmap = 0xdef8}, // INTD# +            }, +            .slot_nr = 2, +        }, { +            // 4th slot entry: 3rd PCI slot +            .dev = 4<<3, +            .links = { +                {.link = 0x63, .bitmap = 0xdef8}, // INTA# +                {.link = 0x60, .bitmap = 0xdef8}, // INTB# +                {.link = 0x61, .bitmap = 0xdef8}, // INTC# +                {.link = 0x62, .bitmap = 0xdef8}, // INTD# +            }, +            .slot_nr = 3, +        }, { +            // 5th slot entry: 4rd PCI slot +            .dev = 5<<3, +            .links = { +                {.link = 0x60, .bitmap = 0xdef8}, // INTA# +                {.link = 0x61, .bitmap = 0xdef8}, // INTB# +                {.link = 0x62, .bitmap = 0xdef8}, // INTC# +                {.link = 0x63, .bitmap = 0xdef8}, // INTD# +            }, +            .slot_nr = 4, +        }, { +            // 6th slot entry: 5rd PCI slot +            .dev = 6<<3, +            .links = { +                {.link = 0x61, .bitmap = 0xdef8}, // INTA# +                {.link = 0x62, .bitmap = 0xdef8}, // INTB# +                {.link = 0x63, .bitmap = 0xdef8}, // INTC# +                {.link = 0x60, .bitmap = 0xdef8}, // INTD# +            }, +            .slot_nr = 5, +        }, +    } +}; + +void +pirtable_setup(void) +{ +    if (! CONFIG_PIRTABLE) +        return; + +    dprintf(3, "init PIR table\n"); + +    PIR_TABLE.pir.signature = PIR_SIGNATURE; +    PIR_TABLE.pir.checksum -= checksum(&PIR_TABLE, sizeof(PIR_TABLE)); +    copy_pir(&PIR_TABLE); +} diff --git a/roms/seabios/src/fw/q35-acpi-dsdt.dsl b/roms/seabios/src/fw/q35-acpi-dsdt.dsl new file mode 100644 index 00000000..5dec5414 --- /dev/null +++ b/roms/seabios/src/fw/q35-acpi-dsdt.dsl @@ -0,0 +1,450 @@ +/* + * Bochs/QEMU ACPI DSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA + */ +/* + * Copyright (c) 2010 Isaku Yamahata + *                    yamahata at valinux co jp + * Based on acpi-dsdt.dsl, but heavily modified for q35 chipset. + */ + +DefinitionBlock ( +    "q35-acpi-dsdt.aml",// Output Filename +    "DSDT",             // Signature +    0x01,               // DSDT Compliance Revision +    "BXPC",             // OEMID +    "BXDSDT",           // TABLE ID +    0x2                 // OEM Revision +    ) +{ + +#include "acpi-dsdt-dbug.dsl" + +    Scope(\_SB) { +        OperationRegion(PCST, SystemIO, 0xae00, 0x0c) +        OperationRegion(PCSB, SystemIO, 0xae0c, 0x01) +        Field(PCSB, AnyAcc, NoLock, WriteAsZeros) { +            PCIB, 8, +        } +    } + + +/**************************************************************** + * PCI Bus definition + ****************************************************************/ + +    Scope(\_SB) { +        Device(PCI0) { +            Name(_HID, EisaId("PNP0A08")) +            Name(_CID, EisaId("PNP0A03")) +            Name(_ADR, 0x00) +            Name(_UID, 1) + +            // _OSC: based on sample of ACPI3.0b spec +            Name(SUPP, 0) // PCI _OSC Support Field value +            Name(CTRL, 0) // PCI _OSC Control Field value +            Method(_OSC, 4) { +                // Create DWORD-addressable fields from the Capabilities Buffer +                CreateDWordField(Arg3, 0, CDW1) + +                // Check for proper UUID +                If (LEqual(Arg0, ToUUID("33DB4D5B-1FF7-401C-9657-7441C03DD766"))) { +                    // Create DWORD-addressable fields from the Capabilities Buffer +                    CreateDWordField(Arg3, 4, CDW2) +                    CreateDWordField(Arg3, 8, CDW3) + +                    // Save Capabilities DWORD2 & 3 +                    Store(CDW2, SUPP) +                    Store(CDW3, CTRL) + +                    // Always allow native PME, AER (no dependencies) +                    // Never allow SHPC (no SHPC controller in this system) +                    And(CTRL, 0x1D, CTRL) + +#if 0 // For now, nothing to do +                    If (Not(And(CDW1, 1))) { // Query flag clear? +                        // Disable GPEs for features granted native control. +                        If (And(CTRL, 0x01)) { // Hot plug control granted? +                            Store(0, HPCE) // clear the hot plug SCI enable bit +                            Store(1, HPCS) // clear the hot plug SCI status bit +                        } +                        If (And(CTRL, 0x04)) { // PME control granted? +                            Store(0, PMCE) // clear the PME SCI enable bit +                            Store(1, PMCS) // clear the PME SCI status bit +                        } +                        If (And(CTRL, 0x10)) { // OS restoring PCI Express cap structure? +                            // Set status to not restore PCI Express cap structure +                            // upon resume from S3 +                            Store(1, S3CR) +                        } +                    } +#endif +                    If (LNotEqual(Arg1, One)) { +                        // Unknown revision +                        Or(CDW1, 0x08, CDW1) +                    } +                    If (LNotEqual(CDW3, CTRL)) { +                        // Capabilities bits were masked +                        Or(CDW1, 0x10, CDW1) +                    } +                    // Update DWORD3 in the buffer +                    Store(CTRL, CDW3) +                } Else { +                    Or(CDW1, 4, CDW1) // Unrecognized UUID +                } +                Return (Arg3) +            } +        } +    } + +#include "acpi-dsdt-pci-crs.dsl" +#include "acpi-dsdt-hpet.dsl" + + +/**************************************************************** + * VGA + ****************************************************************/ + +    Scope(\_SB.PCI0) { +        Device(VGA) { +            Name(_ADR, 0x00010000) +            Method(_S1D, 0, NotSerialized) { +                Return (0x00) +            } +            Method(_S2D, 0, NotSerialized) { +                Return (0x00) +            } +            Method(_S3D, 0, NotSerialized) { +                Return (0x00) +            } +        } +    } + + +/**************************************************************** + * LPC ISA bridge + ****************************************************************/ + +    Scope(\_SB.PCI0) { +        /* PCI D31:f0 LPC ISA bridge */ +        Device(ISA) { +            /* PCI D31:f0 */ +            Name(_ADR, 0x001f0000) + +            /* ICH9 PCI to ISA irq remapping */ +            OperationRegion(PIRQ, PCI_Config, 0x60, 0x0C) + +            OperationRegion(LPCD, PCI_Config, 0x80, 0x2) +            Field(LPCD, AnyAcc, NoLock, Preserve) { +                COMA,   3, +                    ,   1, +                COMB,   3, + +                Offset(0x01), +                LPTD,   2, +                    ,   2, +                FDCD,   2 +            } +            OperationRegion(LPCE, PCI_Config, 0x82, 0x2) +            Field(LPCE, AnyAcc, NoLock, Preserve) { +                CAEN,   1, +                CBEN,   1, +                LPEN,   1, +                FDEN,   1 +            } +        } +    } + +#include "acpi-dsdt-isa.dsl" + + +/**************************************************************** + * PCI IRQs + ****************************************************************/ + +    /* Zero => PIC mode, One => APIC Mode */ +    Name(\PICF, Zero) +    Method(\_PIC, 1, NotSerialized) { +        Store(Arg0, \PICF) +    } + +    Scope(\_SB) { +        Scope(PCI0) { +#define prt_slot_lnk(nr, lnk0, lnk1, lnk2, lnk3)  \ +    Package() { nr##ffff, 0, lnk0, 0 },           \ +    Package() { nr##ffff, 1, lnk1, 0 },           \ +    Package() { nr##ffff, 2, lnk2, 0 },           \ +    Package() { nr##ffff, 3, lnk3, 0 } + +#define prt_slot_lnkA(nr) prt_slot_lnk(nr, LNKA, LNKB, LNKC, LNKD) +#define prt_slot_lnkB(nr) prt_slot_lnk(nr, LNKB, LNKC, LNKD, LNKA) +#define prt_slot_lnkC(nr) prt_slot_lnk(nr, LNKC, LNKD, LNKA, LNKB) +#define prt_slot_lnkD(nr) prt_slot_lnk(nr, LNKD, LNKA, LNKB, LNKC) + +#define prt_slot_lnkE(nr) prt_slot_lnk(nr, LNKE, LNKF, LNKG, LNKH) +#define prt_slot_lnkF(nr) prt_slot_lnk(nr, LNKF, LNKG, LNKH, LNKE) +#define prt_slot_lnkG(nr) prt_slot_lnk(nr, LNKG, LNKH, LNKE, LNKF) +#define prt_slot_lnkH(nr) prt_slot_lnk(nr, LNKH, LNKE, LNKF, LNKG) + +            Name(PRTP, package() { +                prt_slot_lnkE(0x0000), +                prt_slot_lnkF(0x0001), +                prt_slot_lnkG(0x0002), +                prt_slot_lnkH(0x0003), +                prt_slot_lnkE(0x0004), +                prt_slot_lnkF(0x0005), +                prt_slot_lnkG(0x0006), +                prt_slot_lnkH(0x0007), +                prt_slot_lnkE(0x0008), +                prt_slot_lnkF(0x0009), +                prt_slot_lnkG(0x000a), +                prt_slot_lnkH(0x000b), +                prt_slot_lnkE(0x000c), +                prt_slot_lnkF(0x000d), +                prt_slot_lnkG(0x000e), +                prt_slot_lnkH(0x000f), +                prt_slot_lnkE(0x0010), +                prt_slot_lnkF(0x0011), +                prt_slot_lnkG(0x0012), +                prt_slot_lnkH(0x0013), +                prt_slot_lnkE(0x0014), +                prt_slot_lnkF(0x0015), +                prt_slot_lnkG(0x0016), +                prt_slot_lnkH(0x0017), +                prt_slot_lnkE(0x0018), + +                /* INTA -> PIRQA for slot 25 - 31 +                   see the default value of D<N>IR */ +                prt_slot_lnkA(0x0019), +                prt_slot_lnkA(0x001a), +                prt_slot_lnkA(0x001b), +                prt_slot_lnkA(0x001c), +                prt_slot_lnkA(0x001d), + +                /* PCIe->PCI bridge. use PIRQ[E-H] */ +                prt_slot_lnkE(0x001e), + +                prt_slot_lnkA(0x001f) +            }) + +#define prt_slot_gsi(nr, gsi0, gsi1, gsi2, gsi3)  \ +    Package() { nr##ffff, 0, gsi0, 0 },           \ +    Package() { nr##ffff, 1, gsi1, 0 },           \ +    Package() { nr##ffff, 2, gsi2, 0 },           \ +    Package() { nr##ffff, 3, gsi3, 0 } + +#define prt_slot_gsiA(nr) prt_slot_gsi(nr, GSIA, GSIB, GSIC, GSID) +#define prt_slot_gsiB(nr) prt_slot_gsi(nr, GSIB, GSIC, GSID, GSIA) +#define prt_slot_gsiC(nr) prt_slot_gsi(nr, GSIC, GSID, GSIA, GSIB) +#define prt_slot_gsiD(nr) prt_slot_gsi(nr, GSID, GSIA, GSIB, GSIC) + +#define prt_slot_gsiE(nr) prt_slot_gsi(nr, GSIE, GSIF, GSIG, GSIH) +#define prt_slot_gsiF(nr) prt_slot_gsi(nr, GSIF, GSIG, GSIH, GSIE) +#define prt_slot_gsiG(nr) prt_slot_gsi(nr, GSIG, GSIH, GSIE, GSIF) +#define prt_slot_gsiH(nr) prt_slot_gsi(nr, GSIH, GSIE, GSIF, GSIG) + +            Name(PRTA, package() { +                prt_slot_gsiE(0x0000), +                prt_slot_gsiF(0x0001), +                prt_slot_gsiG(0x0002), +                prt_slot_gsiH(0x0003), +                prt_slot_gsiE(0x0004), +                prt_slot_gsiF(0x0005), +                prt_slot_gsiG(0x0006), +                prt_slot_gsiH(0x0007), +                prt_slot_gsiE(0x0008), +                prt_slot_gsiF(0x0009), +                prt_slot_gsiG(0x000a), +                prt_slot_gsiH(0x000b), +                prt_slot_gsiE(0x000c), +                prt_slot_gsiF(0x000d), +                prt_slot_gsiG(0x000e), +                prt_slot_gsiH(0x000f), +                prt_slot_gsiE(0x0010), +                prt_slot_gsiF(0x0011), +                prt_slot_gsiG(0x0012), +                prt_slot_gsiH(0x0013), +                prt_slot_gsiE(0x0014), +                prt_slot_gsiF(0x0015), +                prt_slot_gsiG(0x0016), +                prt_slot_gsiH(0x0017), +                prt_slot_gsiE(0x0018), + +                /* INTA -> PIRQA for slot 25 - 31, but 30 +                   see the default value of D<N>IR */ +                prt_slot_gsiA(0x0019), +                prt_slot_gsiA(0x001a), +                prt_slot_gsiA(0x001b), +                prt_slot_gsiA(0x001c), +                prt_slot_gsiA(0x001d), + +                /* PCIe->PCI bridge. use PIRQ[E-H] */ +                prt_slot_gsiE(0x001e), + +                prt_slot_gsiA(0x001f) +            }) + +            Method(_PRT, 0, NotSerialized) { +                /* PCI IRQ routing table, example from ACPI 2.0a specification, +                   section 6.2.8.1 */ +                /* Note: we provide the same info as the PCI routing +                   table of the Bochs BIOS */ +                If (LEqual(\PICF, Zero)) { +                    Return (PRTP) +                } Else { +                    Return (PRTA) +                } +            } +        } + +        Field(PCI0.ISA.PIRQ, ByteAcc, NoLock, Preserve) { +            PRQA,   8, +            PRQB,   8, +            PRQC,   8, +            PRQD,   8, + +            Offset(0x08), +            PRQE,   8, +            PRQF,   8, +            PRQG,   8, +            PRQH,   8 +        } + +        Method(IQST, 1, NotSerialized) { +            // _STA method - get status +            If (And(0x80, Arg0)) { +                Return (0x09) +            } +            Return (0x0B) +        } +        Method(IQCR, 1, Serialized) { +            // _CRS method - get current settings +            Name(PRR0, ResourceTemplate() { +                Interrupt(, Level, ActiveHigh, Shared) { 0 } +            }) +            CreateDWordField(PRR0, 0x05, PRRI) +            Store(And(Arg0, 0x0F), PRRI) +            Return (PRR0) +        } + +#define define_link(link, uid, reg)                             \ +        Device(link) {                                          \ +            Name(_HID, EISAID("PNP0C0F"))                       \ +            Name(_UID, uid)                                     \ +            Name(_PRS, ResourceTemplate() {                     \ +                Interrupt(, Level, ActiveHigh, Shared) {        \ +                    5, 10, 11                                   \ +                }                                               \ +            })                                                  \ +            Method(_STA, 0, NotSerialized) {                    \ +                Return (IQST(reg))                              \ +            }                                                   \ +            Method(_DIS, 0, NotSerialized) {                    \ +                Or(reg, 0x80, reg)                              \ +            }                                                   \ +            Method(_CRS, 0, NotSerialized) {                    \ +                Return (IQCR(reg))                              \ +            }                                                   \ +            Method(_SRS, 1, NotSerialized) {                    \ +                CreateDWordField(Arg0, 0x05, PRRI)              \ +                Store(PRRI, reg)                                \ +            }                                                   \ +        } + +        define_link(LNKA, 0, PRQA) +        define_link(LNKB, 1, PRQB) +        define_link(LNKC, 2, PRQC) +        define_link(LNKD, 3, PRQD) +        define_link(LNKE, 4, PRQE) +        define_link(LNKF, 5, PRQF) +        define_link(LNKG, 6, PRQG) +        define_link(LNKH, 7, PRQH) + +#define define_gsi_link(link, uid, gsi)                         \ +        Device(link) {                                          \ +            Name(_HID, EISAID("PNP0C0F"))                       \ +            Name(_UID, uid)                                     \ +            Name(_PRS, ResourceTemplate() {                     \ +                Interrupt(, Level, ActiveHigh, Shared) {        \ +                    gsi                                         \ +                }                                               \ +            })                                                  \ +            Name(_CRS, ResourceTemplate() {                     \ +                Interrupt(, Level, ActiveHigh, Shared) {        \ +                    gsi                                         \ +                }                                               \ +            })                                                  \ +            Method(_SRS, 1, NotSerialized) {                    \ +            }                                                   \ +        } + +        define_gsi_link(GSIA, 0, 0x10) +        define_gsi_link(GSIB, 0, 0x11) +        define_gsi_link(GSIC, 0, 0x12) +        define_gsi_link(GSID, 0, 0x13) +        define_gsi_link(GSIE, 0, 0x14) +        define_gsi_link(GSIF, 0, 0x15) +        define_gsi_link(GSIG, 0, 0x16) +        define_gsi_link(GSIH, 0, 0x17) +    } + +#include "acpi-dsdt-cpu-hotplug.dsl" + + +/**************************************************************** + * General purpose events + ****************************************************************/ + +    Scope(\_GPE) { +        Name(_HID, "ACPI0006") + +        Method(_L00) { +        } +        Method(_L01) { +            // CPU hotplug event +            \_SB.PRSC() +        } +        Method(_L02) { +        } +        Method(_L03) { +        } +        Method(_L04) { +        } +        Method(_L05) { +        } +        Method(_L06) { +        } +        Method(_L07) { +        } +        Method(_L08) { +        } +        Method(_L09) { +        } +        Method(_L0A) { +        } +        Method(_L0B) { +        } +        Method(_L0C) { +        } +        Method(_L0D) { +        } +        Method(_L0E) { +        } +        Method(_L0F) { +        } +    } +} diff --git a/roms/seabios/src/fw/romfile_loader.c b/roms/seabios/src/fw/romfile_loader.c new file mode 100644 index 00000000..f4b17ff9 --- /dev/null +++ b/roms/seabios/src/fw/romfile_loader.c @@ -0,0 +1,177 @@ +#include "romfile_loader.h" +#include "byteorder.h" // leXX_to_cpu/cpu_to_leXX +#include "util.h" // checksum +#include "string.h" // strcmp +#include "romfile.h" // struct romfile_s +#include "malloc.h" // Zone*, _malloc +#include "output.h" // warn_* + +struct romfile_loader_file { +    struct romfile_s *file; +    void *data; +}; +struct romfile_loader_files { +    int nfiles; +    struct romfile_loader_file files[]; +}; + +static struct romfile_loader_file * +romfile_loader_find(const char *name, +                    struct romfile_loader_files *files) +{ +    int i; +    if (name[ROMFILE_LOADER_FILESZ - 1]) +        return NULL; +    for (i = 0; i < files->nfiles; ++i) +        if (!strcmp(files->files[i].file->name, name)) +            return &files->files[i]; +    return NULL; +} + +static void romfile_loader_allocate(struct romfile_loader_entry_s *entry, +                                    struct romfile_loader_files *files) +{ +    struct zone_s *zone; +    struct romfile_loader_file *file = &files->files[files->nfiles]; +    void *data; +    int ret; +    unsigned alloc_align = le32_to_cpu(entry->alloc_align); + +    if (alloc_align & (alloc_align - 1)) +        goto err; + +    switch (entry->alloc_zone) { +        case ROMFILE_LOADER_ALLOC_ZONE_HIGH: +            zone = &ZoneHigh; +            break; +        case ROMFILE_LOADER_ALLOC_ZONE_FSEG: +            zone = &ZoneFSeg; +            break; +        default: +            goto err; +    } +    if (alloc_align < MALLOC_MIN_ALIGN) +        alloc_align = MALLOC_MIN_ALIGN; +    if (entry->alloc_file[ROMFILE_LOADER_FILESZ - 1]) +        goto err; +    file->file = romfile_find(entry->alloc_file); +    if (!file->file || !file->file->size) +        return; +    data = _malloc(zone, file->file->size, alloc_align); +    if (!data) { +        warn_noalloc(); +        return; +    } +    ret = file->file->copy(file->file, data, file->file->size); +    if (ret != file->file->size) +        goto file_err; +    file->data = data; +    files->nfiles++; +    return; + +file_err: +    free(data); +err: +    warn_internalerror(); +} + +static void romfile_loader_add_pointer(struct romfile_loader_entry_s *entry, +                                       struct romfile_loader_files *files) +{ +    struct romfile_loader_file *dest_file; +    struct romfile_loader_file *src_file; +    unsigned offset = le32_to_cpu(entry->pointer_offset); +    u64 pointer = 0; + +    dest_file = romfile_loader_find(entry->pointer_dest_file, files); +    src_file = romfile_loader_find(entry->pointer_src_file, files); + +    if (!dest_file || !src_file || !dest_file->data || !src_file->data || +        offset + entry->pointer_size < offset || +        offset + entry->pointer_size > dest_file->file->size || +        entry->pointer_size < 1 || entry->pointer_size > 8 || +        entry->pointer_size & (entry->pointer_size - 1)) +        goto err; + +    memcpy(&pointer, dest_file->data + offset, entry->pointer_size); +    pointer = le64_to_cpu(pointer); +    pointer += (unsigned long)src_file->data; +    pointer = cpu_to_le64(pointer); +    memcpy(dest_file->data + offset, &pointer, entry->pointer_size); + +    return; +err: +    warn_internalerror(); +} + +static void romfile_loader_add_checksum(struct romfile_loader_entry_s *entry, +                                        struct romfile_loader_files *files) +{ +    struct romfile_loader_file *file; +    unsigned offset = le32_to_cpu(entry->cksum_offset); +    unsigned start = le32_to_cpu(entry->cksum_start); +    unsigned len = le32_to_cpu(entry->cksum_length); +    u8 *data; + +    file = romfile_loader_find(entry->cksum_file, files); + +    if (!file || !file->data || offset >= file->file->size || +        start + len < start || start + len > file->file->size) +        goto err; + +    data = file->data + offset; +    *data -= checksum(file->data + start, len); + +    return; +err: +    warn_internalerror(); +} + +int romfile_loader_execute(const char *name) +{ +    struct romfile_loader_entry_s *entry; +    int size, offset = 0, nfiles; +    struct romfile_loader_files *files; +    void *data = romfile_loadfile(name, &size); +    if (!data) +        return -1; + +    if (size % sizeof(*entry)) { +        warn_internalerror(); +        goto err; +    } + +    /* (over)estimate the number of files to load. */ +    nfiles = size / sizeof(*entry); +    files = malloc_tmp(sizeof(*files) + nfiles * sizeof(files->files[0])); +    if (!files) { +        warn_noalloc(); +        goto err; +    } +    files->nfiles = 0; + +    for (offset = 0; offset < size; offset += sizeof(*entry)) { +        entry = data + offset; +        switch (le32_to_cpu(entry->command)) { +                case ROMFILE_LOADER_COMMAND_ALLOCATE: +                        romfile_loader_allocate(entry, files); +                        break; +                case ROMFILE_LOADER_COMMAND_ADD_POINTER: +                        romfile_loader_add_pointer(entry, files); +                        break; +                case ROMFILE_LOADER_COMMAND_ADD_CHECKSUM: +                        romfile_loader_add_checksum(entry, files); +                default: +                        /* Skip commands that we don't recognize. */ +                        break; +        } +    } + +    free(files); +    free(data); +    return 0; + +err: +    free(data); +    return -1; +} diff --git a/roms/seabios/src/fw/romfile_loader.h b/roms/seabios/src/fw/romfile_loader.h new file mode 100644 index 00000000..15eab2ac --- /dev/null +++ b/roms/seabios/src/fw/romfile_loader.h @@ -0,0 +1,72 @@ +#ifndef __ROMFILE_LOADER_H +#define __ROMFILE_LOADER_H + +#include "types.h" // u8 +#include "util.h" // romfile_s + +#define ROMFILE_LOADER_FILESZ 56 + +/* ROM file linker/loader interface. Linker uses little endian format */ +struct romfile_loader_entry_s { +    u32 command; +    union { +        /* +         * COMMAND_ALLOCATE - allocate a table from @alloc_file +         * subject to @alloc_align alignment (must be power of 2) +         * and @alloc_zone (can be HIGH or FSEG) requirements. +         * +         * Must appear exactly once for each file, and before +         * this file is referenced by any other command. +         */ +        struct { +            char alloc_file[ROMFILE_LOADER_FILESZ]; +            u32 alloc_align; +            u8 alloc_zone; +        }; + +        /* +         * COMMAND_ADD_POINTER - patch the table (originating from +         * @dest_file) at @pointer_offset, by adding a pointer to the table +         * originating from @src_file. 1,2,4 or 8 byte unsigned +         * addition is used depending on @pointer_size. +         */ +        struct { +            char pointer_dest_file[ROMFILE_LOADER_FILESZ]; +            char pointer_src_file[ROMFILE_LOADER_FILESZ]; +            u32 pointer_offset; +            u8 pointer_size; +        }; + +        /* +         * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by +         * @cksum_start and @cksum_length fields, +         * and then add the value at @cksum_offset. +         * Checksum simply sums -X for each byte X in the range +         * using 8-bit math. +         */ +        struct { +            char cksum_file[ROMFILE_LOADER_FILESZ]; +            u32 cksum_offset; +            u32 cksum_start; +            u32 cksum_length; +        }; + +        /* padding */ +        char pad[124]; +    }; +}; + +enum { +    ROMFILE_LOADER_COMMAND_ALLOCATE     = 0x1, +    ROMFILE_LOADER_COMMAND_ADD_POINTER  = 0x2, +    ROMFILE_LOADER_COMMAND_ADD_CHECKSUM = 0x3, +}; + +enum { +    ROMFILE_LOADER_ALLOC_ZONE_HIGH = 0x1, +    ROMFILE_LOADER_ALLOC_ZONE_FSEG = 0x2, +}; + +int romfile_loader_execute(const char *name); + +#endif diff --git a/roms/seabios/src/fw/shadow.c b/roms/seabios/src/fw/shadow.c new file mode 100644 index 00000000..4f00006b --- /dev/null +++ b/roms/seabios/src/fw/shadow.c @@ -0,0 +1,171 @@ +// Support for enabling/disabling BIOS ram shadowing. +// +// Copyright (C) 2008-2010  Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "dev-q35.h" // PCI_VENDOR_ID_INTEL +#include "dev-piix.h" // I440FX_PAM0 +#include "hw/pci.h" // pci_config_writeb +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_VENDOR_ID +#include "malloc.h" // rom_get_last +#include "output.h" // dprintf +#include "paravirt.h" // runningOnXen +#include "string.h" // memset +#include "util.h" // make_bios_writable +#include "x86.h" // wbinvd + +// On the emulators, the bios at 0xf0000 is also at 0xffff0000 +#define BIOS_SRC_OFFSET 0xfff00000 + +// Enable shadowing and copy bios. +static void +__make_bios_writable_intel(u16 bdf, u32 pam0) +{ +    // Make ram from 0xc0000-0xf0000 writable +    int clear = 0; +    int i; +    for (i=0; i<6; i++) { +        u32 pam = pam0 + 1 + i; +        int reg = pci_config_readb(bdf, pam); +        if (CONFIG_OPTIONROMS_DEPLOYED && (reg & 0x11) != 0x11) { +            // Need to copy optionroms to work around qemu implementation +            void *mem = (void*)(BUILD_ROM_START + i * 32*1024); +            memcpy((void*)BUILD_BIOS_TMP_ADDR, mem, 32*1024); +            pci_config_writeb(bdf, pam, 0x33); +            memcpy(mem, (void*)BUILD_BIOS_TMP_ADDR, 32*1024); +            clear = 1; +        } else { +            pci_config_writeb(bdf, pam, 0x33); +        } +    } +    if (clear) +        memset((void*)BUILD_BIOS_TMP_ADDR, 0, 32*1024); + +    // Make ram from 0xf0000-0x100000 writable +    int reg = pci_config_readb(bdf, pam0); +    pci_config_writeb(bdf, pam0, 0x30); +    if (reg & 0x10) +        // Ram already present. +        return; + +    // Copy bios. +    extern u8 code32flat_start[], code32flat_end[]; +    memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET +           , code32flat_end - code32flat_start); +} + +static void +make_bios_writable_intel(u16 bdf, u32 pam0) +{ +    int reg = pci_config_readb(bdf, pam0); +    if (!(reg & 0x10)) { +        // QEMU doesn't fully implement the piix shadow capabilities - +        // if ram isn't backing the bios segment when shadowing is +        // disabled, the code itself wont be in memory.  So, run the +        // code from the high-memory flash location. +        u32 pos = (u32)__make_bios_writable_intel + BIOS_SRC_OFFSET; +        void (*func)(u16 bdf, u32 pam0) = (void*)pos; +        func(bdf, pam0); +        return; +    } +    // Ram already present - just enable writes +    __make_bios_writable_intel(bdf, pam0); +} + +static void +make_bios_readonly_intel(u16 bdf, u32 pam0) +{ +    // Flush any pending writes before locking memory. +    wbinvd(); + +    // Write protect roms from 0xc0000-0xf0000 +    u32 romlast = BUILD_BIOS_ADDR, rommax = BUILD_BIOS_ADDR; +    if (CONFIG_WRITABLE_UPPERMEMORY) +        romlast = rom_get_last(); +    if (CONFIG_MALLOC_UPPERMEMORY) +        rommax = rom_get_max(); +    int i; +    for (i=0; i<6; i++) { +        u32 mem = BUILD_ROM_START + i * 32*1024; +        u32 pam = pam0 + 1 + i; +        if (romlast < mem + 16*1024 || rommax < mem + 32*1024) { +            if (romlast >= mem && rommax >= mem + 16*1024) +                pci_config_writeb(bdf, pam, 0x31); +            break; +        } +        pci_config_writeb(bdf, pam, 0x11); +    } + +    // Write protect 0xf0000-0x100000 +    pci_config_writeb(bdf, pam0, 0x10); +} + +static int ShadowBDF = -1; + +// Make the 0xc0000-0x100000 area read/writable. +void +make_bios_writable(void) +{ +    if (!CONFIG_QEMU || runningOnXen()) +        return; + +    dprintf(3, "enabling shadow ram\n"); + +    // At this point, statically allocated variables can't be written, +    // so do this search manually. +    int bdf; +    foreachbdf(bdf, 0) { +        u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID); +        u16 vendor = vendev & 0xffff, device = vendev >> 16; +        if (vendor == PCI_VENDOR_ID_INTEL +            && device == PCI_DEVICE_ID_INTEL_82441) { +            make_bios_writable_intel(bdf, I440FX_PAM0); +            ShadowBDF = bdf; +            return; +        } +        if (vendor == PCI_VENDOR_ID_INTEL +            && device == PCI_DEVICE_ID_INTEL_Q35_MCH) { +            make_bios_writable_intel(bdf, Q35_HOST_BRIDGE_PAM0); +            ShadowBDF = bdf; +            return; +        } +    } +    dprintf(1, "Unable to unlock ram - bridge not found\n"); +} + +// Make the BIOS code segment area (0xf0000) read-only. +void +make_bios_readonly(void) +{ +    if (!CONFIG_QEMU || runningOnXen()) +        return; +    dprintf(3, "locking shadow ram\n"); + +    if (ShadowBDF < 0) { +        dprintf(1, "Unable to lock ram - bridge not found\n"); +        return; +    } + +    u16 device = pci_config_readw(ShadowBDF, PCI_DEVICE_ID); +    if (device == PCI_DEVICE_ID_INTEL_82441) +        make_bios_readonly_intel(ShadowBDF, I440FX_PAM0); +    else +        make_bios_readonly_intel(ShadowBDF, Q35_HOST_BRIDGE_PAM0); +} + +void +qemu_prep_reset(void) +{ +    if (!CONFIG_QEMU || runningOnXen()) +        return; +    // QEMU doesn't map 0xc0000-0xfffff back to the original rom on a +    // reset, so do that manually before invoking a hard reset. +    make_bios_writable(); +    extern u8 code32flat_start[], code32flat_end[]; +    memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET +           , code32flat_end - code32flat_start); +} diff --git a/roms/seabios/src/fw/smbios.c b/roms/seabios/src/fw/smbios.c new file mode 100644 index 00000000..dba05413 --- /dev/null +++ b/roms/seabios/src/fw/smbios.c @@ -0,0 +1,585 @@ +// smbios table generation (on emulators) +// DO NOT ADD NEW FEATURES HERE.  (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "romfile.h" // romfile_findprefix +#include "std/smbios.h" // struct smbios_entry_point +#include "string.h" // memset +#include "util.h" // MaxCountCPUs +#include "x86.h" // cpuid + +static void +smbios_entry_point_setup(u16 max_structure_size, +                         u16 structure_table_length, +                         void *structure_table_address, +                         u16 number_of_structures) +{ +    void *finaltable; +    if (structure_table_length <= BUILD_MAX_SMBIOS_FSEG) +        // Table is small enough for f-seg - allocate there.  This +        // works around a bug in JunOS (at least for small SMBIOS tables). +        finaltable = malloc_fseg(structure_table_length); +    else +        finaltable = malloc_high(structure_table_length); +    if (!finaltable) { +        warn_noalloc(); +        return; +    } +    memcpy(finaltable, structure_table_address, structure_table_length); + +    struct smbios_entry_point ep; +    memset(&ep, 0, sizeof(ep)); +    memcpy(ep.anchor_string, "_SM_", 4); +    ep.length = 0x1f; +    ep.smbios_major_version = 2; +    ep.smbios_minor_version = 4; +    ep.max_structure_size = max_structure_size; +    memcpy(ep.intermediate_anchor_string, "_DMI_", 5); + +    ep.structure_table_length = structure_table_length; +    ep.structure_table_address = (u32)finaltable; +    ep.number_of_structures = number_of_structures; +    ep.smbios_bcd_revision = 0x24; + +    ep.checksum -= checksum(&ep, 0x10); + +    ep.intermediate_checksum -= checksum((void*)&ep + 0x10, ep.length - 0x10); + +    copy_smbios(&ep); +} + +static int +get_field(int type, int offset, void *dest) +{ +    char name[128]; +    snprintf(name, sizeof(name), "smbios/field%d-%d", type, offset); +    struct romfile_s *file = romfile_find(name); +    if (!file) +        return 0; +    file->copy(file, dest, file->size); +    return file->size; +} + +static int +get_external(int type, char **p, unsigned *nr_structs, +             unsigned *max_struct_size, char *end) +{ +    static u64 used_bitmap[4] = { 0 }; +    char *start = *p; + +    /* Check if we've already reported these tables */ +    if (used_bitmap[(type >> 6) & 0x3] & (1ULL << (type & 0x3f))) +        return 1; + +    /* Don't introduce spurious end markers */ +    if (type == 127) +        return 0; + +    char prefix[128]; +    snprintf(prefix, sizeof(prefix), "smbios/table%d-", type); +    struct romfile_s *file = NULL; +    for (;;) { +        file = romfile_findprefix(prefix, file); +        if (!file) +            break; + +        if (end - *p < file->size) { +            warn_noalloc(); +            break; +        } + +        struct smbios_structure_header *header = (void*)*p; +        file->copy(file, header, file->size); +        *p += file->size; + +        /* Entries end with a double NULL char, if there's a string at +         * the end (length is greater than formatted length), the string +         * terminator provides the first NULL. */ +        *((u8*)*p) = 0; +        (*p)++; +        if (header->length >= file->size) { +            *((u8*)*p) = 0; +            (*p)++; +        } + +        (*nr_structs)++; +        if (*p - (char*)header > *max_struct_size) +            *max_struct_size = *p - (char*)header; +    } + +    if (start == *p) +        return 0; + +    /* Mark that we've reported on this type */ +    used_bitmap[(type >> 6) & 0x3] |= (1ULL << (type & 0x3f)); +    return 1; +} + +#define load_str_field_with_default(type, field, def)                   \ +    do {                                                                \ +        size = get_field(type, offsetof(struct smbios_type_##type,      \ +                                        field), end);                   \ +        if (size == 1) {                                                \ +            /* zero-length string, skip to avoid bogus end marker */    \ +            p->field = 0;                                               \ +        } else if (size > 1) {                                          \ +            end += size;                                                \ +            p->field = ++str_index;                                     \ +        } else {                                                        \ +            memcpy(end, def, sizeof(def));                              \ +            end += sizeof(def);                                         \ +            p->field = ++str_index;                                     \ +        }                                                               \ +    } while (0) + +#define load_str_field_or_skip(type, field)                             \ +    do {                                                                \ +        size = get_field(type, offsetof(struct smbios_type_##type,      \ +                                        field), end);                   \ +        if (size > 1) {                                                 \ +            end += size;                                                \ +            p->field = ++str_index;                                     \ +        } else {                                                        \ +            p->field = 0;                                               \ +        }                                                               \ +    } while (0) + +#define set_field_with_default(type, field, def)                        \ +    do {                                                                \ +        if (!get_field(type, offsetof(struct smbios_type_##type,        \ +                                      field), &p->field)) {             \ +            p->field = def;                                             \ +        }                                                               \ +    } while (0) + +/* Type 0 -- BIOS Information */ +#define RELEASE_DATE_STR "01/01/2011" +static void * +smbios_init_type_0(void *start) +{ +    struct smbios_type_0 *p = (struct smbios_type_0 *)start; +    char *end = (char *)start + sizeof(struct smbios_type_0); +    size_t size; +    int str_index = 0; + +    p->header.type = 0; +    p->header.length = sizeof(struct smbios_type_0); +    p->header.handle = 0; + +    load_str_field_with_default(0, vendor_str, BUILD_APPNAME); +    load_str_field_with_default(0, bios_version_str, BUILD_APPNAME); + +    p->bios_starting_address_segment = 0xe800; + +    load_str_field_with_default(0, bios_release_date_str, RELEASE_DATE_STR); + +    p->bios_rom_size = 0; /* FIXME */ + +    if (!get_field(0, offsetof(struct smbios_type_0, bios_characteristics), +                   &p->bios_characteristics)) { +        memset(p->bios_characteristics, 0, 8); +        /* BIOS characteristics not supported */ +        p->bios_characteristics[0] = 0x08; +    } + +    if (!get_field(0, offsetof(struct smbios_type_0, +                               bios_characteristics_extension_bytes), +                   &p->bios_characteristics_extension_bytes)) { +        p->bios_characteristics_extension_bytes[0] = 0; +        /* Enable targeted content distribution. Needed for SVVP */ +        p->bios_characteristics_extension_bytes[1] = 4; +    } + +    set_field_with_default(0, system_bios_major_release, 1); +    set_field_with_default(0, system_bios_minor_release, 0); +    set_field_with_default(0, embedded_controller_major_release, 0xff); +    set_field_with_default(0, embedded_controller_minor_release, 0xff); + +    *end = 0; +    end++; + +    return end; +} + +/* Type 1 -- System Information */ +static void * +smbios_init_type_1(void *start) +{ +    struct smbios_type_1 *p = (struct smbios_type_1 *)start; +    char *end = (char *)start + sizeof(struct smbios_type_1); +    size_t size; +    int str_index = 0; + +    p->header.type = 1; +    p->header.length = sizeof(struct smbios_type_1); +    p->header.handle = 0x100; + +    load_str_field_with_default(1, manufacturer_str, BUILD_APPNAME); +    load_str_field_with_default(1, product_name_str, BUILD_APPNAME); +    load_str_field_or_skip(1, version_str); +    load_str_field_or_skip(1, serial_number_str); + +    if (!get_field(1, offsetof(struct smbios_type_1, uuid), &p->uuid)) +        memset(p->uuid, 0, 16); + +    set_field_with_default(1, wake_up_type, 0x06); /* power switch */ + +    load_str_field_or_skip(1, sku_number_str); +    load_str_field_or_skip(1, family_str); + +    *end = 0; +    end++; +    if (!str_index) { +        *end = 0; +        end++; +    } + +    return end; +} + +/* Type 3 -- System Enclosure */ +static void * +smbios_init_type_3(void *start) +{ +    struct smbios_type_3 *p = (struct smbios_type_3 *)start; +    char *end = (char *)start + sizeof(struct smbios_type_3); +    size_t size; +    int str_index = 0; + +    p->header.type = 3; +    p->header.length = sizeof(struct smbios_type_3); +    p->header.handle = 0x300; + +    load_str_field_with_default(3, manufacturer_str, BUILD_APPNAME); +    set_field_with_default(3, type, 0x01); /* other */ + +    load_str_field_or_skip(3, version_str); +    load_str_field_or_skip(3, serial_number_str); +    load_str_field_or_skip(3, asset_tag_number_str); + +    set_field_with_default(3, boot_up_state, 0x03); /* safe */ +    set_field_with_default(3, power_supply_state, 0x03); /* safe */ +    set_field_with_default(3, thermal_state, 0x03); /* safe */ +    set_field_with_default(3, security_status, 0x02); /* unknown */ + +    set_field_with_default(3, oem_defined, 0); +    set_field_with_default(3, height, 0); +    set_field_with_default(3, number_of_power_cords, 0); +    set_field_with_default(3, contained_element_count, 0); + +    *end = 0; +    end++; +    if (!str_index) { +        *end = 0; +        end++; +    } + +    return end; +} + +/* Type 4 -- Processor Information */ +static void * +smbios_init_type_4(void *start, unsigned int cpu_number) +{ +    struct smbios_type_4 *p = (struct smbios_type_4 *)start; +    char *end = (char *)start + sizeof(struct smbios_type_4); +    size_t size; +    int str_index = 0; +    char name[1024]; + +    p->header.type = 4; +    p->header.length = sizeof(struct smbios_type_4); +    p->header.handle = 0x400 + cpu_number; + +    size = get_field(4, offsetof(struct smbios_type_4, socket_designation_str), +                     name); +    if (size) +        snprintf(name + size - 1, sizeof(name) - size, "%2x", cpu_number); +    else +        snprintf(name, sizeof(name), "CPU%2x", cpu_number); + +    memcpy(end, name, strlen(name) + 1); +    end += strlen(name) + 1; +    p->socket_designation_str = ++str_index; + +    set_field_with_default(4, processor_type, 0x03); /* CPU */ +    set_field_with_default(4, processor_family, 0x01); /* other */ + +    load_str_field_with_default(4, processor_manufacturer_str, BUILD_APPNAME); + +    if (!get_field(4, offsetof(struct smbios_type_4, processor_id) +                   , p->processor_id)) { +        u32 cpuid_signature, ebx, ecx, cpuid_features; +        cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features); +        p->processor_id[0] = cpuid_signature; +        p->processor_id[1] = cpuid_features; +    } + +    load_str_field_or_skip(4, processor_version_str); +    set_field_with_default(4, voltage, 0); +    set_field_with_default(4, external_clock, 0); + +    set_field_with_default(4, max_speed, 2000); +    set_field_with_default(4, current_speed, 2000); + +    set_field_with_default(4, status, 0x41); /* socket populated, CPU enabled */ +    set_field_with_default(4, processor_upgrade, 0x01); /* other */ + +    /* cache information structure not provided */ +    p->l1_cache_handle =  0xffff; +    p->l2_cache_handle =  0xffff; +    p->l3_cache_handle =  0xffff; + +    *end = 0; +    end++; +    if (!str_index) { +        *end = 0; +        end++; +    } + +    return end; +} + +/* Type 16 -- Physical Memory Array */ +static void * +smbios_init_type_16(void *start, u32 memory_size_mb, int nr_mem_devs) +{ +    struct smbios_type_16 *p = (struct smbios_type_16*)start; + +    p->header.type = 16; +    p->header.length = sizeof(struct smbios_type_16); +    p->header.handle = 0x1000; + +    set_field_with_default(16, location, 0x01); /* other */ +    set_field_with_default(16, use, 0x03); /* system memory */ +    /* Multi-bit ECC to make Microsoft happy */ +    set_field_with_default(16, error_correction, 0x06); +    /* 0x80000000 = unknown, accept sizes < 2TB - TODO multiple arrays */ +    p->maximum_capacity = memory_size_mb < 2 << 20 ? +                          memory_size_mb << 10 : 0x80000000; +    p->memory_error_information_handle = 0xfffe; /* none provided */ +    p->number_of_memory_devices = nr_mem_devs; + +    start += sizeof(struct smbios_type_16); +    *((u16 *)start) = 0; + +    return start + 2; +} + +/* Type 17 -- Memory Device */ +static void * +smbios_init_type_17(void *start, u32 size_mb, int instance) +{ +    struct smbios_type_17 *p = (struct smbios_type_17 *)start; +    char *end = (char *)start + sizeof(struct smbios_type_17); +    size_t size; +    int str_index = 0; +    char name[1024]; + +    p->header.type = 17; +    p->header.length = sizeof(struct smbios_type_17); +    p->header.handle = 0x1100 + instance; + +    p->physical_memory_array_handle = 0x1000; +    set_field_with_default(17, total_width, 64); +    set_field_with_default(17, data_width, 64); +/* TODO: should assert in case something is wrong   ASSERT((memory_size_mb & ~0x7fff) == 0); */ +    p->size = size_mb; +    set_field_with_default(17, form_factor, 0x09); /* DIMM */ +    p->device_set = 0; + +    size = get_field(17, offsetof(struct smbios_type_17, device_locator_str), +                     name); +    if (size) +        snprintf(name + size - 1, sizeof(name) - size, "%d", instance); +    else +        snprintf(name, sizeof(name), "DIMM %d", instance); + +    memcpy(end, name, strlen(name) + 1); +    end += strlen(name) + 1; +    p->device_locator_str = ++str_index; + +    load_str_field_or_skip(17, bank_locator_str); +    set_field_with_default(17, memory_type, 0x07); /* RAM */ +    set_field_with_default(17, type_detail, 0); + +    *end = 0; +    end++; +    if (!str_index) { +        *end = 0; +        end++; +    } + +    return end; +} + +/* Type 19 -- Memory Array Mapped Address */ +static void * +smbios_init_type_19(void *start, u32 start_mb, u32 size_mb, int instance) +{ +    struct smbios_type_19 *p = (struct smbios_type_19 *)start; + +    p->header.type = 19; +    p->header.length = sizeof(struct smbios_type_19); +    p->header.handle = 0x1300 + instance; + +    p->starting_address = start_mb << 10; +    p->ending_address = p->starting_address + (size_mb << 10) - 1; +    p->memory_array_handle = 0x1000; +    p->partition_width = 1; + +    start += sizeof(struct smbios_type_19); +    *((u16 *)start) = 0; + +    return start + 2; +} + +/* Type 20 -- Memory Device Mapped Address */ +static void * +smbios_init_type_20(void *start, u32 start_mb, u32 size_mb, int instance, +                    int dev_handle, int array_handle) +{ +    struct smbios_type_20 *p = (struct smbios_type_20 *)start; + +    p->header.type = 20; +    p->header.length = sizeof(struct smbios_type_20); +    p->header.handle = 0x1400 + instance; + +    p->starting_address = start_mb << 10; +    p->ending_address = p->starting_address + (size_mb << 10) - 1; +    p->memory_device_handle = 0x1100 + dev_handle; +    p->memory_array_mapped_address_handle = 0x1300 + array_handle; +    p->partition_row_position = 1; +    p->interleave_position = 0; +    p->interleaved_data_depth = 0; + +    start += sizeof(struct smbios_type_20); + +    *((u16 *)start) = 0; +    return start+2; +} + +/* Type 32 -- System Boot Information */ +static void * +smbios_init_type_32(void *start) +{ +    struct smbios_type_32 *p = (struct smbios_type_32 *)start; + +    p->header.type = 32; +    p->header.length = sizeof(struct smbios_type_32); +    p->header.handle = 0x2000; +    memset(p->reserved, 0, 6); +    set_field_with_default(32, boot_status, 0); /* no errors detected */ + +    start += sizeof(struct smbios_type_32); +    *((u16 *)start) = 0; + +    return start+2; +} + +/* Type 127 -- End of Table */ +static void * +smbios_init_type_127(void *start) +{ +    struct smbios_type_127 *p = (struct smbios_type_127 *)start; + +    p->header.type = 127; +    p->header.length = sizeof(struct smbios_type_127); +    p->header.handle = 0x7f00; + +    start += sizeof(struct smbios_type_127); +    *((u16 *)start) = 0; + +    return start + 2; +} + +#define TEMPSMBIOSSIZE (32 * 1024) + +void +smbios_legacy_setup(void) +{ +    if (! CONFIG_SMBIOS) +        return; + +    dprintf(3, "init SMBIOS tables\n"); + +    char *start = malloc_tmphigh(TEMPSMBIOSSIZE); +    if (! start) { +        warn_noalloc(); +        return; +    } +    memset(start, 0, TEMPSMBIOSSIZE); + +    u32 nr_structs = 0, max_struct_size = 0; +    char *q, *p = start; +    char *end = start + TEMPSMBIOSSIZE - sizeof(struct smbios_type_127); + +#define add_struct(type, args...)                                       \ +    do {                                                                \ +        if (!get_external(type, &p, &nr_structs, &max_struct_size, end)) { \ +            q = smbios_init_type_##type(args);                          \ +            nr_structs++;                                               \ +            if ((q - p) > max_struct_size)                              \ +                max_struct_size = q - p;                                \ +            p = q;                                                      \ +        }                                                               \ +    } while (0) + +    add_struct(0, p); +    add_struct(1, p); +    add_struct(3, p); + +    int cpu_num; +    for (cpu_num = 1; cpu_num <= MaxCountCPUs; cpu_num++) +        add_struct(4, p, cpu_num); + +    int ram_mb = (RamSize + RamSizeOver4G) >> 20; +    int nr_mem_devs = (ram_mb + 0x3fff) >> 14; +    add_struct(16, p, ram_mb, nr_mem_devs); + +    int i, j; +    for (i = 0; i < nr_mem_devs; i++) { +        u32 dev_mb = ((i == (nr_mem_devs - 1)) +                      ? (((ram_mb - 1) & 0x3fff) + 1) +                      : 16384); +        add_struct(17, p, dev_mb, i); +    } + +    add_struct(19, p, 0, RamSize >> 20, 0); +    if (RamSizeOver4G) +        add_struct(19, p, 4096, RamSizeOver4G >> 20, 1); + +    add_struct(20, p, 0, RamSize >> 20, 0, 0, 0); +    if (RamSizeOver4G) { +        u32 start_mb = 4096; +        for (j = 1, i = 0; i < nr_mem_devs; i++, j++) { +            u32 dev_mb = ((i == (nr_mem_devs - 1)) +                               ? (((ram_mb - 1) & 0x3fff) + 1) +                               : 16384); +            if (i == 0) +                dev_mb -= RamSize >> 20; + +            add_struct(20, p, start_mb, dev_mb, j, i, 1); +            start_mb += dev_mb; +        } +    } + +    add_struct(32, p); +    /* Add any remaining provided entries before the end marker */ +    for (i = 0; i < 256; i++) +        get_external(i, &p, &nr_structs, &max_struct_size, end); +    add_struct(127, p); + +#undef add_struct + +    smbios_entry_point_setup(max_struct_size, p - start, start, nr_structs); +    free(start); +} diff --git a/roms/seabios/src/fw/smm.c b/roms/seabios/src/fw/smm.c new file mode 100644 index 00000000..6cb484e7 --- /dev/null +++ b/roms/seabios/src/fw/smm.c @@ -0,0 +1,259 @@ +// System Management Mode support (on emulators) +// +// Copyright (C) 2008-2014  Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "dev-q35.h" +#include "dev-piix.h" +#include "hw/pci.h" // pci_config_writel +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_DEVICE_ID +#include "output.h" // dprintf +#include "paravirt.h" // PORT_SMI_STATUS +#include "stacks.h" // HaveSmmCall32 +#include "string.h" // memcpy +#include "util.h" // smm_setup +#include "x86.h" // wbinvd + +/* + * Check SMM state save area format (bits 0-15) and require support + * for SMBASE relocation. + */ +#define SMM_REV_MASK 0x0002ffff + +#define SMM_REV_I32  0x00020000 +#define SMM_REV_I64  0x00020064 + +struct smm_state { +    union { +        struct { +            u8 pad_000[0xf8]; +            u32 smm_base; +            u32 smm_rev; +            u8 pad_100[0xd0]; +            u32 eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags; +            u8 pad_1f8[0x08]; +        } i32; +        struct { +            u8 pad_000[0xfc]; +            u32 smm_rev; +            u32 smm_base; +            u8 pad_104[0x6c]; +            u64 rflags, rip, r15, r14, r13, r12, r11, r10, r9, r8; +            u64 rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax; +        } i64; +    }; +}; + +struct smm_layout { +    struct smm_state backup1; +    struct smm_state backup2; +    u8 stack[0x7c00]; +    u64 codeentry; +    u8 pad_8008[0x7df8]; +    struct smm_state cpu; +}; + +void VISIBLE32FLAT +handle_smi(u16 cs) +{ +    if (!CONFIG_USE_SMM) +        return; +    u8 cmd = inb(PORT_SMI_CMD); +    struct smm_layout *smm = MAKE_FLATPTR(cs, 0); +    dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x smbase=%p\n", cmd, smm); + +    if (smm == (void*)BUILD_SMM_INIT_ADDR) { +        // relocate SMBASE to 0xa0000 +        u32 rev = smm->cpu.i32.smm_rev & SMM_REV_MASK; +        if (rev == SMM_REV_I32) { +            smm->cpu.i32.smm_base = BUILD_SMM_ADDR; +        } else if (rev == SMM_REV_I64) { +            smm->cpu.i64.smm_base = BUILD_SMM_ADDR; +        } else { +            warn_internalerror(); +            return; +        } +        // indicate to smm_relocate_and_restore() that the SMM code was executed +        outb(0x00, PORT_SMI_STATUS); + +        if (CONFIG_CALL32_SMM) { +            // Backup current cpu state for SMM trampolining +            struct smm_layout *newsmm = (void*)BUILD_SMM_ADDR; +            memcpy(&newsmm->backup1, &smm->cpu, sizeof(newsmm->backup1)); +            memcpy(&newsmm->backup2, &smm->cpu, sizeof(newsmm->backup2)); +            HaveSmmCall32 = 1; +        } + +        return; +    } + +    if (CONFIG_CALL32_SMM && cmd == CALL32SMM_CMDID) { +        if (smm->cpu.i32.smm_rev == SMM_REV_I32) { +            u32 regs[8]; +            memcpy(regs, &smm->cpu.i32.eax, sizeof(regs)); +            if (smm->cpu.i32.ecx == CALL32SMM_ENTERID) { +                dprintf(9, "smm cpu call pc=%x esp=%x\n", regs[3], regs[4]); +                memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2)); +                memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu)); +                memcpy(&smm->cpu.i32.eax, regs, sizeof(regs)); +                smm->cpu.i32.eip = regs[3]; +            } else if (smm->cpu.i32.ecx == CALL32SMM_RETURNID) { +                dprintf(9, "smm cpu ret %x esp=%x\n", regs[3], regs[4]); +                memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu)); +                memcpy(&smm->cpu.i32.eax, regs, sizeof(regs)); +                smm->cpu.i32.eip = regs[3]; +            } +        } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) { +            u64 regs[8]; +            memcpy(regs, &smm->cpu.i64.rdi, sizeof(regs)); +            if ((u32)smm->cpu.i64.rcx == CALL32SMM_ENTERID) { +                memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2)); +                memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu)); +                memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs)); +                smm->cpu.i64.rip = (u32)regs[4]; +            } else if ((u32)smm->cpu.i64.rcx == CALL32SMM_RETURNID) { +                memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu)); +                memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs)); +                smm->cpu.i64.rip = (u32)regs[4]; +            } +        } +    } +} + +extern void entry_smi(void); +// movw %cs, %ax; ljmpw $SEG_BIOS, $(entry_smi - BUILD_BIOS_ADDR) +#define SMI_INSN (0xeac88c | ((u64)SEG_BIOS<<40) \ +                  | ((u64)((u32)entry_smi - BUILD_BIOS_ADDR) << 24)) + +static void +smm_save_and_copy(void) +{ +    // save original memory content +    struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; +    struct smm_layout *smm = (void*)BUILD_SMM_ADDR; +    memcpy(&smm->cpu, &initsmm->cpu, sizeof(smm->cpu)); +    memcpy(&smm->codeentry, &initsmm->codeentry, sizeof(smm->codeentry)); + +    // Setup code entry point. +    initsmm->codeentry = SMI_INSN; +} + +static void +smm_relocate_and_restore(void) +{ +    /* init APM status port */ +    outb(0x01, PORT_SMI_STATUS); + +    /* raise an SMI interrupt */ +    outb(0x00, PORT_SMI_CMD); + +    /* wait until SMM code executed */ +    while (inb(PORT_SMI_STATUS) != 0x00) +        ; + +    /* restore original memory content */ +    struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; +    struct smm_layout *smm = (void*)BUILD_SMM_ADDR; +    memcpy(&initsmm->cpu, &smm->cpu, sizeof(initsmm->cpu)); +    memcpy(&initsmm->codeentry, &smm->codeentry, sizeof(initsmm->codeentry)); + +    // Setup code entry point. +    smm->codeentry = SMI_INSN; +    wbinvd(); +} + +// This code is hardcoded for PIIX4 Power Management device. +static void piix4_apmc_smm_setup(int isabdf, int i440_bdf) +{ +    /* check if SMM init is already done */ +    u32 value = pci_config_readl(isabdf, PIIX_DEVACTB); +    if (value & PIIX_DEVACTB_APMC_EN) +        return; + +    /* enable the SMM memory window */ +    pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x48); + +    smm_save_and_copy(); + +    /* enable SMI generation when writing to the APMC register */ +    pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_DEVACTB_APMC_EN); + +    /* enable SMI generation */ +    value = inl(acpi_pm_base + PIIX_PMIO_GLBCTL); +    outl(acpi_pm_base + PIIX_PMIO_GLBCTL, value | PIIX_PMIO_GLBCTL_SMI_EN); + +    smm_relocate_and_restore(); + +    /* close the SMM memory window and enable normal SMM */ +    pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x08); +} + +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +void ich9_lpc_apmc_smm_setup(int isabdf, int mch_bdf) +{ +    /* check if SMM init is already done */ +    u32 value = inl(acpi_pm_base + ICH9_PMIO_SMI_EN); +    if (value & ICH9_PMIO_SMI_EN_APMC_EN) +        return; + +    /* enable the SMM memory window */ +    pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x48); + +    smm_save_and_copy(); + +    /* enable SMI generation when writing to the APMC register */ +    outl(value | ICH9_PMIO_SMI_EN_APMC_EN | ICH9_PMIO_SMI_EN_GLB_SMI_EN, +         acpi_pm_base + ICH9_PMIO_SMI_EN); + +    /* lock SMI generation */ +    value = pci_config_readw(isabdf, ICH9_LPC_GEN_PMCON_1); +    pci_config_writel(isabdf, ICH9_LPC_GEN_PMCON_1, +                      value | ICH9_LPC_GEN_PMCON_1_SMI_LOCK); + +    smm_relocate_and_restore(); + +    /* close the SMM memory window and enable normal SMM */ +    pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x08); +} + +static int SMMISADeviceBDF = -1, SMMPMDeviceBDF = -1; + +void +smm_device_setup(void) +{ +    if (!CONFIG_USE_SMM) +        return; + +    struct pci_device *isapci, *pmpci; +    isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3); +    pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441); +    if (isapci && pmpci) { +        SMMISADeviceBDF = isapci->bdf; +        SMMPMDeviceBDF = pmpci->bdf; +        return; +    } +    isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC); +    pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH); +    if (isapci && pmpci) { +        SMMISADeviceBDF = isapci->bdf; +        SMMPMDeviceBDF = pmpci->bdf; +    } +} + +void +smm_setup(void) +{ +    if (!CONFIG_USE_SMM || SMMISADeviceBDF < 0) +        return; + +    dprintf(3, "init smm\n"); +    u16 device = pci_config_readw(SMMISADeviceBDF, PCI_DEVICE_ID); +    if (device == PCI_DEVICE_ID_INTEL_82371AB_3) +        piix4_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF); +    else +        ich9_lpc_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF); +} diff --git a/roms/seabios/src/fw/smp.c b/roms/seabios/src/fw/smp.c new file mode 100644 index 00000000..a466ea6e --- /dev/null +++ b/roms/seabios/src/fw/smp.c @@ -0,0 +1,153 @@ +// QEMU multi-CPU initialization code +// +// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "hw/rtc.h" // CMOS_BIOS_SMP_COUNT +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "stacks.h" // yield +#include "util.h" // smp_setup +#include "x86.h" // wrmsr + +#define APIC_ICR_LOW ((u8*)BUILD_APIC_ADDR + 0x300) +#define APIC_SVR     ((u8*)BUILD_APIC_ADDR + 0x0F0) +#define APIC_LINT0   ((u8*)BUILD_APIC_ADDR + 0x350) +#define APIC_LINT1   ((u8*)BUILD_APIC_ADDR + 0x360) + +#define APIC_ENABLED 0x0100 + +static struct { u32 index; u64 val; } smp_mtrr[32]; +static u32 smp_mtrr_count; + +void +wrmsr_smp(u32 index, u64 val) +{ +    wrmsr(index, val); +    if (smp_mtrr_count >= ARRAY_SIZE(smp_mtrr)) { +        warn_noalloc(); +        return; +    } +    smp_mtrr[smp_mtrr_count].index = index; +    smp_mtrr[smp_mtrr_count].val = val; +    smp_mtrr_count++; +} + +u32 MaxCountCPUs; +static u32 CountCPUs; +// 256 bits for the found APIC IDs +static u32 FoundAPICIDs[256/32]; + +int apic_id_is_present(u8 apic_id) +{ +    return !!(FoundAPICIDs[apic_id/32] & (1ul << (apic_id % 32))); +} + +void VISIBLE32FLAT +handle_smp(void) +{ +    if (!CONFIG_QEMU) +        return; + +    // Enable CPU caching +    setcr0(getcr0() & ~(CR0_CD|CR0_NW)); + +    // Detect apic_id +    u32 eax, ebx, ecx, cpuid_features; +    cpuid(1, &eax, &ebx, &ecx, &cpuid_features); +    u8 apic_id = ebx>>24; +    dprintf(DEBUG_HDL_smp, "handle_smp: apic_id=%d\n", apic_id); + +    // MTRR setup +    int i; +    for (i=0; i<smp_mtrr_count; i++) +        wrmsr(smp_mtrr[i].index, smp_mtrr[i].val); + +    // Set bit on FoundAPICIDs +    FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32)); + +    CountCPUs++; +} + +// Atomic lock for shared stack across processors. +u32 SMPLock __VISIBLE; +u32 SMPStack __VISIBLE; + +// find and initialize the CPUs by launching a SIPI to them +void +smp_setup(void) +{ +    if (!CONFIG_QEMU) +        return; + +    ASSERT32FLAT(); +    u32 eax, ebx, ecx, cpuid_features; +    cpuid(1, &eax, &ebx, &ecx, &cpuid_features); +    if (eax < 1 || !(cpuid_features & CPUID_APIC)) { +        // No apic - only the main cpu is present. +        dprintf(1, "No apic - only the main cpu is present.\n"); +        CountCPUs= 1; +        MaxCountCPUs = 1; +        return; +    } + +    // mark the BSP initial APIC ID as found, too: +    u8 apic_id = ebx>>24; +    FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32)); +    CountCPUs = 1; + +    // Setup jump trampoline to counter code. +    u64 old = *(u64*)BUILD_AP_BOOT_ADDR; +    // ljmpw $SEG_BIOS, $(entry_smp - BUILD_BIOS_ADDR) +    extern void entry_smp(void); +    u64 new = (0xea | ((u64)SEG_BIOS<<24) +               | (((u32)entry_smp - BUILD_BIOS_ADDR) << 8)); +    *(u64*)BUILD_AP_BOOT_ADDR = new; + +    // enable local APIC +    u32 val = readl(APIC_SVR); +    writel(APIC_SVR, val | APIC_ENABLED); + +    /* Set LINT0 as Ext_INT, level triggered */ +    writel(APIC_LINT0, 0x8700); + +    /* Set LINT1 as NMI, level triggered */ +    writel(APIC_LINT1, 0x8400); + +    // Init the lock. +    writel(&SMPLock, 1); + +    // broadcast SIPI +    barrier(); +    writel(APIC_ICR_LOW, 0x000C4500); +    u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12; +    writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector); + +    // Wait for other CPUs to process the SIPI. +    u8 cmos_smp_count = rtc_read(CMOS_BIOS_SMP_COUNT) + 1; +    while (cmos_smp_count != CountCPUs) +        asm volatile( +            // Release lock and allow other processors to use the stack. +            "  movl %%esp, %1\n" +            "  movl $0, %0\n" +            // Reacquire lock and take back ownership of stack. +            "1:rep ; nop\n" +            "  lock btsl $0, %0\n" +            "  jc 1b\n" +            : "+m" (SMPLock), "+m" (SMPStack) +            : : "cc", "memory"); +    yield(); + +    // Restore memory. +    *(u64*)BUILD_AP_BOOT_ADDR = old; + +    MaxCountCPUs = romfile_loadint("etc/max-cpus", 0); +    if (!MaxCountCPUs || MaxCountCPUs < CountCPUs) +        MaxCountCPUs = CountCPUs; + +    dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", CountCPUs, +            MaxCountCPUs); +} diff --git a/roms/seabios/src/fw/ssdt-misc.dsl b/roms/seabios/src/fw/ssdt-misc.dsl new file mode 100644 index 00000000..acc850e8 --- /dev/null +++ b/roms/seabios/src/fw/ssdt-misc.dsl @@ -0,0 +1,104 @@ +ACPI_EXTRACT_ALL_CODE ssdp_misc_aml + +DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1) +{ + +/**************************************************************** + * PCI memory ranges + ****************************************************************/ + +    Scope(\) { +       ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_start +       Name(P0S, 0x12345678) +       ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_end +       Name(P0E, 0x12345678) +       ACPI_EXTRACT_NAME_BYTE_CONST acpi_pci64_valid +       Name(P1V, 0x12) +       ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_start +       Name(P1S, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) +       ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_end +       Name(P1E, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) +       ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_length +       Name(P1L, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) +    } + + +/**************************************************************** + * Suspend + ****************************************************************/ + +    Scope(\) { +    /* +     * S3 (suspend-to-ram), S4 (suspend-to-disk) and S5 (power-off) type codes: +     * must match piix4 emulation. +     */ + +        ACPI_EXTRACT_NAME_STRING acpi_s3_name +        Name(_S3, Package(0x04) { +            One,  /* PM1a_CNT.SLP_TYP */ +            One,  /* PM1b_CNT.SLP_TYP */ +            Zero,  /* reserved */ +            Zero   /* reserved */ +        }) +        ACPI_EXTRACT_NAME_STRING acpi_s4_name +        ACPI_EXTRACT_PKG_START acpi_s4_pkg +        Name(_S4, Package(0x04) { +            0x2,  /* PM1a_CNT.SLP_TYP */ +            0x2,  /* PM1b_CNT.SLP_TYP */ +            Zero,  /* reserved */ +            Zero   /* reserved */ +        }) +        Name(_S5, Package(0x04) { +            Zero,  /* PM1a_CNT.SLP_TYP */ +            Zero,  /* PM1b_CNT.SLP_TYP */ +            Zero,  /* reserved */ +            Zero   /* reserved */ +        }) +    } + +    External(\_SB.PCI0, DeviceObj) +    External(\_SB.PCI0.ISA, DeviceObj) + +    Scope(\_SB.PCI0.ISA) { +        Device(PEVT) { +            Name(_HID, "QEMU0001") +            /* PEST will be patched to be Zero if no such device */ +            ACPI_EXTRACT_NAME_WORD_CONST ssdt_isa_pest +            Name(PEST, 0xFFFF) +            OperationRegion(PEOR, SystemIO, PEST, 0x01) +            Field(PEOR, ByteAcc, NoLock, Preserve) { +                PEPT,   8, +            } + +            Method(_STA, 0, NotSerialized) { +                Store(PEST, Local0) +                If (LEqual(Local0, Zero)) { +                    Return (0x00) +                } Else { +                    Return (0x0F) +                } +            } + +            Method(RDPT, 0, NotSerialized) { +                Store(PEPT, Local0) +                Return (Local0) +            } + +            Method(WRPT, 1, NotSerialized) { +                Store(Arg0, PEPT) +            } + +            Name(_CRS, ResourceTemplate() { +                IO(Decode16, 0x00, 0x00, 0x01, 0x01, IO) +            }) + +            CreateWordField(_CRS, IO._MIN, IOMN) +            CreateWordField(_CRS, IO._MAX, IOMX) + +            Method(_INI, 0, NotSerialized) { +                Store(PEST, IOMN) +                Store(PEST, IOMX) +            } +        } +    } +} diff --git a/roms/seabios/src/fw/ssdt-pcihp.dsl b/roms/seabios/src/fw/ssdt-pcihp.dsl new file mode 100644 index 00000000..cb24c11c --- /dev/null +++ b/roms/seabios/src/fw/ssdt-pcihp.dsl @@ -0,0 +1,36 @@ +ACPI_EXTRACT_ALL_CODE ssdp_pcihp_aml + +DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1) +{ + +/**************************************************************** + * PCI hotplug + ****************************************************************/ + +    /* Objects supplied by DSDT */ +    External(\_SB.PCI0, DeviceObj) +    External(\_SB.PCI0.PCEJ, MethodObj) + +    Scope(\_SB.PCI0) { + +        /* Bulk generated PCI hotplug devices */ +        ACPI_EXTRACT_DEVICE_START ssdt_pcihp_start +        ACPI_EXTRACT_DEVICE_END ssdt_pcihp_end +        ACPI_EXTRACT_DEVICE_STRING ssdt_pcihp_name + +        // Method _EJ0 can be patched by BIOS to EJ0_ +        // at runtime, if the slot is detected to not support hotplug. +        // Extract the offset of the address dword and the +        // _EJ0 name to allow this patching. +        Device(SAA) { +            ACPI_EXTRACT_NAME_BYTE_CONST ssdt_pcihp_id +            Name(_SUN, 0xAA) +            ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcihp_adr +            Name(_ADR, 0xAA0000) +            ACPI_EXTRACT_METHOD_STRING ssdt_pcihp_ej0 +            Method(_EJ0, 1) { +                PCEJ(_SUN) +            } +        } +    } +} diff --git a/roms/seabios/src/fw/ssdt-proc.dsl b/roms/seabios/src/fw/ssdt-proc.dsl new file mode 100644 index 00000000..407d61ea --- /dev/null +++ b/roms/seabios/src/fw/ssdt-proc.dsl @@ -0,0 +1,48 @@ +/* This file is the basis for the ssdt table generated in src/acpi.c. + * It defines the contents of the per-cpu Processor() object.  At + * runtime, a dynamically generated SSDT will contain one copy of this + * AML snippet for every possible cpu in the system.  The objects will + * be placed in the \_SB_ namespace. + * + * In addition to the aml code generated from this file, the + * src/acpi.c file creates a NTFY method with an entry for each cpu: + *     Method(NTFY, 2) { + *         If (LEqual(Arg0, 0x00)) { Notify(CP00, Arg1) } + *         If (LEqual(Arg0, 0x01)) { Notify(CP01, Arg1) } + *         ... + *     } + * and a CPON array with the list of active and inactive cpus: + *     Name(CPON, Package() { One, One, ..., Zero, Zero, ... }) + */ + +ACPI_EXTRACT_ALL_CODE ssdp_proc_aml + +DefinitionBlock ("ssdt-proc.aml", "SSDT", 0x01, "BXPC", "BXSSDT", 0x1) +{ +    ACPI_EXTRACT_PROCESSOR_START ssdt_proc_start +    ACPI_EXTRACT_PROCESSOR_END ssdt_proc_end +    ACPI_EXTRACT_PROCESSOR_STRING ssdt_proc_name +    Processor(CPAA, 0xAA, 0x0000b010, 0x06) { +        ACPI_EXTRACT_NAME_BYTE_CONST ssdt_proc_id +        Name(ID, 0xAA) +/* + * The src/acpi.c code requires the above ACP_EXTRACT tags so that it can update + * CPAA and 0xAA with the appropriate CPU id (see + * SD_OFFSET_CPUHEX/CPUID1/CPUID2).  Don't change the above without + * also updating the C code. + */ +        Name(_HID, "ACPI0007") +        External(CPMA, MethodObj) +        External(CPST, MethodObj) +        External(CPEJ, MethodObj) +        Method(_MAT, 0) { +            Return (CPMA(ID)) +        } +        Method(_STA, 0) { +            Return (CPST(ID)) +        } +        Method(_EJ0, 1, NotSerialized) { +            CPEJ(ID, Arg0) +        } +    } +} diff --git a/roms/seabios/src/fw/xen.c b/roms/seabios/src/fw/xen.c new file mode 100644 index 00000000..dd8e8afd --- /dev/null +++ b/roms/seabios/src/fw/xen.c @@ -0,0 +1,147 @@ +// Xen HVM support +// +// Copyright (C) 2011 Citrix Systems. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" +#include "hw/serialio.h" // DebugOutputPort +#include "malloc.h" // memalign_high +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // PlatformRunningOn +#include "string.h" // memcpy +#include "util.h" // copy_acpi_rsdp +#include "x86.h" // cpuid +#include "xen.h" + +#define INFO_PHYSICAL_ADDRESS 0x00001000 + +u32 xen_cpuid_base = 0; +unsigned long xen_hypercall_page = 0; + +struct xen_seabios_info { +    char signature[14]; /* XenHVMSeaBIOS\0 */ +    u8 length;     /* Length of this struct */ +    u8 checksum;   /* Set such that the sum over bytes 0..length == 0 */ +    /* +     * Physical address of an array of tables_nr elements. +     * +     * Each element is a 32 bit value contianing the physical address +     * of a BIOS table. +     */ +    u32 tables; +    u32 tables_nr; +    /* +     * Physical address of the e820 table, contains e820_nr entries. +     */ +    u32 e820; +    u32 e820_nr; +} PACKED; + +static void validate_info(struct xen_seabios_info *t) +{ +    if ( memcmp(t->signature, "XenHVMSeaBIOS", 14) ) +        panic("Bad Xen info signature\n"); + +    if ( t->length < sizeof(struct xen_seabios_info) ) +        panic("Bad Xen info length\n"); + +    if (checksum(t, t->length) != 0) +        panic("Bad Xen info checksum\n"); +} + +void xen_preinit(void) +{ +    u32 base, eax, ebx, ecx, edx; +    char signature[13]; + +    if (!CONFIG_XEN) +        return; + +    for (base = 0x40000000; base < 0x40010000; base += 0x100) { +        cpuid(base, &eax, &ebx, &ecx, &edx); +        memcpy(signature + 0, &ebx, 4); +        memcpy(signature + 4, &ecx, 4); +        memcpy(signature + 8, &edx, 4); +        signature[12] = 0; + +        dprintf(9, "Found hypervisor signature \"%s\" at %x\n", +                signature, base); +        if (strcmp(signature, "XenVMMXenVMM") == 0) { +            /* Set debug_io_port first, so the following messages work. */ +            DebugOutputPort = 0xe9; +            debug_banner(); +            dprintf(1, "\nFound Xen hypervisor signature at %x\n", base); +            if ((eax - base) < 2) +                panic("Insufficient Xen cpuid leaves. eax=%x at base %x\n", +                      eax, base); +            xen_cpuid_base = base; +            break; +        } +    } +    if (!xen_cpuid_base) { +        dprintf(1, "No Xen hypervisor found.\n"); +        return; +    } +    PlatformRunningOn = PF_QEMU|PF_XEN; +} + +static int hypercall_xen_version( int cmd, void *arg) +{ +    return _hypercall2(int, xen_version, cmd, arg); +} + +/* Fill in hypercall transfer pages. */ +void xen_hypercall_setup(void) +{ +    u32 eax, ebx, ecx, edx; +    xen_extraversion_t extraversion; +    unsigned long i; + +    if (!runningOnXen()) +        return; + +    cpuid(xen_cpuid_base + 2, &eax, &ebx, &ecx, &edx); + +    xen_hypercall_page = (unsigned long)memalign_high(PAGE_SIZE, eax*PAGE_SIZE); +    if (!xen_hypercall_page) +        panic("unable to allocate Xen hypercall page\n"); + +    dprintf(1, "Allocated Xen hypercall page at %lx\n", xen_hypercall_page); +    for ( i = 0; i < eax; i++ ) +        wrmsr(ebx, xen_hypercall_page + (i << 12) + i); + +    /* Print version information. */ +    cpuid(xen_cpuid_base + 1, &eax, &ebx, &ecx, &edx); +    hypercall_xen_version(XENVER_extraversion, extraversion); +    dprintf(1, "Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion); +} + +void xen_biostable_setup(void) +{ +    struct xen_seabios_info *info = (void *)INFO_PHYSICAL_ADDRESS; +    void **tables = (void*)info->tables; +    int i; + +    dprintf(1, "xen: copy BIOS tables...\n"); +    for (i=0; i<info->tables_nr; i++) +        copy_table(tables[i]); + +    find_acpi_features(); +} + +void xen_ramsize_preinit(void) +{ +    int i; +    struct xen_seabios_info *info = (void *)INFO_PHYSICAL_ADDRESS; +    struct e820entry *e820 = (struct e820entry *)info->e820; +    validate_info(info); + +    dprintf(1, "xen: copy e820...\n"); + +    for (i = 0; i < info->e820_nr; i++) { +        struct e820entry *e = &e820[i]; +        add_e820(e->start, e->size, e->type); +    } +} diff --git a/roms/seabios/src/fw/xen.h b/roms/seabios/src/fw/xen.h new file mode 100644 index 00000000..f00f8407 --- /dev/null +++ b/roms/seabios/src/fw/xen.h @@ -0,0 +1,125 @@ +#ifndef __XEN_H +#define __XEN_H + +void xen_preinit(void); +void xen_ramsize_preinit(void); +void xen_hypercall_setup(void); +void xen_biostable_setup(void); + +extern unsigned long xen_hypercall_page; + +#define _hypercall0(type, name)                                         \ +({                                                                      \ +    unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ +    long __res;                                                         \ +    asm volatile (                                                      \ +        "call *%%eax"                                                   \ +        : "=a" (__res)                                                  \ +        : "0" (__hentry)                                                \ +        : "memory" );                                                   \ +    (type)__res;                                                        \ +}) + +#define _hypercall1(type, name, a1)                                     \ +({                                                                      \ +    unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ +    long __res, __ign1;                                                 \ +    asm volatile (                                                      \ +        "call *%%eax"                                                   \ +        : "=a" (__res), "=b" (__ign1)                                   \ +        : "0" (__hentry), "1" ((long)(a1))                              \ +        : "memory" );                                                   \ +    (type)__res;                                                        \ +}) + +#define _hypercall2(type, name, a1, a2)                                 \ +({                                                                      \ +    unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ +    long __res, __ign1, __ign2;                                         \ +    asm volatile (                                                      \ +        "call *%%eax"                                                   \ +        : "=a" (__res), "=b" (__ign1), "=c" (__ign2)                    \ +        : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2))            \ +        : "memory" );                                                   \ +    (type)__res;                                                        \ +}) + +#define _hypercall3(type, name, a1, a2, a3)                             \ +({                                                                      \ +    unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ +    long __res, __ign1, __ign2, __ign3;                                 \ +    asm volatile (                                                      \ +        "call *%%eax"                                                   \ +        : "=a" (__res), "=b" (__ign1), "=c" (__ign2),                   \ +          "=d" (__ign3)                                                 \ +        : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)),           \ +          "3" ((long)(a3))                                              \ +        : "memory" );                                                   \ +    (type)__res;                                                        \ +}) + +#define _hypercall4(type, name, a1, a2, a3, a4)                         \ +({                                                                      \ +    unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ +    long __res, __ign1, __ign2, __ign3, __ign4;                         \ +    asm volatile (                                                      \ +        "call *%%eax"                                                   \ +        : "=a" (__res), "=b" (__ign1), "=c" (__ign2),                   \ +          "=d" (__ign3), "=S" (__ign4)                                  \ +        : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)),           \ +          "3" ((long)(a3)), "4" ((long)(a4))                            \ +        : "memory" );                                                   \ +    (type)__res;                                                        \ +}) + +#define _hypercall5(type, name, a1, a2, a3, a4, a5)                     \ +({                                                                      \ +    unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ +    long __res, __ign1, __ign2, __ign3, __ign4, __ign5;                 \ +    asm volatile (                                                      \ +        "call *%%eax"                                                   \ +        : "=a" (__res), "=b" (__ign1), "=c" (__ign2),                   \ +          "=d" (__ign3), "=S" (__ign4), "=D" (__ign5)                   \ +        : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)),           \ +          "3" ((long)(a3)), "4" ((long)(a4)),                           \ +          "5" ((long)(a5))                                              \ +        : "memory" );                                                   \ +    (type)__res;                                                        \ +}) + +/****************************************************************************** + * + * The following interface definitions are taken from Xen and have the + * following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* xen.h */ + +#define __HYPERVISOR_xen_version          17 + +/* version.h */ + +/* arg == xen_extraversion_t. */ +#define XENVER_extraversion 1 +typedef char xen_extraversion_t[16]; +#define XEN_EXTRAVERSION_LEN (sizeof(xen_extraversion_t)) + +#endif | 
