apple_smc.c revision 1.6.8.2 1 1.6.8.2 tls /* $NetBSD: apple_smc.c,v 1.6.8.2 2014/08/20 00:03:37 tls Exp $ */
2 1.6.8.2 tls
3 1.6.8.2 tls /*
4 1.6.8.2 tls * Apple System Management Controller
5 1.6.8.2 tls */
6 1.6.8.2 tls
7 1.6.8.2 tls /*-
8 1.6.8.2 tls * Copyright (c) 2013 The NetBSD Foundation, Inc.
9 1.6.8.2 tls * All rights reserved.
10 1.6.8.2 tls *
11 1.6.8.2 tls * This code is derived from software contributed to The NetBSD Foundation
12 1.6.8.2 tls * by Taylor R. Campbell.
13 1.6.8.2 tls *
14 1.6.8.2 tls * Redistribution and use in source and binary forms, with or without
15 1.6.8.2 tls * modification, are permitted provided that the following conditions
16 1.6.8.2 tls * are met:
17 1.6.8.2 tls * 1. Redistributions of source code must retain the above copyright
18 1.6.8.2 tls * notice, this list of conditions and the following disclaimer.
19 1.6.8.2 tls * 2. Redistributions in binary form must reproduce the above copyright
20 1.6.8.2 tls * notice, this list of conditions and the following disclaimer in the
21 1.6.8.2 tls * documentation and/or other materials provided with the distribution.
22 1.6.8.2 tls *
23 1.6.8.2 tls * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 1.6.8.2 tls * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 1.6.8.2 tls * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 1.6.8.2 tls * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 1.6.8.2 tls * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 1.6.8.2 tls * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 1.6.8.2 tls * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 1.6.8.2 tls * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 1.6.8.2 tls * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 1.6.8.2 tls * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 1.6.8.2 tls * POSSIBILITY OF SUCH DAMAGE.
34 1.6.8.2 tls */
35 1.6.8.2 tls
36 1.6.8.2 tls #include <sys/cdefs.h>
37 1.6.8.2 tls __KERNEL_RCSID(0, "$NetBSD: apple_smc.c,v 1.6.8.2 2014/08/20 00:03:37 tls Exp $");
38 1.6.8.2 tls
39 1.6.8.2 tls #include <sys/types.h>
40 1.6.8.2 tls #include <sys/param.h>
41 1.6.8.2 tls #include <sys/device.h>
42 1.6.8.2 tls #include <sys/errno.h>
43 1.6.8.2 tls #include <sys/kmem.h>
44 1.6.8.2 tls #include <sys/module.h>
45 1.6.8.2 tls #include <sys/mutex.h>
46 1.6.8.2 tls #include <sys/rwlock.h>
47 1.6.8.2 tls #if 0 /* XXX sysctl */
48 1.6.8.2 tls #include <sys/sysctl.h>
49 1.6.8.2 tls #endif
50 1.6.8.2 tls #include <sys/systm.h>
51 1.6.8.2 tls
52 1.6.8.2 tls #include <dev/ic/apple_smc.h>
53 1.6.8.2 tls #include <dev/ic/apple_smcreg.h>
54 1.6.8.2 tls #include <dev/ic/apple_smcvar.h>
55 1.6.8.2 tls
56 1.6.8.2 tls /* Must match the config(5) name. */
57 1.6.8.2 tls #define APPLE_SMC_BUS "applesmcbus"
58 1.6.8.2 tls
59 1.6.8.2 tls static int apple_smc_search(device_t, cfdata_t, const int *, void *);
60 1.6.8.2 tls static uint8_t apple_smc_bus_read_1(struct apple_smc_tag *, bus_size_t);
61 1.6.8.2 tls static void apple_smc_bus_write_1(struct apple_smc_tag *, bus_size_t,
62 1.6.8.2 tls uint8_t);
63 1.6.8.2 tls static int apple_smc_read_data(struct apple_smc_tag *, uint8_t *);
64 1.6.8.2 tls static int apple_smc_write(struct apple_smc_tag *, bus_size_t, uint8_t);
65 1.6.8.2 tls static int apple_smc_write_cmd(struct apple_smc_tag *, uint8_t);
66 1.6.8.2 tls static int apple_smc_write_data(struct apple_smc_tag *, uint8_t);
67 1.6.8.2 tls static int apple_smc_begin(struct apple_smc_tag *, uint8_t,
68 1.6.8.2 tls const char *, uint8_t);
69 1.6.8.2 tls static int apple_smc_input(struct apple_smc_tag *, uint8_t,
70 1.6.8.2 tls const char *, void *, uint8_t);
71 1.6.8.2 tls static int apple_smc_output(struct apple_smc_tag *, uint8_t,
72 1.6.8.2 tls const char *, const void *, uint8_t);
73 1.6.8.2 tls
74 1.6.8.2 tls void
76 1.6.8.2 tls apple_smc_attach(struct apple_smc_tag *smc)
77 1.6.8.2 tls {
78 1.6.8.2 tls
79 1.6.8.2 tls mutex_init(&smc->smc_io_lock, MUTEX_DEFAULT, IPL_NONE);
80 1.6.8.2 tls #if 0 /* XXX sysctl */
81 1.6.8.2 tls apple_smc_sysctl_setup(smc);
82 1.6.8.2 tls #endif
83 1.6.8.2 tls
84 1.6.8.2 tls /* Attach any children. */
85 1.6.8.2 tls (void)apple_smc_rescan(smc, APPLE_SMC_BUS, NULL);
86 1.6.8.2 tls }
87 1.6.8.2 tls
88 1.6.8.2 tls int
89 1.6.8.2 tls apple_smc_detach(struct apple_smc_tag *smc, int flags)
90 1.6.8.2 tls {
91 1.6.8.2 tls int error;
92 1.6.8.2 tls
93 1.6.8.2 tls /* Fail if we can't detach all our children. */
94 1.6.8.2 tls error = config_detach_children(smc->smc_dev, flags);
95 1.6.8.2 tls if (error)
96 1.6.8.2 tls return error;
97 1.6.8.2 tls
98 1.6.8.2 tls #if 0 /* XXX sysctl */
99 1.6.8.2 tls sysctl_teardown(&smc->smc_log);
100 1.6.8.2 tls #endif
101 1.6.8.2 tls mutex_destroy(&smc->smc_io_lock);
102 1.6.8.2 tls
103 1.6.8.2 tls return 0;
104 1.6.8.2 tls }
105 1.6.8.2 tls
106 1.6.8.2 tls int
107 1.6.8.2 tls apple_smc_rescan(struct apple_smc_tag *smc, const char *ifattr,
108 1.6.8.2 tls const int *locators)
109 1.6.8.2 tls {
110 1.6.8.2 tls
111 1.6.8.2 tls /* Let autoconf(9) do the work of finding new children. */
112 1.6.8.2 tls (void)config_search_loc(&apple_smc_search, smc->smc_dev, APPLE_SMC_BUS,
113 1.6.8.2 tls locators, smc);
114 1.6.8.2 tls return 0;
115 1.6.8.2 tls }
116 1.6.8.2 tls
117 1.6.8.2 tls static int
118 1.6.8.2 tls apple_smc_search(device_t parent, cfdata_t cf, const int *locators, void *aux)
119 1.6.8.2 tls {
120 1.6.8.2 tls struct apple_smc_tag *const smc = aux;
121 1.6.8.2 tls static const struct apple_smc_attach_args zero_asa;
122 1.6.8.2 tls struct apple_smc_attach_args asa = zero_asa;
123 1.6.8.2 tls device_t dev;
124 1.6.8.2 tls deviter_t di;
125 1.6.8.2 tls bool attached = false;
126 1.6.8.2 tls
127 1.6.8.2 tls /*
128 1.6.8.2 tls * If this device has already attached, don't attach it again.
129 1.6.8.2 tls *
130 1.6.8.2 tls * XXX This is a pretty silly way to query the children, but
131 1.6.8.2 tls * struct device doesn't seem to list its children.
132 1.6.8.2 tls */
133 1.6.8.2 tls for (dev = deviter_first(&di, DEVITER_F_LEAVES_FIRST);
134 1.6.8.2 tls dev != NULL;
135 1.6.8.2 tls dev = deviter_next(&di)) {
136 1.6.8.2 tls if (device_parent(dev) != parent)
137 1.6.8.2 tls continue;
138 1.6.8.2 tls if (!device_is_a(dev, cf->cf_name))
139 1.6.8.2 tls continue;
140 1.6.8.2 tls attached = true;
141 1.6.8.2 tls break;
142 1.6.8.2 tls }
143 1.6.8.2 tls deviter_release(&di);
144 1.6.8.2 tls if (attached)
145 1.6.8.2 tls return 0;
146 1.6.8.2 tls
147 1.6.8.2 tls /* If this device doesn't match, don't attach it. */
148 1.6.8.2 tls if (!config_match(parent, cf, aux))
149 1.6.8.2 tls return 0;
150 1.6.8.2 tls
151 1.6.8.2 tls /* Looks hunky-dory. Attach. */
152 1.6.8.2 tls asa.asa_smc = smc;
153 1.6.8.2 tls (void)config_attach_loc(parent, cf, locators, &asa, NULL);
154 1.6.8.2 tls return 0;
155 1.6.8.2 tls }
156 1.6.8.2 tls
157 1.6.8.2 tls void
158 1.6.8.2 tls apple_smc_child_detached(struct apple_smc_tag *smc __unused,
159 1.6.8.2 tls device_t child __unused)
160 1.6.8.2 tls {
161 1.6.8.2 tls /* We keep no books about our children. */
162 1.6.8.2 tls }
163 1.6.8.2 tls
164 1.6.8.2 tls static uint8_t
166 1.6.8.2 tls apple_smc_bus_read_1(struct apple_smc_tag *smc, bus_size_t reg)
167 1.6.8.2 tls {
168 1.6.8.2 tls
169 1.6.8.2 tls return bus_space_read_1(smc->smc_bst, smc->smc_bsh, reg);
170 1.6.8.2 tls }
171 1.6.8.2 tls
172 1.6.8.2 tls static void
173 1.6.8.2 tls apple_smc_bus_write_1(struct apple_smc_tag *smc, bus_size_t reg, uint8_t v)
174 1.6.8.2 tls {
175 1.6.8.2 tls
176 1.6.8.2 tls bus_space_write_1(smc->smc_bst, smc->smc_bsh, reg, v);
177 1.6.8.2 tls }
178 1.6.8.2 tls
179 1.6.8.2 tls /*
180 1.6.8.2 tls * XXX These delays are pretty randomly chosen. Wait in 100 us
181 1.6.8.2 tls * increments, up to a total of 1 ms.
182 1.6.8.2 tls */
183 1.6.8.2 tls
184 1.6.8.2 tls static int
185 1.6.8.2 tls apple_smc_read_data(struct apple_smc_tag *smc, uint8_t *byte)
186 1.6.8.2 tls {
187 1.6.8.2 tls uint8_t status;
188 1.6.8.2 tls unsigned int i;
189 1.6.8.2 tls
190 1.6.8.2 tls KASSERT(mutex_owned(&smc->smc_io_lock));
191 1.6.8.2 tls
192 1.6.8.2 tls /*
193 1.6.8.2 tls * Wait until the status register says there's data to read and
194 1.6.8.2 tls * read it.
195 1.6.8.2 tls */
196 1.6.8.2 tls for (i = 0; i < 100; i++) {
197 1.6.8.2 tls status = apple_smc_bus_read_1(smc, APPLE_SMC_CSR);
198 1.6.8.2 tls if (status & APPLE_SMC_STATUS_READ_READY) {
199 1.6.8.2 tls *byte = apple_smc_bus_read_1(smc, APPLE_SMC_DATA);
200 1.6.8.2 tls return 0;
201 1.6.8.2 tls }
202 1.6.8.2 tls DELAY(100);
203 1.6.8.2 tls }
204 1.6.8.2 tls
205 1.6.8.2 tls return ETIMEDOUT;
206 1.6.8.2 tls }
207 1.6.8.2 tls
208 1.6.8.2 tls static int
209 1.6.8.2 tls apple_smc_write(struct apple_smc_tag *smc, bus_size_t reg, uint8_t byte)
210 1.6.8.2 tls {
211 1.6.8.2 tls uint8_t status;
212 1.6.8.2 tls unsigned int i;
213 1.6.8.2 tls
214 1.6.8.2 tls KASSERT(mutex_owned(&smc->smc_io_lock));
215 1.6.8.2 tls
216 1.6.8.2 tls /*
217 1.6.8.2 tls * Write the byte and then wait until the status register says
218 1.6.8.2 tls * it has been accepted.
219 1.6.8.2 tls */
220 1.6.8.2 tls apple_smc_bus_write_1(smc, reg, byte);
221 1.6.8.2 tls for (i = 0; i < 100; i++) {
222 1.6.8.2 tls status = apple_smc_bus_read_1(smc, APPLE_SMC_CSR);
223 1.6.8.2 tls if (status & APPLE_SMC_STATUS_WRITE_ACCEPTED)
224 1.6.8.2 tls return 0;
225 1.6.8.2 tls DELAY(100);
226 1.6.8.2 tls
227 1.6.8.2 tls /* Write again if it hasn't been acknowledged at all. */
228 1.6.8.2 tls if (!(status & APPLE_SMC_STATUS_WRITE_PENDING))
229 1.6.8.2 tls apple_smc_bus_write_1(smc, reg, byte);
230 1.6.8.2 tls }
231 1.6.8.2 tls
232 1.6.8.2 tls return ETIMEDOUT;
233 1.6.8.2 tls }
234 1.6.8.2 tls
235 1.6.8.2 tls static int
236 1.6.8.2 tls apple_smc_write_cmd(struct apple_smc_tag *smc, uint8_t cmd)
237 1.6.8.2 tls {
238 1.6.8.2 tls
239 1.6.8.2 tls return apple_smc_write(smc, APPLE_SMC_CSR, cmd);
240 1.6.8.2 tls }
241 1.6.8.2 tls
242 1.6.8.2 tls static int
243 1.6.8.2 tls apple_smc_write_data(struct apple_smc_tag *smc, uint8_t data)
244 1.6.8.2 tls {
245 1.6.8.2 tls
246 1.6.8.2 tls return apple_smc_write(smc, APPLE_SMC_DATA, data);
247 1.6.8.2 tls }
248 1.6.8.2 tls
249 1.6.8.2 tls static int
251 1.6.8.2 tls apple_smc_begin(struct apple_smc_tag *smc, uint8_t cmd, const char *key,
252 1.6.8.2 tls uint8_t size)
253 1.6.8.2 tls {
254 1.6.8.2 tls unsigned int i;
255 1.6.8.2 tls int error;
256 1.6.8.2 tls
257 1.6.8.2 tls KASSERT(mutex_owned(&smc->smc_io_lock));
258 1.6.8.2 tls
259 1.6.8.2 tls /* Write the command first. */
260 1.6.8.2 tls error = apple_smc_write_cmd(smc, cmd);
261 1.6.8.2 tls if (error)
262 1.6.8.2 tls return error;
263 1.6.8.2 tls
264 1.6.8.2 tls /* Write the key next. */
265 1.6.8.2 tls for (i = 0; i < 4; i++) {
266 1.6.8.2 tls error = apple_smc_write_data(smc, key[i]);
267 1.6.8.2 tls if (error)
268 1.6.8.2 tls return error;
269 1.6.8.2 tls }
270 1.6.8.2 tls
271 1.6.8.2 tls /* Finally, report how many bytes of data we want to send/receive. */
272 1.6.8.2 tls error = apple_smc_write_data(smc, size);
273 1.6.8.2 tls if (error)
274 1.6.8.2 tls return error;
275 1.6.8.2 tls
276 1.6.8.2 tls return 0;
277 1.6.8.2 tls }
278 1.6.8.2 tls
279 1.6.8.2 tls static int
280 1.6.8.2 tls apple_smc_input(struct apple_smc_tag *smc, uint8_t cmd, const char *key,
281 1.6.8.2 tls void *buffer, uint8_t size)
282 1.6.8.2 tls {
283 1.6.8.2 tls uint8_t *bytes = buffer;
284 1.6.8.2 tls uint8_t i;
285 1.6.8.2 tls int error;
286 1.6.8.2 tls
287 1.6.8.2 tls /* Grab the SMC I/O lock. */
288 1.6.8.2 tls mutex_enter(&smc->smc_io_lock);
289 1.6.8.2 tls
290 1.6.8.2 tls /* Initiate the command with this key. */
291 1.6.8.2 tls error = apple_smc_begin(smc, cmd, key, size);
292 1.6.8.2 tls if (error)
293 1.6.8.2 tls goto out;
294 1.6.8.2 tls
295 1.6.8.2 tls /* Read each byte of data in sequence. */
296 1.6.8.2 tls for (i = 0; i < size; i++) {
297 1.6.8.2 tls error = apple_smc_read_data(smc, &bytes[i]);
298 1.6.8.2 tls if (error)
299 1.6.8.2 tls goto out;
300 1.6.8.2 tls }
301 1.6.8.2 tls
302 1.6.8.2 tls /* Success! */
303 1.6.8.2 tls error = 0;
304 1.6.8.2 tls
305 1.6.8.2 tls out: mutex_exit(&smc->smc_io_lock);
306 1.6.8.2 tls return error;
307 1.6.8.2 tls }
308 1.6.8.2 tls
309 1.6.8.2 tls static int
310 1.6.8.2 tls apple_smc_output(struct apple_smc_tag *smc, uint8_t cmd, const char *key,
311 1.6.8.2 tls const void *buffer, uint8_t size)
312 1.6.8.2 tls {
313 1.6.8.2 tls const uint8_t *bytes = buffer;
314 1.6.8.2 tls uint8_t i;
315 1.6.8.2 tls int error;
316 1.6.8.2 tls
317 1.6.8.2 tls /* Grab the SMC I/O lock. */
318 1.6.8.2 tls mutex_enter(&smc->smc_io_lock);
319 1.6.8.2 tls
320 1.6.8.2 tls /* Initiate the command with this key. */
321 1.6.8.2 tls error = apple_smc_begin(smc, cmd, key, size);
322 1.6.8.2 tls if (error)
323 1.6.8.2 tls goto out;
324 1.6.8.2 tls
325 1.6.8.2 tls /* Write each byte of data in sequence. */
326 1.6.8.2 tls for (i = 0; i < size; i++) {
327 1.6.8.2 tls error = apple_smc_write_data(smc, bytes[i]);
328 1.6.8.2 tls if (error)
329 1.6.8.2 tls goto out;
330 1.6.8.2 tls }
331 1.6.8.2 tls
332 1.6.8.2 tls /* Success! */
333 1.6.8.2 tls error = 0;
334 1.6.8.2 tls
335 1.6.8.2 tls out: mutex_exit(&smc->smc_io_lock);
336 1.6.8.2 tls return error;
337 1.6.8.2 tls }
338 1.6.8.2 tls
339 1.6.8.2 tls struct apple_smc_key {
341 1.6.8.2 tls char ask_name[4 + 1];
342 1.6.8.2 tls struct apple_smc_desc ask_desc;
343 1.6.8.2 tls #ifdef DIAGNOSTIC
344 1.6.8.2 tls struct apple_smc_tag *ask_smc;
345 1.6.8.2 tls #endif
346 1.6.8.2 tls };
347 1.6.8.2 tls
348 1.6.8.2 tls const char *
349 1.6.8.2 tls apple_smc_key_name(const struct apple_smc_key *key)
350 1.6.8.2 tls {
351 1.6.8.2 tls
352 1.6.8.2 tls return key->ask_name;
353 1.6.8.2 tls }
354 1.6.8.2 tls
355 1.6.8.2 tls const struct apple_smc_desc *
356 1.6.8.2 tls apple_smc_key_desc(const struct apple_smc_key *key)
357 1.6.8.2 tls {
358 1.6.8.2 tls
359 1.6.8.2 tls return &key->ask_desc;
360 1.6.8.2 tls }
361 1.6.8.2 tls
362 1.6.8.2 tls uint32_t
363 1.6.8.2 tls apple_smc_nkeys(struct apple_smc_tag *smc)
364 1.6.8.2 tls {
365 1.6.8.2 tls
366 1.6.8.2 tls return smc->smc_nkeys;
367 1.6.8.2 tls }
368 1.6.8.2 tls
369 1.6.8.2 tls int
370 1.6.8.2 tls apple_smc_nth_key(struct apple_smc_tag *smc, uint32_t index,
371 1.6.8.2 tls const char type[4 + 1], struct apple_smc_key **keyp)
372 1.6.8.2 tls {
373 1.6.8.2 tls union { uint32_t u32; char name[4]; } index_be;
374 1.6.8.2 tls struct apple_smc_key *key;
375 1.6.8.2 tls int error;
376 1.6.8.2 tls
377 1.6.8.2 tls /* Paranoia: type must be NULL or 4 non-null characters long. */
378 1.6.8.2 tls if ((type != NULL) && (strlen(type) != 4))
379 1.6.8.2 tls return EINVAL;
380 1.6.8.2 tls
381 1.6.8.2 tls /* Create a new key. XXX Consider caching these. */
382 1.6.8.2 tls key = kmem_alloc(sizeof(*key), KM_SLEEP);
383 1.6.8.2 tls #ifdef DIAGNOSTIC
384 1.6.8.2 tls key->ask_smc = smc;
385 1.6.8.2 tls #endif
386 1.6.8.2 tls
387 1.6.8.2 tls /* Ask the SMC what the name of the key by this number is. */
388 1.6.8.2 tls index_be.u32 = htobe32(index);
389 1.6.8.2 tls error = apple_smc_input(smc, APPLE_SMC_CMD_NTH_KEY, index_be.name,
390 1.6.8.2 tls key->ask_name, 4);
391 1.6.8.2 tls if (error)
392 1.6.8.2 tls goto fail;
393 1.6.8.2 tls
394 1.6.8.2 tls /* Null-terminate the name. */
395 1.6.8.2 tls key->ask_name[4] = '\0';
396 1.6.8.2 tls
397 1.6.8.2 tls /* Ask the SMC for a description of this key by name. */
398 1.6.8.2 tls CTASSERT(sizeof(key->ask_desc) == 6);
399 1.6.8.2 tls error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name,
400 1.6.8.2 tls &key->ask_desc, 6);
401 1.6.8.2 tls if (error)
402 1.6.8.2 tls goto fail;
403 1.6.8.2 tls
404 1.6.8.2 tls /* Fail with EINVAL if the types don't match. */
405 1.6.8.2 tls if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) {
406 1.6.8.2 tls error = EINVAL;
407 1.6.8.2 tls goto fail;
408 1.6.8.2 tls }
409 1.6.8.2 tls
410 1.6.8.2 tls /* Success! */
411 1.6.8.2 tls *keyp = key;
412 1.6.8.2 tls return 0;
413 1.6.8.2 tls
414 1.6.8.2 tls fail: kmem_free(key, sizeof(*key));
415 1.6.8.2 tls return error;
416 1.6.8.2 tls }
417 1.6.8.2 tls
418 1.6.8.2 tls int
419 1.6.8.2 tls apple_smc_named_key(struct apple_smc_tag *smc, const char name[4 + 1],
420 1.6.8.2 tls const char type[4 + 1], struct apple_smc_key **keyp)
421 1.6.8.2 tls {
422 1.6.8.2 tls struct apple_smc_key *key;
423 1.6.8.2 tls int error;
424 1.6.8.2 tls
425 1.6.8.2 tls /* Paranoia: name must be 4 non-null characters long. */
426 1.6.8.2 tls KASSERT(name != NULL);
427 1.6.8.2 tls if (strlen(name) != 4)
428 1.6.8.2 tls return EINVAL;
429 1.6.8.2 tls
430 1.6.8.2 tls /* Paranoia: type must be NULL or 4 non-null characters long. */
431 1.6.8.2 tls if ((type != NULL) && (strlen(type) != 4))
432 1.6.8.2 tls return EINVAL;
433 1.6.8.2 tls
434 1.6.8.2 tls /* Create a new key. XXX Consider caching these. */
435 1.6.8.2 tls key = kmem_alloc(sizeof(*key), KM_SLEEP);
436 1.6.8.2 tls #ifdef DIAGNOSTIC
437 1.6.8.2 tls key->ask_smc = smc;
438 1.6.8.2 tls #endif
439 1.6.8.2 tls
440 1.6.8.2 tls /* Use the specified name, and make sure it's null-terminated. */
441 1.6.8.2 tls (void)memcpy(key->ask_name, name, 4);
442 1.6.8.2 tls key->ask_name[4] = '\0';
443 1.6.8.2 tls
444 1.6.8.2 tls /* Ask the SMC for a description of this key by name. */
445 1.6.8.2 tls CTASSERT(sizeof(key->ask_desc) == 6);
446 1.6.8.2 tls error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name,
447 1.6.8.2 tls &key->ask_desc, 6);
448 1.6.8.2 tls if (error)
449 1.6.8.2 tls goto fail;
450 1.6.8.2 tls
451 1.6.8.2 tls /* Fail with EINVAL if the types don't match. */
452 1.6.8.2 tls if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) {
453 1.6.8.2 tls error = EINVAL;
454 1.6.8.2 tls goto fail;
455 1.6.8.2 tls }
456 1.6.8.2 tls
457 1.6.8.2 tls /* Success! */
458 1.6.8.2 tls *keyp = key;
459 1.6.8.2 tls return 0;
460 1.6.8.2 tls
461 1.6.8.2 tls fail: kmem_free(key, sizeof(*key));
462 1.6.8.2 tls return error;
463 1.6.8.2 tls }
464 1.6.8.2 tls
465 1.6.8.2 tls void
466 1.6.8.2 tls apple_smc_release_key(struct apple_smc_tag *smc, struct apple_smc_key *key)
467 1.6.8.2 tls {
468 1.6.8.2 tls
469 1.6.8.2 tls #ifdef DIAGNOSTIC
470 1.6.8.2 tls /* Make sure the caller didn't mix up SMC tags. */
471 1.6.8.2 tls if (key->ask_smc != smc)
472 1.6.8.2 tls aprint_error_dev(smc->smc_dev,
473 1.6.8.2 tls "releasing key with wrong tag: %p != %p",
474 1.6.8.2 tls smc, key->ask_smc);
475 1.6.8.2 tls #endif
476 1.6.8.2 tls
477 1.6.8.2 tls /* Nothing to do but free the key's memory. */
478 1.6.8.2 tls kmem_free(key, sizeof(*key));
479 1.6.8.2 tls }
480 1.6.8.2 tls
481 1.6.8.2 tls int
482 1.6.8.2 tls apple_smc_key_search(struct apple_smc_tag *smc, const char *name,
483 1.6.8.2 tls uint32_t *result)
484 1.6.8.2 tls {
485 1.6.8.2 tls struct apple_smc_key *key;
486 1.6.8.2 tls uint32_t start = 0, end = apple_smc_nkeys(smc), median;
487 1.6.8.2 tls int cmp;
488 1.6.8.2 tls int error;
489 1.6.8.2 tls
490 1.6.8.2 tls /* Do a binary search on the SMC's key space. */
491 1.6.8.2 tls while (start < end) {
492 1.6.8.2 tls median = (start + ((end - start) / 2));
493 1.6.8.2 tls error = apple_smc_nth_key(smc, median, NULL, &key);
494 1.6.8.2 tls if (error)
495 1.6.8.2 tls return error;
496 1.6.8.2 tls
497 1.6.8.2 tls cmp = memcmp(name, apple_smc_key_name(key), 4);
498 1.6.8.2 tls if (cmp < 0)
499 1.6.8.2 tls end = median;
500 1.6.8.2 tls else if (cmp > 0)
501 1.6.8.2 tls start = (median + 1);
502 1.6.8.2 tls else
503 1.6.8.2 tls start = end = median; /* stop here */
504 1.6.8.2 tls
505 1.6.8.2 tls apple_smc_release_key(smc, key);
506 1.6.8.2 tls }
507 1.6.8.2 tls
508 1.6.8.2 tls /* Success! */
509 1.6.8.2 tls *result = start;
510 1.6.8.2 tls return 0;
511 1.6.8.2 tls }
512 1.6.8.2 tls
513 1.6.8.2 tls int
515 1.6.8.2 tls apple_smc_read_key(struct apple_smc_tag *smc, const struct apple_smc_key *key,
516 1.6.8.2 tls void *buffer, uint8_t size)
517 1.6.8.2 tls {
518 1.6.8.2 tls
519 1.6.8.2 tls /* Refuse if software and hardware disagree on the key's size. */
520 1.6.8.2 tls if (key->ask_desc.asd_size != size)
521 1.6.8.2 tls return EINVAL;
522 1.6.8.2 tls
523 1.6.8.2 tls /* Refuse if the hardware doesn't want us to read it. */
524 1.6.8.2 tls if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_READ))
525 1.6.8.2 tls return EACCES;
526 1.6.8.2 tls
527 1.6.8.2 tls /* Looks good. Try reading it from the hardware. */
528 1.6.8.2 tls return apple_smc_input(smc, APPLE_SMC_CMD_READ_KEY, key->ask_name,
529 1.6.8.2 tls buffer, size);
530 1.6.8.2 tls }
531 1.6.8.2 tls
532 1.6.8.2 tls int
533 1.6.8.2 tls apple_smc_read_key_1(struct apple_smc_tag *smc,
534 1.6.8.2 tls const struct apple_smc_key *key, uint8_t *p)
535 1.6.8.2 tls {
536 1.6.8.2 tls
537 1.6.8.2 tls return apple_smc_read_key(smc, key, p, 1);
538 1.6.8.2 tls }
539 1.6.8.2 tls
540 1.6.8.2 tls int
541 1.6.8.2 tls apple_smc_read_key_2(struct apple_smc_tag *smc,
542 1.6.8.2 tls const struct apple_smc_key *key, uint16_t *p)
543 1.6.8.2 tls {
544 1.6.8.2 tls uint16_t be;
545 1.6.8.2 tls int error;
546 1.6.8.2 tls
547 1.6.8.2 tls /* Read a big-endian quantity from the hardware. */
548 1.6.8.2 tls error = apple_smc_read_key(smc, key, &be, 2);
549 1.6.8.2 tls if (error)
550 1.6.8.2 tls return error;
551 1.6.8.2 tls
552 1.6.8.2 tls /* Convert it to host order. */
553 1.6.8.2 tls *p = be16toh(be);
554 1.6.8.2 tls
555 1.6.8.2 tls /* Success! */
556 1.6.8.2 tls return 0;
557 1.6.8.2 tls }
558 1.6.8.2 tls
559 1.6.8.2 tls int
560 1.6.8.2 tls apple_smc_read_key_4(struct apple_smc_tag *smc,
561 1.6.8.2 tls const struct apple_smc_key *key, uint32_t *p)
562 1.6.8.2 tls {
563 1.6.8.2 tls uint32_t be;
564 1.6.8.2 tls int error;
565 1.6.8.2 tls
566 1.6.8.2 tls /* Read a big-endian quantity from the hardware. */
567 1.6.8.2 tls error = apple_smc_read_key(smc, key, &be, 4);
568 1.6.8.2 tls if (error)
569 1.6.8.2 tls return error;
570 1.6.8.2 tls
571 1.6.8.2 tls /* Convert it to host order. */
572 1.6.8.2 tls *p = be32toh(be);
573 1.6.8.2 tls
574 1.6.8.2 tls /* Success! */
575 1.6.8.2 tls return 0;
576 1.6.8.2 tls }
577 1.6.8.2 tls
578 1.6.8.2 tls int
579 1.6.8.2 tls apple_smc_write_key(struct apple_smc_tag *smc, const struct apple_smc_key *key,
580 1.6.8.2 tls const void *buffer, uint8_t size)
581 1.6.8.2 tls {
582 1.6.8.2 tls
583 1.6.8.2 tls /* Refuse if software and hardware disagree on the key's size. */
584 1.6.8.2 tls if (key->ask_desc.asd_size != size)
585 1.6.8.2 tls return EINVAL;
586 1.6.8.2 tls
587 1.6.8.2 tls /* Refuse if the hardware doesn't want us to write it. */
588 1.6.8.2 tls if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_WRITE))
589 1.6.8.2 tls return EACCES;
590 1.6.8.2 tls
591 1.6.8.2 tls /* Looks good. Try writing it to the hardware. */
592 1.6.8.2 tls return apple_smc_output(smc, APPLE_SMC_CMD_WRITE_KEY, key->ask_name,
593 1.6.8.2 tls buffer, size);
594 1.6.8.2 tls }
595 1.6.8.2 tls
596 1.6.8.2 tls int
597 1.6.8.2 tls apple_smc_write_key_1(struct apple_smc_tag *smc,
598 1.6.8.2 tls const struct apple_smc_key *key, uint8_t v)
599 1.6.8.2 tls {
600 1.6.8.2 tls
601 1.6.8.2 tls return apple_smc_write_key(smc, key, &v, 1);
602 1.6.8.2 tls }
603 1.6.8.2 tls
604 1.6.8.2 tls int
605 1.6.8.2 tls apple_smc_write_key_2(struct apple_smc_tag *smc,
606 1.6.8.2 tls const struct apple_smc_key *key, uint16_t v)
607 1.6.8.2 tls {
608 1.6.8.2 tls /* Convert the quantity from host to big-endian byte order. */
609 1.6.8.2 tls const uint16_t v_be = htobe16(v);
610 1.6.8.2 tls
611 1.6.8.2 tls /* Write the big-endian quantity to the hardware. */
612 1.6.8.2 tls return apple_smc_write_key(smc, key, &v_be, 2);
613 1.6.8.2 tls }
614 1.6.8.2 tls
615 1.6.8.2 tls int
616 1.6.8.2 tls apple_smc_write_key_4(struct apple_smc_tag *smc,
617 1.6.8.2 tls const struct apple_smc_key *key, uint32_t v)
618 1.6.8.2 tls {
619 1.6.8.2 tls /* Convert the quantity from host to big-endian byte order. */
620 1.6.8.2 tls const uint32_t v_be = htobe32(v);
621 1.6.8.2 tls
622 1.6.8.2 tls /* Write the big-endian quantity to the hardware. */
623 1.6.8.2 tls return apple_smc_write_key(smc, key, &v_be, 4);
624 1.6.8.2 tls }
625 1.6.8.2 tls
626 1.6.8.2 tls MODULE(MODULE_CLASS_MISC, apple_smc, NULL)
628 1.6.8.2 tls
629 1.6.8.2 tls static int
630 1.6.8.2 tls apple_smc_modcmd(modcmd_t cmd, void *data __unused)
631 1.6.8.2 tls {
632 1.6.8.2 tls
633 1.6.8.2 tls /* Nothing to do for now to set up or tear down the module. */
634 1.6.8.2 tls switch (cmd) {
635 1.6.8.2 tls case MODULE_CMD_INIT:
636 1.6.8.2 tls return 0;
637 1.6.8.2 tls
638 1.6.8.2 tls case MODULE_CMD_FINI:
639 return 0;
640
641 default:
642 return ENOTTY;
643 }
644 }
645