xref: /illumos-gate/usr/src/uts/sun4v/os/intrq.c (revision 27c48ed935c6a5f6015c6534b98e3090b1ddfdb6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/machsystm.h>
30 #include <sys/cpu.h>
31 #include <sys/intreg.h>
32 #include <sys/machcpuvar.h>
33 #include <vm/hat_sfmmu.h>
34 #include <sys/error.h>
35 #include <sys/hypervisor_api.h>
36 
37 void
38 cpu_intrq_register(struct cpu *cpu)
39 {
40 	struct machcpu *mcpup = &cpu->cpu_m;
41 	uint64_t ret;
42 
43 	ret = hv_cpu_qconf(INTR_CPU_Q, mcpup->cpu_q_base_pa, cpu_q_entries);
44 	if (ret != H_EOK)
45 		cmn_err(CE_PANIC, "cpu%d: cpu_mondo queue configuration "
46 		    "failed, error %lu", cpu->cpu_id, ret);
47 
48 	ret = hv_cpu_qconf(INTR_DEV_Q, mcpup->dev_q_base_pa, dev_q_entries);
49 	if (ret != H_EOK)
50 		cmn_err(CE_PANIC, "cpu%d: dev_mondo queue configuration "
51 		    "failed, error %lu", cpu->cpu_id, ret);
52 
53 	ret = hv_cpu_qconf(CPU_RQ, mcpup->cpu_rq_base_pa, cpu_rq_entries);
54 	if (ret != H_EOK)
55 		cmn_err(CE_PANIC, "cpu%d: resumable error queue configuration "
56 		    "failed, error %lu", cpu->cpu_id, ret);
57 
58 	ret = hv_cpu_qconf(CPU_NRQ, mcpup->cpu_nrq_base_pa, cpu_nrq_entries);
59 	if (ret != H_EOK)
60 		cmn_err(CE_PANIC, "cpu%d: non-resumable error queue "
61 			"configuration failed, error %lu", cpu->cpu_id, ret);
62 }
63 
64 int
65 cpu_intrq_setup(struct cpu *cpu)
66 {
67 	struct machcpu *mcpup = &cpu->cpu_m;
68 	size_t size;
69 
70 	/*
71 	 * This routine will return with an error return if any
72 	 * contig_mem_alloc() fails.  It is expected that the caller will
73 	 * call cpu_intrq_cleanup() (or cleanup_cpu_common() which will).
74 	 * That will cleanly free only those blocks that were alloc'd.
75 	 */
76 
77 	/*
78 	 * Allocate mondo data for xcalls.
79 	 */
80 	mcpup->mondo_data = contig_mem_alloc(INTR_REPORT_SIZE);
81 
82 	if (mcpup->mondo_data == NULL) {
83 		cmn_err(CE_NOTE, "cpu%d: cpu mondo_data allocation failed",
84 			cpu->cpu_id);
85 		return (ENOMEM);
86 	}
87 	/*
88 	 * va_to_pa() is too expensive to call for every crosscall
89 	 * so we do it here at init time and save it in machcpu.
90 	 */
91 	mcpup->mondo_data_ra = va_to_pa(mcpup->mondo_data);
92 
93 	/*
94 	 *  Allocate a percpu list of NCPU for xcalls
95 	 */
96 	size = NCPU * sizeof (uint16_t);
97 	if (size < INTR_REPORT_SIZE)
98 		size = INTR_REPORT_SIZE;
99 
100 	mcpup->cpu_list = contig_mem_alloc(size);
101 
102 	if (mcpup->cpu_list == NULL) {
103 		cmn_err(CE_NOTE, "cpu%d: cpu cpu_list allocation failed",
104 			cpu->cpu_id);
105 		return (ENOMEM);
106 	}
107 	mcpup->cpu_list_ra = va_to_pa(mcpup->cpu_list);
108 
109 	/*
110 	 * Allocate sun4v interrupt and error queues.
111 	 */
112 	size = cpu_q_entries * INTR_REPORT_SIZE;
113 
114 	mcpup->cpu_q_va = contig_mem_alloc(size);
115 
116 	if (mcpup->cpu_q_va == NULL) {
117 		cmn_err(CE_NOTE, "cpu%d: cpu intrq allocation failed",
118 			cpu->cpu_id);
119 		return (ENOMEM);
120 	}
121 	mcpup->cpu_q_base_pa = va_to_pa(mcpup->cpu_q_va);
122 	mcpup->cpu_q_size = size;
123 
124 	/*
125 	 * Allocate device queues
126 	 */
127 	size = dev_q_entries * INTR_REPORT_SIZE;
128 
129 	mcpup->dev_q_va = contig_mem_alloc(size);
130 
131 	if (mcpup->dev_q_va == NULL) {
132 		cmn_err(CE_NOTE, "cpu%d: dev intrq allocation failed",
133 			cpu->cpu_id);
134 		return (ENOMEM);
135 	}
136 	mcpup->dev_q_base_pa = va_to_pa(mcpup->dev_q_va);
137 	mcpup->dev_q_size = size;
138 
139 	/*
140 	 * Allocate resumable queue and its kernel buffer
141 	 */
142 	size = cpu_rq_entries * Q_ENTRY_SIZE;
143 
144 	mcpup->cpu_rq_va = contig_mem_alloc(2 * size);
145 
146 	if (mcpup->cpu_rq_va == NULL) {
147 		cmn_err(CE_NOTE, "cpu%d: resumable queue allocation failed",
148 			cpu->cpu_id);
149 		return (ENOMEM);
150 	}
151 	mcpup->cpu_rq_base_pa = va_to_pa(mcpup->cpu_rq_va);
152 	mcpup->cpu_rq_size = size;
153 	/* zero out the memory */
154 	bzero(mcpup->cpu_rq_va, 2 * size);
155 
156 	/*
157 	 * Allocate non-resumable queues
158 	 */
159 	size = cpu_nrq_entries * Q_ENTRY_SIZE;
160 
161 	mcpup->cpu_nrq_va = contig_mem_alloc(2 * size);
162 
163 	if (mcpup->cpu_nrq_va == NULL) {
164 		cmn_err(CE_NOTE, "cpu%d: nonresumable queue allocation failed",
165 			cpu->cpu_id);
166 		return (ENOMEM);
167 	}
168 	mcpup->cpu_nrq_base_pa = va_to_pa(mcpup->cpu_nrq_va);
169 	mcpup->cpu_nrq_size = size;
170 	/* zero out the memory */
171 	bzero(mcpup->cpu_nrq_va, 2 * size);
172 
173 	return (0);
174 }
175 
176 void
177 cpu_intrq_cleanup(struct cpu *cpu)
178 {
179 	struct machcpu *mcpup = &cpu->cpu_m;
180 	int cpu_list_size;
181 	uint64_t cpu_q_size;
182 	uint64_t dev_q_size;
183 	uint64_t cpu_rq_size;
184 	uint64_t cpu_nrq_size;
185 
186 	/*
187 	 * Free mondo data for xcalls.
188 	 */
189 	if (mcpup->mondo_data) {
190 		contig_mem_free(mcpup->mondo_data, INTR_REPORT_SIZE);
191 		mcpup->mondo_data = NULL;
192 		mcpup->mondo_data_ra = NULL;
193 	}
194 
195 	/*
196 	 *  Free percpu list of NCPU for xcalls
197 	 */
198 	cpu_list_size = NCPU * sizeof (uint16_t);
199 	if (cpu_list_size < INTR_REPORT_SIZE)
200 		cpu_list_size = INTR_REPORT_SIZE;
201 
202 	if (mcpup->cpu_list) {
203 		contig_mem_free(mcpup->cpu_list, cpu_list_size);
204 		mcpup->cpu_list = NULL;
205 		mcpup->cpu_list_ra = NULL;
206 	}
207 
208 	/*
209 	 * Free sun4v interrupt and error queues.
210 	 */
211 	if (mcpup->cpu_q_va) {
212 		cpu_q_size = cpu_q_entries * INTR_REPORT_SIZE;
213 		contig_mem_free(mcpup->cpu_q_va, cpu_q_size);
214 		mcpup->cpu_q_va = NULL;
215 		mcpup->cpu_q_base_pa = NULL;
216 		mcpup->cpu_q_size = 0;
217 	}
218 
219 	if (mcpup->dev_q_va) {
220 		dev_q_size = dev_q_entries * INTR_REPORT_SIZE;
221 		contig_mem_free(mcpup->dev_q_va, dev_q_size);
222 		mcpup->dev_q_va = NULL;
223 		mcpup->dev_q_base_pa = NULL;
224 		mcpup->dev_q_size = 0;
225 	}
226 
227 	if (mcpup->cpu_rq_va) {
228 		cpu_rq_size = cpu_rq_entries * Q_ENTRY_SIZE;
229 		contig_mem_free(mcpup->cpu_rq_va, 2 * cpu_rq_size);
230 		mcpup->cpu_rq_va = NULL;
231 		mcpup->cpu_rq_base_pa = NULL;
232 		mcpup->cpu_rq_size = 0;
233 	}
234 
235 	if (mcpup->cpu_nrq_va) {
236 		cpu_nrq_size = cpu_nrq_entries * Q_ENTRY_SIZE;
237 		contig_mem_free(mcpup->cpu_nrq_va, 2 * cpu_nrq_size);
238 		mcpup->cpu_nrq_va = NULL;
239 		mcpup->cpu_nrq_base_pa = NULL;
240 		mcpup->cpu_nrq_size = 0;
241 	}
242 }
243