forked from Minki/linux
5d375199ea
When a guest has a PCI pass-through device with an interrupt, it will direct the interrupt to a particular guest VCPU. In fact the physical interrupt might arrive on any CPU, and then get delivered to the target VCPU in the emulated XICS (guest interrupt controller), and eventually delivered to the target VCPU. Now that we have code to handle device interrupts in real mode without exiting to the host kernel, there is an advantage to having the device interrupt arrive on the same sub(core) as the target VCPU is running on. In this situation, the interrupt can be delivered to the target VCPU without any exit to the host kernel (using a hypervisor doorbell interrupt between threads if necessary). This patch aims to get passed-through device interrupts arriving on the correct core by setting the interrupt server in the real hardware XICS for the interrupt to the first thread in the (sub)core where its target VCPU is running. We do this in the real-mode H_EOI code because the H_EOI handler already needs to look at the emulated ICS state for the interrupt (whereas the H_XIRR handler doesn't), and we know we are running in the target VCPU context at that point. We set the server CPU in hardware using an OPAL call, regardless of what the IRQ affinity mask for the interrupt says, and without updating the affinity mask. This amounts to saying that when an interrupt is passed through to a guest, as a matter of policy we allow the guest's affinity for the interrupt to override the host's. This is inspired by an earlier patch from Suresh Warrier, although none of this code came from that earlier patch. Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
312 lines
10 KiB
ArmAsm
312 lines
10 KiB
ArmAsm
/*
|
|
* PowerNV OPAL API wrappers
|
|
*
|
|
* Copyright 2011 IBM Corp.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/jump_label.h>
|
|
#include <asm/ppc_asm.h>
|
|
#include <asm/hvcall.h>
|
|
#include <asm/asm-offsets.h>
|
|
#include <asm/opal.h>
|
|
|
|
.section ".text"
|
|
|
|
#ifdef CONFIG_TRACEPOINTS
|
|
#ifdef HAVE_JUMP_LABEL
|
|
#define OPAL_BRANCH(LABEL) \
|
|
ARCH_STATIC_BRANCH(LABEL, opal_tracepoint_key)
|
|
#else
|
|
|
|
.section ".toc","aw"
|
|
|
|
.globl opal_tracepoint_refcount
|
|
opal_tracepoint_refcount:
|
|
.llong 0
|
|
|
|
.section ".text"
|
|
|
|
/*
|
|
* We branch around this in early init by using an unconditional cpu
|
|
* feature.
|
|
*/
|
|
#define OPAL_BRANCH(LABEL) \
|
|
BEGIN_FTR_SECTION; \
|
|
b 1f; \
|
|
END_FTR_SECTION(0, 1); \
|
|
ld r12,opal_tracepoint_refcount@toc(r2); \
|
|
cmpdi r12,0; \
|
|
bne- LABEL; \
|
|
1:
|
|
|
|
#endif
|
|
|
|
#else
|
|
#define OPAL_BRANCH(LABEL)
|
|
#endif
|
|
|
|
/* TODO:
|
|
*
|
|
* - Trace irqs in/off (needs saving/restoring all args, argh...)
|
|
* - Get r11 feed up by Dave so I can have better register usage
|
|
*/
|
|
|
|
#define OPAL_CALL(name, token) \
|
|
_GLOBAL_TOC(name); \
|
|
mflr r0; \
|
|
std r0,PPC_LR_STKOFF(r1); \
|
|
li r0,token; \
|
|
OPAL_BRANCH(opal_tracepoint_entry) \
|
|
mfcr r12; \
|
|
stw r12,8(r1); \
|
|
li r11,0; \
|
|
mfmsr r12; \
|
|
ori r11,r11,MSR_EE; \
|
|
std r12,PACASAVEDMSR(r13); \
|
|
andc r12,r12,r11; \
|
|
mtmsrd r12,1; \
|
|
LOAD_REG_ADDR(r11,opal_return); \
|
|
mtlr r11; \
|
|
li r11,MSR_DR|MSR_IR|MSR_LE;\
|
|
andc r12,r12,r11; \
|
|
mtspr SPRN_HSRR1,r12; \
|
|
LOAD_REG_ADDR(r11,opal); \
|
|
ld r12,8(r11); \
|
|
ld r2,0(r11); \
|
|
mtspr SPRN_HSRR0,r12; \
|
|
hrfid
|
|
|
|
opal_return:
|
|
/*
|
|
* Fixup endian on OPAL return... we should be able to simplify
|
|
* this by instead converting the below trampoline to a set of
|
|
* bytes (always BE) since MSR:LE will end up fixed up as a side
|
|
* effect of the rfid.
|
|
*/
|
|
FIXUP_ENDIAN
|
|
ld r2,PACATOC(r13);
|
|
lwz r4,8(r1);
|
|
ld r5,PPC_LR_STKOFF(r1);
|
|
ld r6,PACASAVEDMSR(r13);
|
|
mtspr SPRN_SRR0,r5;
|
|
mtspr SPRN_SRR1,r6;
|
|
mtcr r4;
|
|
rfid
|
|
|
|
#ifdef CONFIG_TRACEPOINTS
|
|
opal_tracepoint_entry:
|
|
stdu r1,-STACKFRAMESIZE(r1)
|
|
std r0,STK_REG(R23)(r1)
|
|
std r3,STK_REG(R24)(r1)
|
|
std r4,STK_REG(R25)(r1)
|
|
std r5,STK_REG(R26)(r1)
|
|
std r6,STK_REG(R27)(r1)
|
|
std r7,STK_REG(R28)(r1)
|
|
std r8,STK_REG(R29)(r1)
|
|
std r9,STK_REG(R30)(r1)
|
|
std r10,STK_REG(R31)(r1)
|
|
mr r3,r0
|
|
addi r4,r1,STK_REG(R24)
|
|
bl __trace_opal_entry
|
|
ld r0,STK_REG(R23)(r1)
|
|
ld r3,STK_REG(R24)(r1)
|
|
ld r4,STK_REG(R25)(r1)
|
|
ld r5,STK_REG(R26)(r1)
|
|
ld r6,STK_REG(R27)(r1)
|
|
ld r7,STK_REG(R28)(r1)
|
|
ld r8,STK_REG(R29)(r1)
|
|
ld r9,STK_REG(R30)(r1)
|
|
ld r10,STK_REG(R31)(r1)
|
|
LOAD_REG_ADDR(r11,opal_tracepoint_return)
|
|
mfcr r12
|
|
std r11,16(r1)
|
|
stw r12,8(r1)
|
|
li r11,0
|
|
mfmsr r12
|
|
ori r11,r11,MSR_EE
|
|
std r12,PACASAVEDMSR(r13)
|
|
andc r12,r12,r11
|
|
mtmsrd r12,1
|
|
LOAD_REG_ADDR(r11,opal_return)
|
|
mtlr r11
|
|
li r11,MSR_DR|MSR_IR|MSR_LE
|
|
andc r12,r12,r11
|
|
mtspr SPRN_HSRR1,r12
|
|
LOAD_REG_ADDR(r11,opal)
|
|
ld r12,8(r11)
|
|
ld r2,0(r11)
|
|
mtspr SPRN_HSRR0,r12
|
|
hrfid
|
|
|
|
opal_tracepoint_return:
|
|
std r3,STK_REG(R31)(r1)
|
|
mr r4,r3
|
|
ld r0,STK_REG(R23)(r1)
|
|
bl __trace_opal_exit
|
|
ld r3,STK_REG(R31)(r1)
|
|
addi r1,r1,STACKFRAMESIZE
|
|
ld r0,16(r1)
|
|
mtlr r0
|
|
blr
|
|
#endif
|
|
|
|
#define OPAL_CALL_REAL(name, token) \
|
|
_GLOBAL_TOC(name); \
|
|
mflr r0; \
|
|
std r0,PPC_LR_STKOFF(r1); \
|
|
li r0,token; \
|
|
mfcr r12; \
|
|
stw r12,8(r1); \
|
|
\
|
|
/* Set opal return address */ \
|
|
LOAD_REG_ADDR(r11, opal_return_realmode); \
|
|
mtlr r11; \
|
|
mfmsr r12; \
|
|
li r11,MSR_LE; \
|
|
andc r12,r12,r11; \
|
|
mtspr SPRN_HSRR1,r12; \
|
|
LOAD_REG_ADDR(r11,opal); \
|
|
ld r12,8(r11); \
|
|
ld r2,0(r11); \
|
|
mtspr SPRN_HSRR0,r12; \
|
|
hrfid
|
|
|
|
opal_return_realmode:
|
|
FIXUP_ENDIAN
|
|
ld r2,PACATOC(r13);
|
|
lwz r11,8(r1);
|
|
ld r12,PPC_LR_STKOFF(r1)
|
|
mtcr r11;
|
|
mtlr r12
|
|
blr
|
|
|
|
|
|
OPAL_CALL(opal_invalid_call, OPAL_INVALID_CALL);
|
|
OPAL_CALL(opal_console_write, OPAL_CONSOLE_WRITE);
|
|
OPAL_CALL(opal_console_read, OPAL_CONSOLE_READ);
|
|
OPAL_CALL(opal_console_write_buffer_space, OPAL_CONSOLE_WRITE_BUFFER_SPACE);
|
|
OPAL_CALL(opal_rtc_read, OPAL_RTC_READ);
|
|
OPAL_CALL(opal_rtc_write, OPAL_RTC_WRITE);
|
|
OPAL_CALL(opal_cec_power_down, OPAL_CEC_POWER_DOWN);
|
|
OPAL_CALL(opal_cec_reboot, OPAL_CEC_REBOOT);
|
|
OPAL_CALL(opal_cec_reboot2, OPAL_CEC_REBOOT2);
|
|
OPAL_CALL(opal_read_nvram, OPAL_READ_NVRAM);
|
|
OPAL_CALL(opal_write_nvram, OPAL_WRITE_NVRAM);
|
|
OPAL_CALL(opal_handle_interrupt, OPAL_HANDLE_INTERRUPT);
|
|
OPAL_CALL(opal_poll_events, OPAL_POLL_EVENTS);
|
|
OPAL_CALL(opal_pci_set_hub_tce_memory, OPAL_PCI_SET_HUB_TCE_MEMORY);
|
|
OPAL_CALL(opal_pci_set_phb_tce_memory, OPAL_PCI_SET_PHB_TCE_MEMORY);
|
|
OPAL_CALL(opal_pci_config_read_byte, OPAL_PCI_CONFIG_READ_BYTE);
|
|
OPAL_CALL(opal_pci_config_read_half_word, OPAL_PCI_CONFIG_READ_HALF_WORD);
|
|
OPAL_CALL(opal_pci_config_read_word, OPAL_PCI_CONFIG_READ_WORD);
|
|
OPAL_CALL(opal_pci_config_write_byte, OPAL_PCI_CONFIG_WRITE_BYTE);
|
|
OPAL_CALL(opal_pci_config_write_half_word, OPAL_PCI_CONFIG_WRITE_HALF_WORD);
|
|
OPAL_CALL(opal_pci_config_write_word, OPAL_PCI_CONFIG_WRITE_WORD);
|
|
OPAL_CALL(opal_set_xive, OPAL_SET_XIVE);
|
|
OPAL_CALL_REAL(opal_rm_set_xive, OPAL_SET_XIVE);
|
|
OPAL_CALL(opal_get_xive, OPAL_GET_XIVE);
|
|
OPAL_CALL(opal_register_exception_handler, OPAL_REGISTER_OPAL_EXCEPTION_HANDLER);
|
|
OPAL_CALL(opal_pci_eeh_freeze_status, OPAL_PCI_EEH_FREEZE_STATUS);
|
|
OPAL_CALL(opal_pci_eeh_freeze_clear, OPAL_PCI_EEH_FREEZE_CLEAR);
|
|
OPAL_CALL(opal_pci_eeh_freeze_set, OPAL_PCI_EEH_FREEZE_SET);
|
|
OPAL_CALL(opal_pci_err_inject, OPAL_PCI_ERR_INJECT);
|
|
OPAL_CALL(opal_pci_shpc, OPAL_PCI_SHPC);
|
|
OPAL_CALL(opal_pci_phb_mmio_enable, OPAL_PCI_PHB_MMIO_ENABLE);
|
|
OPAL_CALL(opal_pci_set_phb_mem_window, OPAL_PCI_SET_PHB_MEM_WINDOW);
|
|
OPAL_CALL(opal_pci_map_pe_mmio_window, OPAL_PCI_MAP_PE_MMIO_WINDOW);
|
|
OPAL_CALL(opal_pci_set_phb_table_memory, OPAL_PCI_SET_PHB_TABLE_MEMORY);
|
|
OPAL_CALL(opal_pci_set_pe, OPAL_PCI_SET_PE);
|
|
OPAL_CALL(opal_pci_set_peltv, OPAL_PCI_SET_PELTV);
|
|
OPAL_CALL(opal_pci_set_mve, OPAL_PCI_SET_MVE);
|
|
OPAL_CALL(opal_pci_set_mve_enable, OPAL_PCI_SET_MVE_ENABLE);
|
|
OPAL_CALL(opal_pci_get_xive_reissue, OPAL_PCI_GET_XIVE_REISSUE);
|
|
OPAL_CALL(opal_pci_set_xive_reissue, OPAL_PCI_SET_XIVE_REISSUE);
|
|
OPAL_CALL(opal_pci_set_xive_pe, OPAL_PCI_SET_XIVE_PE);
|
|
OPAL_CALL(opal_get_xive_source, OPAL_GET_XIVE_SOURCE);
|
|
OPAL_CALL(opal_get_msi_32, OPAL_GET_MSI_32);
|
|
OPAL_CALL(opal_get_msi_64, OPAL_GET_MSI_64);
|
|
OPAL_CALL(opal_start_cpu, OPAL_START_CPU);
|
|
OPAL_CALL(opal_query_cpu_status, OPAL_QUERY_CPU_STATUS);
|
|
OPAL_CALL(opal_write_oppanel, OPAL_WRITE_OPPANEL);
|
|
OPAL_CALL(opal_pci_map_pe_dma_window, OPAL_PCI_MAP_PE_DMA_WINDOW);
|
|
OPAL_CALL(opal_pci_map_pe_dma_window_real, OPAL_PCI_MAP_PE_DMA_WINDOW_REAL);
|
|
OPAL_CALL(opal_pci_reset, OPAL_PCI_RESET);
|
|
OPAL_CALL(opal_pci_get_hub_diag_data, OPAL_PCI_GET_HUB_DIAG_DATA);
|
|
OPAL_CALL(opal_pci_get_phb_diag_data, OPAL_PCI_GET_PHB_DIAG_DATA);
|
|
OPAL_CALL(opal_pci_fence_phb, OPAL_PCI_FENCE_PHB);
|
|
OPAL_CALL(opal_pci_reinit, OPAL_PCI_REINIT);
|
|
OPAL_CALL(opal_pci_mask_pe_error, OPAL_PCI_MASK_PE_ERROR);
|
|
OPAL_CALL(opal_set_slot_led_status, OPAL_SET_SLOT_LED_STATUS);
|
|
OPAL_CALL(opal_get_epow_status, OPAL_GET_EPOW_STATUS);
|
|
OPAL_CALL(opal_get_dpo_status, OPAL_GET_DPO_STATUS);
|
|
OPAL_CALL(opal_set_system_attention_led, OPAL_SET_SYSTEM_ATTENTION_LED);
|
|
OPAL_CALL(opal_pci_next_error, OPAL_PCI_NEXT_ERROR);
|
|
OPAL_CALL(opal_pci_poll, OPAL_PCI_POLL);
|
|
OPAL_CALL(opal_pci_msi_eoi, OPAL_PCI_MSI_EOI);
|
|
OPAL_CALL(opal_pci_get_phb_diag_data2, OPAL_PCI_GET_PHB_DIAG_DATA2);
|
|
OPAL_CALL(opal_xscom_read, OPAL_XSCOM_READ);
|
|
OPAL_CALL(opal_xscom_write, OPAL_XSCOM_WRITE);
|
|
OPAL_CALL(opal_lpc_read, OPAL_LPC_READ);
|
|
OPAL_CALL(opal_lpc_write, OPAL_LPC_WRITE);
|
|
OPAL_CALL(opal_return_cpu, OPAL_RETURN_CPU);
|
|
OPAL_CALL(opal_reinit_cpus, OPAL_REINIT_CPUS);
|
|
OPAL_CALL(opal_read_elog, OPAL_ELOG_READ);
|
|
OPAL_CALL(opal_send_ack_elog, OPAL_ELOG_ACK);
|
|
OPAL_CALL(opal_get_elog_size, OPAL_ELOG_SIZE);
|
|
OPAL_CALL(opal_resend_pending_logs, OPAL_ELOG_RESEND);
|
|
OPAL_CALL(opal_write_elog, OPAL_ELOG_WRITE);
|
|
OPAL_CALL(opal_validate_flash, OPAL_FLASH_VALIDATE);
|
|
OPAL_CALL(opal_manage_flash, OPAL_FLASH_MANAGE);
|
|
OPAL_CALL(opal_update_flash, OPAL_FLASH_UPDATE);
|
|
OPAL_CALL(opal_resync_timebase, OPAL_RESYNC_TIMEBASE);
|
|
OPAL_CALL_REAL(opal_rm_resync_timebase, OPAL_RESYNC_TIMEBASE);
|
|
OPAL_CALL(opal_check_token, OPAL_CHECK_TOKEN);
|
|
OPAL_CALL(opal_dump_init, OPAL_DUMP_INIT);
|
|
OPAL_CALL(opal_dump_info, OPAL_DUMP_INFO);
|
|
OPAL_CALL(opal_dump_info2, OPAL_DUMP_INFO2);
|
|
OPAL_CALL(opal_dump_read, OPAL_DUMP_READ);
|
|
OPAL_CALL(opal_dump_ack, OPAL_DUMP_ACK);
|
|
OPAL_CALL(opal_get_msg, OPAL_GET_MSG);
|
|
OPAL_CALL(opal_write_oppanel_async, OPAL_WRITE_OPPANEL_ASYNC);
|
|
OPAL_CALL(opal_check_completion, OPAL_CHECK_ASYNC_COMPLETION);
|
|
OPAL_CALL(opal_dump_resend_notification, OPAL_DUMP_RESEND);
|
|
OPAL_CALL(opal_sync_host_reboot, OPAL_SYNC_HOST_REBOOT);
|
|
OPAL_CALL(opal_sensor_read, OPAL_SENSOR_READ);
|
|
OPAL_CALL(opal_get_param, OPAL_GET_PARAM);
|
|
OPAL_CALL(opal_set_param, OPAL_SET_PARAM);
|
|
OPAL_CALL(opal_handle_hmi, OPAL_HANDLE_HMI);
|
|
OPAL_CALL_REAL(opal_rm_handle_hmi, OPAL_HANDLE_HMI);
|
|
OPAL_CALL(opal_config_cpu_idle_state, OPAL_CONFIG_CPU_IDLE_STATE);
|
|
OPAL_CALL_REAL(opal_rm_config_cpu_idle_state, OPAL_CONFIG_CPU_IDLE_STATE);
|
|
OPAL_CALL(opal_slw_set_reg, OPAL_SLW_SET_REG);
|
|
OPAL_CALL(opal_register_dump_region, OPAL_REGISTER_DUMP_REGION);
|
|
OPAL_CALL(opal_unregister_dump_region, OPAL_UNREGISTER_DUMP_REGION);
|
|
OPAL_CALL(opal_pci_set_phb_cxl_mode, OPAL_PCI_SET_PHB_CAPI_MODE);
|
|
OPAL_CALL(opal_tpo_write, OPAL_WRITE_TPO);
|
|
OPAL_CALL(opal_tpo_read, OPAL_READ_TPO);
|
|
OPAL_CALL(opal_ipmi_send, OPAL_IPMI_SEND);
|
|
OPAL_CALL(opal_ipmi_recv, OPAL_IPMI_RECV);
|
|
OPAL_CALL(opal_i2c_request, OPAL_I2C_REQUEST);
|
|
OPAL_CALL(opal_flash_read, OPAL_FLASH_READ);
|
|
OPAL_CALL(opal_flash_write, OPAL_FLASH_WRITE);
|
|
OPAL_CALL(opal_flash_erase, OPAL_FLASH_ERASE);
|
|
OPAL_CALL(opal_prd_msg, OPAL_PRD_MSG);
|
|
OPAL_CALL(opal_leds_get_ind, OPAL_LEDS_GET_INDICATOR);
|
|
OPAL_CALL(opal_leds_set_ind, OPAL_LEDS_SET_INDICATOR);
|
|
OPAL_CALL(opal_console_flush, OPAL_CONSOLE_FLUSH);
|
|
OPAL_CALL(opal_get_device_tree, OPAL_GET_DEVICE_TREE);
|
|
OPAL_CALL(opal_pci_get_presence_state, OPAL_PCI_GET_PRESENCE_STATE);
|
|
OPAL_CALL(opal_pci_get_power_state, OPAL_PCI_GET_POWER_STATE);
|
|
OPAL_CALL(opal_pci_set_power_state, OPAL_PCI_SET_POWER_STATE);
|
|
OPAL_CALL(opal_int_get_xirr, OPAL_INT_GET_XIRR);
|
|
OPAL_CALL(opal_int_set_cppr, OPAL_INT_SET_CPPR);
|
|
OPAL_CALL(opal_int_eoi, OPAL_INT_EOI);
|
|
OPAL_CALL(opal_int_set_mfrr, OPAL_INT_SET_MFRR);
|
|
OPAL_CALL(opal_pci_tce_kill, OPAL_PCI_TCE_KILL);
|
|
OPAL_CALL_REAL(opal_rm_pci_tce_kill, OPAL_PCI_TCE_KILL);
|