xref: /illumos-gate/usr/src/uts/sun4v/os/intrq.c (revision 12551037)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/sysmacros.h>
27 #include <sys/machsystm.h>
28 #include <sys/cpu.h>
29 #include <sys/intreg.h>
30 #include <sys/machcpuvar.h>
31 #include <vm/hat_sfmmu.h>
32 #include <sys/error.h>
33 #include <sys/hypervisor_api.h>
34 
35 void
cpu_intrq_register(struct cpu * cpu)36 cpu_intrq_register(struct cpu *cpu)
37 {
38 	struct machcpu *mcpup = &cpu->cpu_m;
39 	uint64_t ret;
40 
41 	ret = hv_cpu_qconf(INTR_CPU_Q, mcpup->cpu_q_base_pa, cpu_q_entries);
42 	if (ret != H_EOK)
43 		cmn_err(CE_PANIC, "cpu%d: cpu_mondo queue configuration "
44 		    "failed, error %lu", cpu->cpu_id, ret);
45 
46 	ret = hv_cpu_qconf(INTR_DEV_Q, mcpup->dev_q_base_pa, dev_q_entries);
47 	if (ret != H_EOK)
48 		cmn_err(CE_PANIC, "cpu%d: dev_mondo queue configuration "
49 		    "failed, error %lu", cpu->cpu_id, ret);
50 
51 	ret = hv_cpu_qconf(CPU_RQ, mcpup->cpu_rq_base_pa, cpu_rq_entries);
52 	if (ret != H_EOK)
53 		cmn_err(CE_PANIC, "cpu%d: resumable error queue configuration "
54 		    "failed, error %lu", cpu->cpu_id, ret);
55 
56 	ret = hv_cpu_qconf(CPU_NRQ, mcpup->cpu_nrq_base_pa, cpu_nrq_entries);
57 	if (ret != H_EOK)
58 		cmn_err(CE_PANIC, "cpu%d: non-resumable error queue "
59 		    "configuration failed, error %lu", cpu->cpu_id, ret);
60 }
61 
62 int
cpu_intrq_setup(struct cpu * cpu)63 cpu_intrq_setup(struct cpu *cpu)
64 {
65 	struct machcpu *mcpup = &cpu->cpu_m;
66 	size_t size;
67 
68 	/*
69 	 * This routine will return with an error return if any
70 	 * contig_mem_alloc() fails.  It is expected that the caller will
71 	 * call cpu_intrq_cleanup() (or cleanup_cpu_common() which will).
72 	 * That will cleanly free only those blocks that were alloc'd.
73 	 */
74 
75 	/*
76 	 * Allocate mondo data for xcalls.
77 	 */
78 	mcpup->mondo_data = contig_mem_alloc(INTR_REPORT_SIZE);
79 
80 	if (mcpup->mondo_data == NULL) {
81 		cmn_err(CE_NOTE, "cpu%d: cpu mondo_data allocation failed",
82 		    cpu->cpu_id);
83 		return (ENOMEM);
84 	}
85 	/*
86 	 * va_to_pa() is too expensive to call for every crosscall
87 	 * so we do it here at init time and save it in machcpu.
88 	 */
89 	mcpup->mondo_data_ra = va_to_pa(mcpup->mondo_data);
90 
91 	/*
92 	 *  Allocate a per-cpu list of ncpu_guest_max for xcalls
93 	 */
94 	size = ncpu_guest_max * sizeof (uint16_t);
95 	if (size < INTR_REPORT_SIZE)
96 		size = INTR_REPORT_SIZE;
97 
98 	/*
99 	 * contig_mem_alloc() requires size to be a power of 2.
100 	 * Increase size to a power of 2 if necessary.
101 	 */
102 	if (!ISP2(size)) {
103 		size = 1 << highbit(size);
104 	}
105 
106 	mcpup->cpu_list = contig_mem_alloc(size);
107 
108 	if (mcpup->cpu_list == NULL) {
109 		cmn_err(CE_NOTE, "cpu%d: cpu cpu_list allocation failed",
110 		    cpu->cpu_id);
111 		return (ENOMEM);
112 	}
113 	mcpup->cpu_list_ra = va_to_pa(mcpup->cpu_list);
114 
115 	/*
116 	 * Allocate sun4v interrupt and error queues.
117 	 */
118 	size = cpu_q_entries * INTR_REPORT_SIZE;
119 
120 	mcpup->cpu_q_va = contig_mem_alloc(size);
121 
122 	if (mcpup->cpu_q_va == NULL) {
123 		cmn_err(CE_NOTE, "cpu%d: cpu intrq allocation failed",
124 		    cpu->cpu_id);
125 		return (ENOMEM);
126 	}
127 	mcpup->cpu_q_base_pa = va_to_pa(mcpup->cpu_q_va);
128 	mcpup->cpu_q_size = size;
129 
130 	/*
131 	 * Allocate device queues
132 	 */
133 	size = dev_q_entries * INTR_REPORT_SIZE;
134 
135 	mcpup->dev_q_va = contig_mem_alloc(size);
136 
137 	if (mcpup->dev_q_va == NULL) {
138 		cmn_err(CE_NOTE, "cpu%d: dev intrq allocation failed",
139 		    cpu->cpu_id);
140 		return (ENOMEM);
141 	}
142 	mcpup->dev_q_base_pa = va_to_pa(mcpup->dev_q_va);
143 	mcpup->dev_q_size = size;
144 
145 	/*
146 	 * Allocate resumable queue and its kernel buffer
147 	 */
148 	size = cpu_rq_entries * Q_ENTRY_SIZE;
149 
150 	mcpup->cpu_rq_va = contig_mem_alloc(2 * size);
151 
152 	if (mcpup->cpu_rq_va == NULL) {
153 		cmn_err(CE_NOTE, "cpu%d: resumable queue allocation failed",
154 		    cpu->cpu_id);
155 		return (ENOMEM);
156 	}
157 	mcpup->cpu_rq_base_pa = va_to_pa(mcpup->cpu_rq_va);
158 	mcpup->cpu_rq_size = size;
159 	/* zero out the memory */
160 	bzero(mcpup->cpu_rq_va, 2 * size);
161 
162 	/*
163 	 * Allocate non-resumable queues
164 	 */
165 	size = cpu_nrq_entries * Q_ENTRY_SIZE;
166 
167 	mcpup->cpu_nrq_va = contig_mem_alloc(2 * size);
168 
169 	if (mcpup->cpu_nrq_va == NULL) {
170 		cmn_err(CE_NOTE, "cpu%d: nonresumable queue allocation failed",
171 		    cpu->cpu_id);
172 		return (ENOMEM);
173 	}
174 	mcpup->cpu_nrq_base_pa = va_to_pa(mcpup->cpu_nrq_va);
175 	mcpup->cpu_nrq_size = size;
176 	/* zero out the memory */
177 	bzero(mcpup->cpu_nrq_va, 2 * size);
178 
179 	return (0);
180 }
181 
182 void
cpu_intrq_cleanup(struct cpu * cpu)183 cpu_intrq_cleanup(struct cpu *cpu)
184 {
185 	struct machcpu *mcpup = &cpu->cpu_m;
186 	int cpu_list_size;
187 	uint64_t cpu_q_size;
188 	uint64_t dev_q_size;
189 	uint64_t cpu_rq_size;
190 	uint64_t cpu_nrq_size;
191 
192 	/*
193 	 * Free mondo data for xcalls.
194 	 */
195 	if (mcpup->mondo_data) {
196 		contig_mem_free(mcpup->mondo_data, INTR_REPORT_SIZE);
197 		mcpup->mondo_data = NULL;
198 		mcpup->mondo_data_ra = 0;
199 	}
200 
201 	/*
202 	 *  Free per-cpu list of ncpu_guest_max for xcalls
203 	 */
204 	cpu_list_size = ncpu_guest_max * sizeof (uint16_t);
205 	if (cpu_list_size < INTR_REPORT_SIZE)
206 		cpu_list_size = INTR_REPORT_SIZE;
207 
208 	/*
209 	 * contig_mem_alloc() requires size to be a power of 2.
210 	 * Increase size to a power of 2 if necessary.
211 	 */
212 	if (!ISP2(cpu_list_size)) {
213 		cpu_list_size = 1 << highbit(cpu_list_size);
214 	}
215 
216 	if (mcpup->cpu_list) {
217 		contig_mem_free(mcpup->cpu_list, cpu_list_size);
218 		mcpup->cpu_list = NULL;
219 		mcpup->cpu_list_ra = 0;
220 	}
221 
222 	/*
223 	 * Free sun4v interrupt and error queues.
224 	 */
225 	if (mcpup->cpu_q_va) {
226 		cpu_q_size = cpu_q_entries * INTR_REPORT_SIZE;
227 		contig_mem_free(mcpup->cpu_q_va, cpu_q_size);
228 		mcpup->cpu_q_va = NULL;
229 		mcpup->cpu_q_base_pa = 0;
230 		mcpup->cpu_q_size = 0;
231 	}
232 
233 	if (mcpup->dev_q_va) {
234 		dev_q_size = dev_q_entries * INTR_REPORT_SIZE;
235 		contig_mem_free(mcpup->dev_q_va, dev_q_size);
236 		mcpup->dev_q_va = NULL;
237 		mcpup->dev_q_base_pa = 0;
238 		mcpup->dev_q_size = 0;
239 	}
240 
241 	if (mcpup->cpu_rq_va) {
242 		cpu_rq_size = cpu_rq_entries * Q_ENTRY_SIZE;
243 		contig_mem_free(mcpup->cpu_rq_va, 2 * cpu_rq_size);
244 		mcpup->cpu_rq_va = NULL;
245 		mcpup->cpu_rq_base_pa = 0;
246 		mcpup->cpu_rq_size = 0;
247 	}
248 
249 	if (mcpup->cpu_nrq_va) {
250 		cpu_nrq_size = cpu_nrq_entries * Q_ENTRY_SIZE;
251 		contig_mem_free(mcpup->cpu_nrq_va, 2 * cpu_nrq_size);
252 		mcpup->cpu_nrq_va = NULL;
253 		mcpup->cpu_nrq_base_pa = 0;
254 		mcpup->cpu_nrq_size = 0;
255 	}
256 }
257