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 */
59static uint_t
60resolve_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
93static kmutex_t iommu_tsb_avail_lock;
94static uint16_t iommu_tsb_avail[MAX_TSB_ALLOC];
95#define	IOMMU_TSB_INUSE		0x8000u
96static uint_t iommu_tsb_num;
97#ifdef DEBUG
98static uint_t iommu_tsb_nfree;
99#endif /* DEBUG */
100
101static caddr_t iommu_tsb_base;
102static uint_t iommu_tsb_size;
103
104uint_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 */
112uint_t iommu_tsb_size_min = MIN_TSB_BYTES;
113uint_t iommu_tsb_size_max = MAX_TSB_BYTES;
114
115caddr_t
116iommu_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 */
193uint16_t
194iommu_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
216void
217iommu_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*/
234uint_t
235iommu_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
243uint64_t *
244iommu_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