diff options
-rw-r--r-- | arch/x86/include/asm/io_apic.h | 4 | ||||
-rw-r--r-- | arch/x86/kernel/apic/io_apic.c | 87 |
2 files changed, 58 insertions, 33 deletions
diff --git a/arch/x86/include/asm/io_apic.h b/arch/x86/include/asm/io_apic.h index 0b31aebd9405..94d05bd6586f 100644 --- a/arch/x86/include/asm/io_apic.h +++ b/arch/x86/include/asm/io_apic.h | |||
@@ -188,8 +188,8 @@ extern int mp_find_ioapic_pin(int ioapic, u32 gsi); | |||
188 | extern u32 mp_pin_to_gsi(int ioapic, int pin); | 188 | extern u32 mp_pin_to_gsi(int ioapic, int pin); |
189 | extern int mp_map_gsi_to_irq(u32 gsi, unsigned int flags); | 189 | extern int mp_map_gsi_to_irq(u32 gsi, unsigned int flags); |
190 | extern void mp_unmap_irq(int irq); | 190 | extern void mp_unmap_irq(int irq); |
191 | extern void mp_register_ioapic(int id, u32 address, u32 gsi_base, | 191 | extern int mp_register_ioapic(int id, u32 address, u32 gsi_base, |
192 | struct ioapic_domain_cfg *cfg); | 192 | struct ioapic_domain_cfg *cfg); |
193 | extern int mp_irqdomain_map(struct irq_domain *domain, unsigned int virq, | 193 | extern int mp_irqdomain_map(struct irq_domain *domain, unsigned int virq, |
194 | irq_hw_number_t hwirq); | 194 | irq_hw_number_t hwirq); |
195 | extern void mp_irqdomain_unmap(struct irq_domain *domain, unsigned int virq); | 195 | extern void mp_irqdomain_unmap(struct irq_domain *domain, unsigned int virq); |
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 60f25e88734b..4333a751937d 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c | |||
@@ -3840,20 +3840,6 @@ int mp_find_ioapic_pin(int ioapic, u32 gsi) | |||
3840 | return gsi - gsi_cfg->gsi_base; | 3840 | return gsi - gsi_cfg->gsi_base; |
3841 | } | 3841 | } |
3842 | 3842 | ||
3843 | static int bad_ioapic(unsigned long address) | ||
3844 | { | ||
3845 | if (nr_ioapics >= MAX_IO_APICS) { | ||
3846 | pr_warn("WARNING: Max # of I/O APICs (%d) exceeded (found %d), skipping\n", | ||
3847 | MAX_IO_APICS, nr_ioapics); | ||
3848 | return 1; | ||
3849 | } | ||
3850 | if (!address) { | ||
3851 | pr_warn("WARNING: Bogus (zero) I/O APIC address found in table, skipping!\n"); | ||
3852 | return 1; | ||
3853 | } | ||
3854 | return 0; | ||
3855 | } | ||
3856 | |||
3857 | static int bad_ioapic_register(int idx) | 3843 | static int bad_ioapic_register(int idx) |
3858 | { | 3844 | { |
3859 | union IO_APIC_reg_00 reg_00; | 3845 | union IO_APIC_reg_00 reg_00; |
@@ -3873,29 +3859,51 @@ static int bad_ioapic_register(int idx) | |||
3873 | return 0; | 3859 | return 0; |
3874 | } | 3860 | } |
3875 | 3861 | ||
3876 | void mp_register_ioapic(int id, u32 address, u32 gsi_base, | 3862 | static int find_free_ioapic_entry(void) |
3877 | struct ioapic_domain_cfg *cfg) | 3863 | { |
3864 | return nr_ioapics; | ||
3865 | } | ||
3866 | |||
3867 | /** | ||
3868 | * mp_register_ioapic - Register an IOAPIC device | ||
3869 | * @id: hardware IOAPIC ID | ||
3870 | * @address: physical address of IOAPIC register area | ||
3871 | * @gsi_base: base of GSI associated with the IOAPIC | ||
3872 | * @cfg: configuration information for the IOAPIC | ||
3873 | */ | ||
3874 | int mp_register_ioapic(int id, u32 address, u32 gsi_base, | ||
3875 | struct ioapic_domain_cfg *cfg) | ||
3878 | { | 3876 | { |
3879 | int idx = 0; | ||
3880 | int entries; | ||
3881 | struct mp_ioapic_gsi *gsi_cfg; | 3877 | struct mp_ioapic_gsi *gsi_cfg; |
3878 | int idx, ioapic, entries; | ||
3879 | u32 gsi_end; | ||
3882 | 3880 | ||
3883 | if (bad_ioapic(address)) | 3881 | if (!address) { |
3884 | return; | 3882 | pr_warn("Bogus (zero) I/O APIC address found, skipping!\n"); |
3883 | return -EINVAL; | ||
3884 | } | ||
3885 | for_each_ioapic(ioapic) | ||
3886 | if (ioapics[ioapic].mp_config.apicaddr == address) { | ||
3887 | pr_warn("address 0x%x conflicts with IOAPIC%d\n", | ||
3888 | address, ioapic); | ||
3889 | return -EEXIST; | ||
3890 | } | ||
3885 | 3891 | ||
3886 | idx = nr_ioapics; | 3892 | idx = find_free_ioapic_entry(); |
3893 | if (idx >= MAX_IO_APICS) { | ||
3894 | pr_warn("Max # of I/O APICs (%d) exceeded (found %d), skipping\n", | ||
3895 | MAX_IO_APICS, idx); | ||
3896 | return -ENOSPC; | ||
3897 | } | ||
3887 | 3898 | ||
3888 | ioapics[idx].mp_config.type = MP_IOAPIC; | 3899 | ioapics[idx].mp_config.type = MP_IOAPIC; |
3889 | ioapics[idx].mp_config.flags = MPC_APIC_USABLE; | 3900 | ioapics[idx].mp_config.flags = MPC_APIC_USABLE; |
3890 | ioapics[idx].mp_config.apicaddr = address; | 3901 | ioapics[idx].mp_config.apicaddr = address; |
3891 | ioapics[idx].irqdomain = NULL; | ||
3892 | ioapics[idx].irqdomain_cfg = *cfg; | ||
3893 | 3902 | ||
3894 | set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address); | 3903 | set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address); |
3895 | |||
3896 | if (bad_ioapic_register(idx)) { | 3904 | if (bad_ioapic_register(idx)) { |
3897 | clear_fixmap(FIX_IO_APIC_BASE_0 + idx); | 3905 | clear_fixmap(FIX_IO_APIC_BASE_0 + idx); |
3898 | return; | 3906 | return -ENODEV; |
3899 | } | 3907 | } |
3900 | 3908 | ||
3901 | ioapics[idx].mp_config.apicid = io_apic_unique_id(idx, id); | 3909 | ioapics[idx].mp_config.apicid = io_apic_unique_id(idx, id); |
@@ -3906,24 +3914,41 @@ void mp_register_ioapic(int id, u32 address, u32 gsi_base, | |||
3906 | * and to prevent reprogramming of IOAPIC pins (PCI GSIs). | 3914 | * and to prevent reprogramming of IOAPIC pins (PCI GSIs). |
3907 | */ | 3915 | */ |
3908 | entries = io_apic_get_redir_entries(idx); | 3916 | entries = io_apic_get_redir_entries(idx); |
3917 | gsi_end = gsi_base + entries - 1; | ||
3918 | for_each_ioapic(ioapic) { | ||
3919 | gsi_cfg = mp_ioapic_gsi_routing(ioapic); | ||
3920 | if ((gsi_base >= gsi_cfg->gsi_base && | ||
3921 | gsi_base <= gsi_cfg->gsi_end) || | ||
3922 | (gsi_end >= gsi_cfg->gsi_base && | ||
3923 | gsi_end <= gsi_cfg->gsi_end)) { | ||
3924 | pr_warn("GSI range [%u-%u] for new IOAPIC conflicts with GSI[%u-%u]\n", | ||
3925 | gsi_base, gsi_end, | ||
3926 | gsi_cfg->gsi_base, gsi_cfg->gsi_end); | ||
3927 | clear_fixmap(FIX_IO_APIC_BASE_0 + idx); | ||
3928 | return -ENOSPC; | ||
3929 | } | ||
3930 | } | ||
3909 | gsi_cfg = mp_ioapic_gsi_routing(idx); | 3931 | gsi_cfg = mp_ioapic_gsi_routing(idx); |
3910 | gsi_cfg->gsi_base = gsi_base; | 3932 | gsi_cfg->gsi_base = gsi_base; |
3911 | gsi_cfg->gsi_end = gsi_base + entries - 1; | 3933 | gsi_cfg->gsi_end = gsi_end; |
3912 | 3934 | ||
3913 | /* | 3935 | ioapics[idx].irqdomain = NULL; |
3914 | * The number of IO-APIC IRQ registers (== #pins): | 3936 | ioapics[idx].irqdomain_cfg = *cfg; |
3915 | */ | ||
3916 | ioapics[idx].nr_registers = entries; | ||
3917 | 3937 | ||
3918 | if (gsi_cfg->gsi_end >= gsi_top) | 3938 | if (gsi_cfg->gsi_end >= gsi_top) |
3919 | gsi_top = gsi_cfg->gsi_end + 1; | 3939 | gsi_top = gsi_cfg->gsi_end + 1; |
3940 | if (nr_ioapics <= idx) | ||
3941 | nr_ioapics = idx + 1; | ||
3942 | |||
3943 | /* Set nr_registers to mark entry present */ | ||
3944 | ioapics[idx].nr_registers = entries; | ||
3920 | 3945 | ||
3921 | pr_info("IOAPIC[%d]: apic_id %d, version %d, address 0x%x, GSI %d-%d\n", | 3946 | pr_info("IOAPIC[%d]: apic_id %d, version %d, address 0x%x, GSI %d-%d\n", |
3922 | idx, mpc_ioapic_id(idx), | 3947 | idx, mpc_ioapic_id(idx), |
3923 | mpc_ioapic_ver(idx), mpc_ioapic_addr(idx), | 3948 | mpc_ioapic_ver(idx), mpc_ioapic_addr(idx), |
3924 | gsi_cfg->gsi_base, gsi_cfg->gsi_end); | 3949 | gsi_cfg->gsi_base, gsi_cfg->gsi_end); |
3925 | 3950 | ||
3926 | nr_ioapics++; | 3951 | return 0; |
3927 | } | 3952 | } |
3928 | 3953 | ||
3929 | int mp_irqdomain_map(struct irq_domain *domain, unsigned int virq, | 3954 | int mp_irqdomain_map(struct irq_domain *domain, unsigned int virq, |