| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2010,2011 David Gibson, IBM Corporation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "qemu/osdep.h"
 | 
					
						
							|  |  |  | #include "cpu.h"
 | 
					
						
							|  |  |  | #include "hw/hw.h"
 | 
					
						
							|  |  |  | #include "trace.h"
 | 
					
						
							|  |  |  | #include "qemu/timer.h"
 | 
					
						
							|  |  |  | #include "hw/ppc/spapr.h"
 | 
					
						
							|  |  |  | #include "hw/ppc/xics.h"
 | 
					
						
							| 
									
										
										
										
											2016-10-20 16:07:56 +11:00
										 |  |  | #include "hw/ppc/fdt.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | #include "qapi/visitor.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Guest interfaces | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                            target_ulong opcode, target_ulong *args) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     target_ulong cppr = args[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-29 15:53:23 +02:00
										 |  |  |     icp_set_cppr(ICP(cpu->intc), cppr); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  |     return H_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                           target_ulong opcode, target_ulong *args) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     target_ulong mfrr = args[1]; | 
					
						
							| 
									
										
										
										
											2017-04-03 09:45:57 +02:00
										 |  |  |     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), args[0]); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-27 15:29:25 +01:00
										 |  |  |     if (!icp) { | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  |         return H_PARAMETER; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-27 15:29:25 +01:00
										 |  |  |     icp_set_mfrr(icp, mfrr); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  |     return H_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                            target_ulong opcode, target_ulong *args) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-03-29 15:53:23 +02:00
										 |  |  |     uint32_t xirr = icp_accept(ICP(cpu->intc)); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     args[0] = xirr; | 
					
						
							|  |  |  |     return H_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                              target_ulong opcode, target_ulong *args) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-03-29 15:53:23 +02:00
										 |  |  |     uint32_t xirr = icp_accept(ICP(cpu->intc)); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     args[0] = xirr; | 
					
						
							|  |  |  |     args[1] = cpu_get_host_ticks(); | 
					
						
							|  |  |  |     return H_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                           target_ulong opcode, target_ulong *args) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     target_ulong xirr = args[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-29 15:53:23 +02:00
										 |  |  |     icp_eoi(ICP(cpu->intc), xirr); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  |     return H_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                             target_ulong opcode, target_ulong *args) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:14 +05:30
										 |  |  |     uint32_t mfrr; | 
					
						
							| 
									
										
										
										
											2017-03-29 15:53:23 +02:00
										 |  |  |     uint32_t xirr = icp_ipoll(ICP(cpu->intc), &mfrr); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:14 +05:30
										 |  |  |     args[0] = xirr; | 
					
						
							|  |  |  |     args[1] = mfrr; | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     return H_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                           uint32_t token, | 
					
						
							|  |  |  |                           uint32_t nargs, target_ulong args, | 
					
						
							|  |  |  |                           uint32_t nret, target_ulong rets) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-02-27 15:29:12 +01:00
										 |  |  |     ICSState *ics = spapr->ics; | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:47 +02:00
										 |  |  |     uint32_t nr, srcno, server, priority; | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((nargs != 3) || (nret != 1)) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:46 +02:00
										 |  |  |     if (!ics) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_HW_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     nr = rtas_ld(args, 0); | 
					
						
							| 
									
										
										
										
											2017-04-03 09:45:57 +02:00
										 |  |  |     server = rtas_ld(args, 1); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  |     priority = rtas_ld(args, 2); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-27 15:29:25 +01:00
										 |  |  |     if (!ics_valid_irq(ics, nr) || !xics_icp_get(XICS_FABRIC(spapr), server) | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  |         || (priority > 0xff)) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:47 +02:00
										 |  |  |     srcno = nr - ics->offset; | 
					
						
							|  |  |  |     ics_simple_write_xive(ics, srcno, server, priority, priority); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     rtas_st(rets, 0, RTAS_OUT_SUCCESS); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                           uint32_t token, | 
					
						
							|  |  |  |                           uint32_t nargs, target_ulong args, | 
					
						
							|  |  |  |                           uint32_t nret, target_ulong rets) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-02-27 15:29:12 +01:00
										 |  |  |     ICSState *ics = spapr->ics; | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:47 +02:00
										 |  |  |     uint32_t nr, srcno; | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((nargs != 1) || (nret != 3)) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:46 +02:00
										 |  |  |     if (!ics) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_HW_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     nr = rtas_ld(args, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!ics_valid_irq(ics, nr)) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rtas_st(rets, 0, RTAS_OUT_SUCCESS); | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:47 +02:00
										 |  |  |     srcno = nr - ics->offset; | 
					
						
							|  |  |  |     rtas_st(rets, 1, ics->irqs[srcno].server); | 
					
						
							|  |  |  |     rtas_st(rets, 2, ics->irqs[srcno].priority); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                          uint32_t token, | 
					
						
							|  |  |  |                          uint32_t nargs, target_ulong args, | 
					
						
							|  |  |  |                          uint32_t nret, target_ulong rets) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-02-27 15:29:12 +01:00
										 |  |  |     ICSState *ics = spapr->ics; | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:47 +02:00
										 |  |  |     uint32_t nr, srcno; | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((nargs != 1) || (nret != 1)) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:46 +02:00
										 |  |  |     if (!ics) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_HW_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     nr = rtas_ld(args, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!ics_valid_irq(ics, nr)) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:47 +02:00
										 |  |  |     srcno = nr - ics->offset; | 
					
						
							|  |  |  |     ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server, 0xff, | 
					
						
							|  |  |  |                           ics->irqs[srcno].priority); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     rtas_st(rets, 0, RTAS_OUT_SUCCESS); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, | 
					
						
							|  |  |  |                         uint32_t token, | 
					
						
							|  |  |  |                         uint32_t nargs, target_ulong args, | 
					
						
							|  |  |  |                         uint32_t nret, target_ulong rets) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-02-27 15:29:12 +01:00
										 |  |  |     ICSState *ics = spapr->ics; | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:47 +02:00
										 |  |  |     uint32_t nr, srcno; | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((nargs != 1) || (nret != 1)) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:46 +02:00
										 |  |  |     if (!ics) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_HW_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     nr = rtas_ld(args, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!ics_valid_irq(ics, nr)) { | 
					
						
							|  |  |  |         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-03 09:24:47 +02:00
										 |  |  |     srcno = nr - ics->offset; | 
					
						
							|  |  |  |     ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server, | 
					
						
							|  |  |  |                           ics->irqs[srcno].saved_priority, | 
					
						
							|  |  |  |                           ics->irqs[srcno].saved_priority); | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     rtas_st(rets, 0, RTAS_OUT_SUCCESS); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-15 13:39:16 +02:00
										 |  |  | void xics_spapr_init(sPAPRMachineState *spapr) | 
					
						
							| 
									
										
										
										
											2016-06-29 00:35:13 +05:30
										 |  |  | { | 
					
						
							|  |  |  |     /* Registration of global state belongs into realize */ | 
					
						
							|  |  |  |     spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive); | 
					
						
							|  |  |  |     spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive); | 
					
						
							|  |  |  |     spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off); | 
					
						
							|  |  |  |     spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     spapr_register_hypercall(H_CPPR, h_cppr); | 
					
						
							|  |  |  |     spapr_register_hypercall(H_IPI, h_ipi); | 
					
						
							|  |  |  |     spapr_register_hypercall(H_XIRR, h_xirr); | 
					
						
							|  |  |  |     spapr_register_hypercall(H_XIRR_X, h_xirr_x); | 
					
						
							|  |  |  |     spapr_register_hypercall(H_EOI, h_eoi); | 
					
						
							|  |  |  |     spapr_register_hypercall(H_IPOLL, h_ipoll); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-27 15:29:26 +01:00
										 |  |  | void spapr_dt_xics(int nr_servers, void *fdt, uint32_t phandle) | 
					
						
							| 
									
										
										
										
											2016-10-20 16:07:56 +11:00
										 |  |  | { | 
					
						
							|  |  |  |     uint32_t interrupt_server_ranges_prop[] = { | 
					
						
							| 
									
										
										
										
											2017-02-27 15:29:26 +01:00
										 |  |  |         0, cpu_to_be32(nr_servers), | 
					
						
							| 
									
										
										
										
											2016-10-20 16:07:56 +11:00
										 |  |  |     }; | 
					
						
							|  |  |  |     int node; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _FDT(fdt_setprop_string(fdt, node, "device_type", | 
					
						
							|  |  |  |                             "PowerPC-External-Interrupt-Presentation")); | 
					
						
							|  |  |  |     _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,ppc-xicp")); | 
					
						
							|  |  |  |     _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0)); | 
					
						
							|  |  |  |     _FDT(fdt_setprop(fdt, node, "ibm,interrupt-server-ranges", | 
					
						
							|  |  |  |                      interrupt_server_ranges_prop, | 
					
						
							|  |  |  |                      sizeof(interrupt_server_ranges_prop))); | 
					
						
							|  |  |  |     _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2)); | 
					
						
							|  |  |  |     _FDT(fdt_setprop_cell(fdt, node, "linux,phandle", phandle)); | 
					
						
							|  |  |  |     _FDT(fdt_setprop_cell(fdt, node, "phandle", phandle)); | 
					
						
							|  |  |  | } |