1*962Stsien /*
2*962Stsien  * CDDL HEADER START
3*962Stsien  *
4*962Stsien  * The contents of this file are subject to the terms of the
5*962Stsien  * Common Development and Distribution License, Version 1.0 only
6*962Stsien  * (the "License").  You may not use this file except in compliance
7*962Stsien  * with the License.
8*962Stsien  *
9*962Stsien  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*962Stsien  * or http://www.opensolaris.org/os/licensing.
11*962Stsien  * See the License for the specific language governing permissions
12*962Stsien  * and limitations under the License.
13*962Stsien  *
14*962Stsien  * When distributing Covered Code, include this CDDL HEADER in each
15*962Stsien  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*962Stsien  * If applicable, add the following below this CDDL HEADER, with the
17*962Stsien  * fields enclosed by brackets "[]" replaced with your own identifying
18*962Stsien  * information: Portions Copyright [yyyy] [name of copyright owner]
19*962Stsien  *
20*962Stsien  * CDDL HEADER END
21*962Stsien  */
22*962Stsien /*
23*962Stsien  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*962Stsien  * Use is subject to license terms.
25*962Stsien  */
26*962Stsien 
27*962Stsien #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*962Stsien 
29*962Stsien /*
30*962Stsien  * E-cache flushing
31*962Stsien  *
32*962Stsien  * Prior to clearing the UE cache, the CPU state code needs to ensure that the
33*962Stsien  * CPU's E-cache has been flushed.  The flushing is handled by the routines in
34*962Stsien  * in this file, which use the memory controller (mc) driver to perform the
35*962Stsien  * flush.
36*962Stsien  *
37*962Stsien  * Matters are complicated by the fact that there isn't a well-known device name
38*962Stsien  * for driver access - we have to hunt around for one.  Furthermore, the minor
39*962Stsien  * nodes that are created correspond to individual memory controllers, and as
40*962Stsien  * such can change during a DR.  We'll search for a memory controller device
41*962Stsien  * during initialization just to make sure that we can do E$ flushing on this
42*962Stsien  * platform, but we're also able to rescan if the device we found suddenly
43*962Stsien  * disappears.
44*962Stsien  */
45*962Stsien 
46*962Stsien #include <cmd_ecache.h>
47*962Stsien #include <cmd.h>
48*962Stsien 
49*962Stsien #include <string.h>
50*962Stsien #include <fcntl.h>
51*962Stsien #include <unistd.h>
52*962Stsien #include <errno.h>
53*962Stsien #include <dirent.h>
54*962Stsien #include <fm/fmd_api.h>
55*962Stsien #include <sys/mc.h>
56*962Stsien #include <sys/param.h>
57*962Stsien 
58*962Stsien static int
ecache_scan_dir(const char * dir,const char * pref,char * buf,size_t bufsz)59*962Stsien ecache_scan_dir(const char *dir, const char *pref, char *buf, size_t bufsz)
60*962Stsien {
61*962Stsien 	struct dirent *dp;
62*962Stsien 	char path[MAXPATHLEN];
63*962Stsien 	DIR *mcdir;
64*962Stsien 
65*962Stsien 	if ((mcdir = opendir(dir)) == NULL)
66*962Stsien 		return (-1); /* errno is set for us */
67*962Stsien 
68*962Stsien 	while ((dp = readdir(mcdir)) != NULL) {
69*962Stsien 		struct mc_ctrlconf mcc;
70*962Stsien 		int fd;
71*962Stsien 
72*962Stsien 		if (strncmp(dp->d_name, pref, strlen(pref)) != 0)
73*962Stsien 			continue;
74*962Stsien 
75*962Stsien 		(void) snprintf(path, sizeof (path), "%s/%s", dir, dp->d_name);
76*962Stsien 
77*962Stsien 		if ((fd = open(path, O_RDONLY)) < 0)
78*962Stsien 			continue;
79*962Stsien 
80*962Stsien 		mcc.nmcs = 0;
81*962Stsien 		if (ioctl(fd, MCIOC_CTRLCONF, &mcc) >= 0 || errno != EINVAL ||
82*962Stsien 		    mcc.nmcs == 0) {
83*962Stsien 			(void) close(fd);
84*962Stsien 			continue;
85*962Stsien 		}
86*962Stsien 
87*962Stsien 		(void) close(fd);
88*962Stsien 		(void) closedir(mcdir);
89*962Stsien 
90*962Stsien 		if (strlen(path) >= bufsz)
91*962Stsien 			return (cmd_set_errno(ENOSPC));
92*962Stsien 		(void) strcpy(buf, path);
93*962Stsien 		return (0);
94*962Stsien 	}
95*962Stsien 
96*962Stsien 	(void) closedir(mcdir);
97*962Stsien 	return (cmd_set_errno(ENOENT));
98*962Stsien }
99*962Stsien 
100*962Stsien static int
ecache_find_device(char * buf,size_t bufsz)101*962Stsien ecache_find_device(char *buf, size_t bufsz)
102*962Stsien {
103*962Stsien 	if (ecache_scan_dir("/dev/mc", "mc", buf, bufsz) != 0) {
104*962Stsien 		/*
105*962Stsien 		 * Yet more platform-specific hackery.  It's possible that the
106*962Stsien 		 * /dev/mc links could be out of date, and thus we may not be
107*962Stsien 		 * able to use any of them.  As a fallback, therefore, we're
108*962Stsien 		 * going to search a couple of well-known locations in /devices.
109*962Stsien 		 */
110*962Stsien 		const char *dir = "/devices/ssm@0,0";
111*962Stsien 
112*962Stsien 		if (access(dir, R_OK) != 0)
113*962Stsien 			dir = "/devices";
114*962Stsien 
115*962Stsien 		return (ecache_scan_dir(dir, "memory-controller", buf, bufsz));
116*962Stsien 	}
117*962Stsien 
118*962Stsien 	return (0);
119*962Stsien }
120*962Stsien 
121*962Stsien int
cmd_ecache_init(void)122*962Stsien cmd_ecache_init(void)
123*962Stsien {
124*962Stsien 	return (ecache_find_device(cmd.cmd_ecache_dev,
125*962Stsien 	    sizeof (cmd.cmd_ecache_dev)));
126*962Stsien }
127*962Stsien 
128*962Stsien int
cmd_ecache_flush(int cpuid)129*962Stsien cmd_ecache_flush(int cpuid)
130*962Stsien {
131*962Stsien 	int fd;
132*962Stsien 
133*962Stsien 	if ((fd = open(cmd.cmd_ecache_dev, O_RDONLY)) < 0) {
134*962Stsien 		if (errno != ENOENT)
135*962Stsien 			return (-1); /* errno is set for us */
136*962Stsien 
137*962Stsien 		/*
138*962Stsien 		 * A DR may have occurred, thus rendering our path invalid.
139*962Stsien 		 * Try once to find another one.
140*962Stsien 		 */
141*962Stsien 		if (cmd_ecache_init() < 0 ||
142*962Stsien 		    (fd = open(cmd.cmd_ecache_dev, O_RDONLY)) < 0)
143*962Stsien 			return (-1); /* errno is set for us */
144*962Stsien 	}
145*962Stsien 
146*962Stsien 	if (ioctl(fd, MCIOC_ECFLUSH, cpuid) < 0) {
147*962Stsien 		int oserr = errno;
148*962Stsien 		(void) close(fd);
149*962Stsien 		return (cmd_set_errno(oserr));
150*962Stsien 	}
151*962Stsien 
152*962Stsien 	(void) close(fd);
153*962Stsien 	return (0);
154*962Stsien }
155