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