xref: /illumos-gate/usr/src/uts/sun4/os/iommutsb.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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/types.h>
30 #include <sys/iommutsb.h>
31 #include <sys/systm.h>
32 #include <sys/sysmacros.h>
33 #include <sys/debug.h>
34 #include <sys/bootconf.h>
35 #include <sys/mutex.h>
36 #include <sys/platform_module.h>
37 #include <sys/cmn_err.h>
38 
39 /*
40  * The interfaces provided by this file will eventually no longer
41  * be required once a physically contiguous memory allocator
42  * is available.
43  */
44 
45 /*
46  *  The TSB size and consequently the DVMA range is appropriated proportional
47  *  to the physical memory size.
48  *
49  *     phys_mem_size	   iommu TSB size	DVMA size
50  *	    <= 32MB		 64KB		 64MB
51  *	    <= 128MB		256KB		256MB
52  *	    <= 512MB		512KB		512MB
53  *	     > 512MB		  1MB		  1GB
54  *
55  *  NOTE: The original Solaris 8 FCS allocations must be used with
56  *        32-bit kernels.
57  *
58  */
59 static uint_t
60 resolve_tsb_size(pgcnt_t phys_mem_size)
61 {
62 	if (phys_mem_size <= 0x1000)
63 		return (0x10000);
64 	else if (phys_mem_size <= 0x4000)
65 		return (0x40000);
66 	else if (phys_mem_size <= 0x10000)
67 		return (0x80000);
68 	else
69 		return (0x100000);
70 }
71 
72 /* TSB size must be a power of 2 between the minimum and the maximum. */
73 #define	MIN_TSB_BYTES	0x2000
74 #define	MAX_TSB_BYTES	0x100000
75 
76 /*
77  * Use boot to allocate the physically contiguous memory needed for the
78  * IOMMU's TSB arrays until there is an interface for dynamically
79  * allocated, physically contiguous memory.
80  * The number IOMMUs at boot, niommu_tsbs, is set as a side effect
81  * of map_wellknown_devices(). The number of TSBs allocated is
82  * at least niommu_tsbs. On platforms supporting Dynamic Reconfiguration
83  * the platmod routine set_platform_tsb_spares() returns the
84  * maximum total number of TSBs expected. The final number of TSBs
85  * allocated is set in iommu_tsb_num.
86  *
87  * WARNING - since this routine uses boot to allocate memory, it MUST
88  * be called before the kernel takes over memory allocation from boot.
89  */
90 #define	MAX_IOMMU_PER_AGENT	2
91 #define	MAX_TSB_ALLOC		(MAX_UPA * MAX_IOMMU_PER_AGENT)
92 
93 static kmutex_t iommu_tsb_avail_lock;
94 static uint16_t iommu_tsb_avail[MAX_TSB_ALLOC];
95 #define	IOMMU_TSB_INUSE		0x8000u
96 static uint_t iommu_tsb_num;
97 #ifdef DEBUG
98 static uint_t iommu_tsb_nfree;
99 #endif /* DEBUG */
100 
101 static caddr_t iommu_tsb_base;
102 static uint_t iommu_tsb_size;
103 
104 uint_t niommu_tsbs;
105 
106 /*
107  * The following variables can be patched to override the auto-selection
108  * of dvma space based on the amount of installed physical memory.
109  * Not settable via /etc/system as it is read after iommu_tsb_init()
110  * is called.
111  */
112 uint_t iommu_tsb_size_min = MIN_TSB_BYTES;
113 uint_t iommu_tsb_size_max = MAX_TSB_BYTES;
114 
115 caddr_t
116 iommu_tsb_init(caddr_t alloc_base)
117 {
118 	size_t total_size;
119 	caddr_t base = (caddr_t)roundup((uintptr_t)alloc_base, MMU_PAGESIZE);
120 	uint_t tsb_min, tsb_max;
121 	uint_t tsb_size;
122 	uint_t ntsbs;
123 
124 	/*
125 	 * determine the amount of physical memory required for the TSB arrays
126 	 *
127 	 * assumes niommu_tsbs has already been initialized, i.e.
128 	 * map_wellknown_devices()
129 	 *
130 	 * TSB space is allocated proportional to memory size (see
131 	 * resolve_tsb_size) but later constained by the limit obtained
132 	 * from get_dvma_property_limit in the nexus attach.
133 	 */
134 	tsb_size = resolve_tsb_size(physinstalled);
135 
136 	tsb_min = MAX(iommu_tsb_size_min, MIN_TSB_BYTES);
137 	tsb_max = MIN(iommu_tsb_size_max, MAX_TSB_BYTES);
138 
139 	if (tsb_min <= tsb_max) {
140 		uint_t sz;
141 
142 		/* Ensure that min and max are powers of two. */
143 		/* guaranteed min and max are both between MIN/MAX_TSB_BYTES */
144 		for (sz = MAX_TSB_BYTES; !(sz & tsb_min); sz >>= 1)
145 			/* empty */;
146 		tsb_min = sz;
147 		for (sz = MAX_TSB_BYTES; !(sz & tsb_max); sz >>= 1)
148 			/* empty */;
149 		tsb_max = sz;
150 
151 		/* guaranteed min still <= max */
152 		tsb_size = MIN(tsb_size, tsb_max);
153 		tsb_size = MAX(tsb_size, tsb_min);
154 	} else
155 		cmn_err(CE_WARN,
156 		    "iommutsb: bad iommu_tsb_size_min/max value pair");
157 
158 	iommu_tsb_size = tsb_size;
159 
160 	if (&set_platform_tsb_spares)
161 		ntsbs = set_platform_tsb_spares();
162 	else
163 		ntsbs = 0;
164 	ntsbs = MAX(ntsbs, niommu_tsbs);
165 	ntsbs = MIN(ntsbs, MAX_TSB_ALLOC);
166 
167 	total_size = ntsbs * tsb_size;
168 
169 	if (total_size == 0)
170 		return (alloc_base);
171 
172 	/*
173 	 * allocate the physical memory for the TSB arrays
174 	 */
175 	if ((iommu_tsb_base = (caddr_t)BOP_ALLOC(bootops, base,
176 	    total_size, MMU_PAGESIZE)) == NULL)
177 		cmn_err(CE_PANIC, "Cannot allocate IOMMU TSB arrays");
178 	ASSERT(iommu_tsb_base == base);
179 
180 	iommu_tsb_num = ntsbs;
181 #ifdef DEBUG
182 	iommu_tsb_nfree = iommu_tsb_num;
183 #endif /* DEBUG */
184 
185 	return (base + total_size);
186 }
187 
188 /*
189  * External allocation interface to the nexus drivers (sbus, pci).
190  * As an aid to debugging, the upaid or portid is recorded against
191  * an allocation.
192  */
193 uint16_t
194 iommu_tsb_alloc(uint16_t id)
195 {
196 	uint16_t tsbc;
197 	uint_t i;
198 
199 	tsbc = IOMMU_TSB_COOKIE_NONE;
200 	mutex_enter(&iommu_tsb_avail_lock);
201 	for (i = 0; i < iommu_tsb_num; i++) {
202 		if (iommu_tsb_avail[i] == 0) {
203 			iommu_tsb_avail[i] = IOMMU_TSB_INUSE | id;
204 			tsbc = (uint16_t)i;
205 #ifdef DEBUG
206 			ASSERT(iommu_tsb_nfree != 0);
207 			iommu_tsb_nfree--;
208 #endif /* DEBUG */
209 			break;
210 		}
211 	}
212 	mutex_exit(&iommu_tsb_avail_lock);
213 	return (tsbc);
214 }
215 
216 void
217 iommu_tsb_free(uint16_t tsbc)
218 {
219 	ASSERT(tsbc != IOMMU_TSB_COOKIE_NONE);
220 	ASSERT(tsbc < iommu_tsb_num);
221 	mutex_enter(&iommu_tsb_avail_lock);
222 	if (iommu_tsb_avail[tsbc] == 0) {
223 		cmn_err(CE_PANIC, "iommu_tsb_free(%d): tsb not in use", tsbc);
224 	}
225 	iommu_tsb_avail[tsbc] = 0;
226 #ifdef DEBUG
227 	ASSERT(iommu_tsb_nfree < iommu_tsb_num);
228 	iommu_tsb_nfree++;
229 #endif /* DEBUG */
230 	mutex_exit(&iommu_tsb_avail_lock);
231 }
232 
233 /*ARGSUSED*/
234 uint_t
235 iommu_tsb_cookie_to_size(uint16_t tsbc)
236 {
237 	ASSERT(tsbc != IOMMU_TSB_COOKIE_NONE);
238 	ASSERT(tsbc < iommu_tsb_num);
239 	ASSERT(iommu_tsb_avail[tsbc] != 0);
240 	return (iommu_tsb_size);
241 }
242 
243 uint64_t *
244 iommu_tsb_cookie_to_va(uint16_t tsbc)
245 {
246 	ASSERT(tsbc != IOMMU_TSB_COOKIE_NONE);
247 	ASSERT(tsbc < iommu_tsb_num);
248 	ASSERT(iommu_tsb_avail[tsbc] != 0);
249 	return ((uint64_t *)(iommu_tsb_base + (tsbc * iommu_tsb_size)));
250 }
251