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 2004 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/*
30 * E-cache flushing
31 *
32 * Prior to clearing the UE cache, the CPU state code needs to ensure that the
33 * CPU's E-cache has been flushed.  The flushing is handled by the routines in
34 * in this file, which use the memory controller (mc) driver to perform the
35 * flush.
36 *
37 * Matters are complicated by the fact that there isn't a well-known device name
38 * for driver access - we have to hunt around for one.  Furthermore, the minor
39 * nodes that are created correspond to individual memory controllers, and as
40 * such can change during a DR.  We'll search for a memory controller device
41 * during initialization just to make sure that we can do E$ flushing on this
42 * platform, but we're also able to rescan if the device we found suddenly
43 * disappears.
44 */
45
46#include <cmd_ecache.h>
47#include <cmd.h>
48
49#include <string.h>
50#include <fcntl.h>
51#include <unistd.h>
52#include <errno.h>
53#include <dirent.h>
54#include <fm/fmd_api.h>
55#include <sys/mc.h>
56#include <sys/param.h>
57
58static int
59ecache_scan_dir(const char *dir, const char *pref, char *buf, size_t bufsz)
60{
61	struct dirent *dp;
62	char path[MAXPATHLEN];
63	DIR *mcdir;
64
65	if ((mcdir = opendir(dir)) == NULL)
66		return (-1); /* errno is set for us */
67
68	while ((dp = readdir(mcdir)) != NULL) {
69		struct mc_ctrlconf mcc;
70		int fd;
71
72		if (strncmp(dp->d_name, pref, strlen(pref)) != 0)
73			continue;
74
75		(void) snprintf(path, sizeof (path), "%s/%s", dir, dp->d_name);
76
77		if ((fd = open(path, O_RDONLY)) < 0)
78			continue;
79
80		mcc.nmcs = 0;
81		if (ioctl(fd, MCIOC_CTRLCONF, &mcc) >= 0 || errno != EINVAL ||
82		    mcc.nmcs == 0) {
83			(void) close(fd);
84			continue;
85		}
86
87		(void) close(fd);
88		(void) closedir(mcdir);
89
90		if (strlen(path) >= bufsz)
91			return (cmd_set_errno(ENOSPC));
92		(void) strcpy(buf, path);
93		return (0);
94	}
95
96	(void) closedir(mcdir);
97	return (cmd_set_errno(ENOENT));
98}
99
100static int
101ecache_find_device(char *buf, size_t bufsz)
102{
103	if (ecache_scan_dir("/dev/mc", "mc", buf, bufsz) != 0) {
104		/*
105		 * Yet more platform-specific hackery.  It's possible that the
106		 * /dev/mc links could be out of date, and thus we may not be
107		 * able to use any of them.  As a fallback, therefore, we're
108		 * going to search a couple of well-known locations in /devices.
109		 */
110		const char *dir = "/devices/ssm@0,0";
111
112		if (access(dir, R_OK) != 0)
113			dir = "/devices";
114
115		return (ecache_scan_dir(dir, "memory-controller", buf, bufsz));
116	}
117
118	return (0);
119}
120
121int
122cmd_ecache_init(void)
123{
124	return (ecache_find_device(cmd.cmd_ecache_dev,
125	    sizeof (cmd.cmd_ecache_dev)));
126}
127
128int
129cmd_ecache_flush(int cpuid)
130{
131	int fd;
132
133	if ((fd = open(cmd.cmd_ecache_dev, O_RDONLY)) < 0) {
134		if (errno != ENOENT)
135			return (-1); /* errno is set for us */
136
137		/*
138		 * A DR may have occurred, thus rendering our path invalid.
139		 * Try once to find another one.
140		 */
141		if (cmd_ecache_init() < 0 ||
142		    (fd = open(cmd.cmd_ecache_dev, O_RDONLY)) < 0)
143			return (-1); /* errno is set for us */
144	}
145
146	if (ioctl(fd, MCIOC_ECFLUSH, cpuid) < 0) {
147		int oserr = errno;
148		(void) close(fd);
149		return (cmd_set_errno(oserr));
150	}
151
152	(void) close(fd);
153	return (0);
154}
155