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 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <sys/param.h>
28 #include <fcntl.h>
29 #include <poll.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <errno.h>
33
34 #include "sys/ds_pri.h"
35 #include "pri.h"
36
37 static int pri_fd = -1;
38
39
40
41 /*
42 * Library init function
43 * Returns: Success (0), Failure (-1)
44 */
45 int
pri_init(void)46 pri_init(void)
47 {
48 int fd;
49
50 if (pri_fd != -1)
51 return (-1);
52
53 fd = open(DS_PRI_DRIVER, O_RDONLY);
54 if (fd < 0)
55 return (-1);
56
57 pri_fd = fd;
58
59 return (0);
60 }
61
62 /*
63 * Library fini function
64 * Returns: N/A
65 */
66 void
pri_fini(void)67 pri_fini(void)
68 {
69 if (pri_fd < 0)
70 return;
71
72 (void) close(pri_fd);
73 pri_fd = -1;
74 }
75
76 /*
77 * PRI retrieval function.
78 * Description:
79 * - Library routine to retrieve the Physical Resource Inventory (PRI)
80 * - Utilized by sun4v platforms which support Logical Domains
81 * - Interacts with the ds_pri pseudo driver to retrieve the
82 * PRI. ds_pri driver in turn gets the PRI from the
83 * Domain Services kernel module. Domain Services gets the
84 * PRI from the Service Processor via LDC (Logical Domain
85 * Channel).
86 * - Consumers of this api include FMA, Zeus, and picld
87 * - MT-Safe, Stateless
88 *
89 * Imports:
90 * - ds_pri driver interfaces
91 *
92 * Arguments:
93 * - wait: specifies whether caller wants to wait for a new PRI,
94 * PRI_GET is no-wait, PRI_WAITGET is wait-forever
95 * - token: opaque PRI token, accepted from and/or returned to caller,
96 * see write-only or read-write semantics below
97 * - buf: PRI buffer received from ds_pri driver, returned to caller
98 * - allocp: caller provided pointer to memory allocator function
99 * - freep: caller provided pointer to memory free function
100 *
101 * Calling Semantics:
102 * - PRI_GET call ignores the token passed in, and returns
103 * immediately with current PRI and its token (if any)
104 * - PRI_WAITGET call returns only upon the receipt of a new PRI
105 * whose token differs from the token passed in by the caller;
106 * the passed in token should come from a previous pri_get()
107 * call with return value >= 0; the new PRI buffer and its token
108 * are returned to the caller
109 * - If wait time must be bounded, the caller can spawn a thread
110 * which makes a PRI_WAITGET call; caller can choose to kill the
111 * spawned thread after a finite time
112 *
113 * Usage Semantics:
114 * - Caller can use the returned PRI buffer as an argument to
115 * to md_init_intern() to process it into a machine
116 * descriptor (md_t) format
117 * - Caller can choose to supply the same allocator and free
118 * functions to the md_init_intern() call
119 * - Once the caller is done using these data structures,
120 * the following actions need to be performed by the caller:
121 * - md_fini(mdp) if called md_init_intern()
122 * - freep(bufp, size)
123 *
124 * Returns:
125 * >0 if PRI is returned successfully (size of PRI buffer)
126 * 0 if no PRI is available
127 * -1 if there is an error (errno contains the error code
128 * provided)
129 *
130 */
131 ssize_t
pri_get(uint8_t wait,uint64_t * token,uint64_t ** buf,void * (* allocp)(size_t),void (* freep)(void *,size_t))132 pri_get(uint8_t wait, uint64_t *token, uint64_t **buf,
133 void *(*allocp)(size_t), void (*freep)(void *, size_t))
134 {
135 uint64_t *bufp; /* buf holding PRI */
136 size_t size; /* sizeof PRI */
137 struct dspri_info pri_info; /* info about PRI */
138 struct dspri_info pri_info2; /* for PRI delta check */
139
140 if (pri_fd < 0) {
141 errno = EBADF;
142 return (-1);
143 }
144
145 if (wait == PRI_WAITGET) {
146 /* wait until have new PRI with different token */
147 if (ioctl(pri_fd, DSPRI_WAIT, token) < 0) {
148 return (-1);
149 }
150 }
151
152 do {
153 /* get info on current PRI */
154 if (ioctl(pri_fd, DSPRI_GETINFO, &pri_info) < 0) {
155 return (-1);
156 }
157
158 size = (size_t)pri_info.size;
159
160 /* check to see if no PRI available yet */
161 if (size == 0) {
162 *token = pri_info.token;
163 return (0);
164 }
165
166 /* allocate a buffer and read the PRI into it */
167 if ((bufp = (uint64_t *)allocp(size)) == NULL) {
168 if (errno == 0)
169 errno = ENOMEM;
170 return (-1);
171 }
172 if (read(pri_fd, bufp, size) < 0) {
173 freep(bufp, size);
174 return (-1);
175 }
176
177 /*
178 * Check whether PRI token changed between the time
179 * we did the DSPRI_GETINFO ioctl() and the actual
180 * read() from the ds_pri driver. The token delta check
181 * tries to catch the above race condition; be sure
182 * to not leak memory on retries.
183 */
184 if (ioctl(pri_fd, DSPRI_GETINFO, &pri_info2) < 0) {
185 freep(bufp, size);
186 return (-1);
187 }
188 if (pri_info2.token != pri_info.token)
189 freep(bufp, size);
190
191 } while (pri_info2.token != pri_info.token);
192
193 /* return the PRI, its token, and its size to the caller */
194 *buf = bufp;
195 *token = pri_info.token;
196 return ((ssize_t)size);
197 }
198