xref: /OK3568_Linux_fs/kernel/arch/ia64/kernel/salinfo.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * salinfo.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Creates entries in /proc/sal for various system features.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Copyright (c) 2003, 2006 Silicon Graphics, Inc.  All rights reserved.
8*4882a593Smuzhiyun  * Copyright (c) 2003 Hewlett-Packard Co
9*4882a593Smuzhiyun  *	Bjorn Helgaas <bjorn.helgaas@hp.com>
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * 10/30/2001	jbarnes@sgi.com		copied much of Stephane's palinfo
12*4882a593Smuzhiyun  *					code to create this file
13*4882a593Smuzhiyun  * Oct 23 2003	kaos@sgi.com
14*4882a593Smuzhiyun  *   Replace IPI with set_cpus_allowed() to read a record from the required cpu.
15*4882a593Smuzhiyun  *   Redesign salinfo log processing to separate interrupt and user space
16*4882a593Smuzhiyun  *   contexts.
17*4882a593Smuzhiyun  *   Cache the record across multi-block reads from user space.
18*4882a593Smuzhiyun  *   Support > 64 cpus.
19*4882a593Smuzhiyun  *   Delete module_exit and MOD_INC/DEC_COUNT, salinfo cannot be a module.
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  * Jan 28 2004	kaos@sgi.com
22*4882a593Smuzhiyun  *   Periodically check for outstanding MCA or INIT records.
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * Dec  5 2004	kaos@sgi.com
25*4882a593Smuzhiyun  *   Standardize which records are cleared automatically.
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  * Aug 18 2005	kaos@sgi.com
28*4882a593Smuzhiyun  *   mca.c may not pass a buffer, a NULL buffer just indicates that a new
29*4882a593Smuzhiyun  *   record is available in SAL.
30*4882a593Smuzhiyun  *   Replace some NR_CPUS by cpus_online, for hotplug cpu.
31*4882a593Smuzhiyun  *
32*4882a593Smuzhiyun  * Jan  5 2006        kaos@sgi.com
33*4882a593Smuzhiyun  *   Handle hotplug cpus coming online.
34*4882a593Smuzhiyun  *   Handle hotplug cpus going offline while they still have outstanding records.
35*4882a593Smuzhiyun  *   Use the cpu_* macros consistently.
36*4882a593Smuzhiyun  *   Replace the counting semaphore with a mutex and a test if the cpumask is non-empty.
37*4882a593Smuzhiyun  *   Modify the locking to make the test for "work to do" an atomic operation.
38*4882a593Smuzhiyun  */
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #include <linux/capability.h>
41*4882a593Smuzhiyun #include <linux/cpu.h>
42*4882a593Smuzhiyun #include <linux/types.h>
43*4882a593Smuzhiyun #include <linux/proc_fs.h>
44*4882a593Smuzhiyun #include <linux/seq_file.h>
45*4882a593Smuzhiyun #include <linux/module.h>
46*4882a593Smuzhiyun #include <linux/smp.h>
47*4882a593Smuzhiyun #include <linux/timer.h>
48*4882a593Smuzhiyun #include <linux/vmalloc.h>
49*4882a593Smuzhiyun #include <linux/semaphore.h>
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun #include <asm/sal.h>
52*4882a593Smuzhiyun #include <linux/uaccess.h>
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun MODULE_AUTHOR("Jesse Barnes <jbarnes@sgi.com>");
55*4882a593Smuzhiyun MODULE_DESCRIPTION("/proc interface to IA-64 SAL features");
56*4882a593Smuzhiyun MODULE_LICENSE("GPL");
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun typedef struct {
59*4882a593Smuzhiyun 	const char		*name;		/* name of the proc entry */
60*4882a593Smuzhiyun 	unsigned long           feature;        /* feature bit */
61*4882a593Smuzhiyun 	struct proc_dir_entry	*entry;		/* registered entry (removal) */
62*4882a593Smuzhiyun } salinfo_entry_t;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun /*
65*4882a593Smuzhiyun  * List {name,feature} pairs for every entry in /proc/sal/<feature>
66*4882a593Smuzhiyun  * that this module exports
67*4882a593Smuzhiyun  */
68*4882a593Smuzhiyun static const salinfo_entry_t salinfo_entries[]={
69*4882a593Smuzhiyun 	{ "bus_lock",           IA64_SAL_PLATFORM_FEATURE_BUS_LOCK, },
70*4882a593Smuzhiyun 	{ "irq_redirection",	IA64_SAL_PLATFORM_FEATURE_IRQ_REDIR_HINT, },
71*4882a593Smuzhiyun 	{ "ipi_redirection",	IA64_SAL_PLATFORM_FEATURE_IPI_REDIR_HINT, },
72*4882a593Smuzhiyun 	{ "itc_drift",		IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT, },
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun #define NR_SALINFO_ENTRIES ARRAY_SIZE(salinfo_entries)
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun static char *salinfo_log_name[] = {
78*4882a593Smuzhiyun 	"mca",
79*4882a593Smuzhiyun 	"init",
80*4882a593Smuzhiyun 	"cmc",
81*4882a593Smuzhiyun 	"cpe",
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun static struct proc_dir_entry *salinfo_proc_entries[
85*4882a593Smuzhiyun 	ARRAY_SIZE(salinfo_entries) +			/* /proc/sal/bus_lock */
86*4882a593Smuzhiyun 	ARRAY_SIZE(salinfo_log_name) +			/* /proc/sal/{mca,...} */
87*4882a593Smuzhiyun 	(2 * ARRAY_SIZE(salinfo_log_name)) +		/* /proc/sal/mca/{event,data} */
88*4882a593Smuzhiyun 	1];						/* /proc/sal */
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun /* Some records we get ourselves, some are accessed as saved data in buffers
91*4882a593Smuzhiyun  * that are owned by mca.c.
92*4882a593Smuzhiyun  */
93*4882a593Smuzhiyun struct salinfo_data_saved {
94*4882a593Smuzhiyun 	u8*			buffer;
95*4882a593Smuzhiyun 	u64			size;
96*4882a593Smuzhiyun 	u64			id;
97*4882a593Smuzhiyun 	int			cpu;
98*4882a593Smuzhiyun };
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun /* State transitions.  Actions are :-
101*4882a593Smuzhiyun  *   Write "read <cpunum>" to the data file.
102*4882a593Smuzhiyun  *   Write "clear <cpunum>" to the data file.
103*4882a593Smuzhiyun  *   Write "oemdata <cpunum> <offset> to the data file.
104*4882a593Smuzhiyun  *   Read from the data file.
105*4882a593Smuzhiyun  *   Close the data file.
106*4882a593Smuzhiyun  *
107*4882a593Smuzhiyun  * Start state is NO_DATA.
108*4882a593Smuzhiyun  *
109*4882a593Smuzhiyun  * NO_DATA
110*4882a593Smuzhiyun  *    write "read <cpunum>" -> NO_DATA or LOG_RECORD.
111*4882a593Smuzhiyun  *    write "clear <cpunum>" -> NO_DATA or LOG_RECORD.
112*4882a593Smuzhiyun  *    write "oemdata <cpunum> <offset> -> return -EINVAL.
113*4882a593Smuzhiyun  *    read data -> return EOF.
114*4882a593Smuzhiyun  *    close -> unchanged.  Free record areas.
115*4882a593Smuzhiyun  *
116*4882a593Smuzhiyun  * LOG_RECORD
117*4882a593Smuzhiyun  *    write "read <cpunum>" -> NO_DATA or LOG_RECORD.
118*4882a593Smuzhiyun  *    write "clear <cpunum>" -> NO_DATA or LOG_RECORD.
119*4882a593Smuzhiyun  *    write "oemdata <cpunum> <offset> -> format the oem data, goto OEMDATA.
120*4882a593Smuzhiyun  *    read data -> return the INIT/MCA/CMC/CPE record.
121*4882a593Smuzhiyun  *    close -> unchanged.  Keep record areas.
122*4882a593Smuzhiyun  *
123*4882a593Smuzhiyun  * OEMDATA
124*4882a593Smuzhiyun  *    write "read <cpunum>" -> NO_DATA or LOG_RECORD.
125*4882a593Smuzhiyun  *    write "clear <cpunum>" -> NO_DATA or LOG_RECORD.
126*4882a593Smuzhiyun  *    write "oemdata <cpunum> <offset> -> format the oem data, goto OEMDATA.
127*4882a593Smuzhiyun  *    read data -> return the formatted oemdata.
128*4882a593Smuzhiyun  *    close -> unchanged.  Keep record areas.
129*4882a593Smuzhiyun  *
130*4882a593Smuzhiyun  * Closing the data file does not change the state.  This allows shell scripts
131*4882a593Smuzhiyun  * to manipulate salinfo data, each shell redirection opens the file, does one
132*4882a593Smuzhiyun  * action then closes it again.  The record areas are only freed at close when
133*4882a593Smuzhiyun  * the state is NO_DATA.
134*4882a593Smuzhiyun  */
135*4882a593Smuzhiyun enum salinfo_state {
136*4882a593Smuzhiyun 	STATE_NO_DATA,
137*4882a593Smuzhiyun 	STATE_LOG_RECORD,
138*4882a593Smuzhiyun 	STATE_OEMDATA,
139*4882a593Smuzhiyun };
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun struct salinfo_data {
142*4882a593Smuzhiyun 	cpumask_t		cpu_event;	/* which cpus have outstanding events */
143*4882a593Smuzhiyun 	wait_queue_head_t	read_wait;
144*4882a593Smuzhiyun 	u8			*log_buffer;
145*4882a593Smuzhiyun 	u64			log_size;
146*4882a593Smuzhiyun 	u8			*oemdata;	/* decoded oem data */
147*4882a593Smuzhiyun 	u64			oemdata_size;
148*4882a593Smuzhiyun 	int			open;		/* single-open to prevent races */
149*4882a593Smuzhiyun 	u8			type;
150*4882a593Smuzhiyun 	u8			saved_num;	/* using a saved record? */
151*4882a593Smuzhiyun 	enum salinfo_state	state :8;	/* processing state */
152*4882a593Smuzhiyun 	u8			padding;
153*4882a593Smuzhiyun 	int			cpu_check;	/* next CPU to check */
154*4882a593Smuzhiyun 	struct salinfo_data_saved data_saved[5];/* save last 5 records from mca.c, must be < 255 */
155*4882a593Smuzhiyun };
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun static struct salinfo_data salinfo_data[ARRAY_SIZE(salinfo_log_name)];
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun static DEFINE_SPINLOCK(data_lock);
160*4882a593Smuzhiyun static DEFINE_SPINLOCK(data_saved_lock);
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun /** salinfo_platform_oemdata - optional callback to decode oemdata from an error
163*4882a593Smuzhiyun  * record.
164*4882a593Smuzhiyun  * @sect_header: pointer to the start of the section to decode.
165*4882a593Smuzhiyun  * @oemdata: returns vmalloc area containing the decoded output.
166*4882a593Smuzhiyun  * @oemdata_size: returns length of decoded output (strlen).
167*4882a593Smuzhiyun  *
168*4882a593Smuzhiyun  * Description: If user space asks for oem data to be decoded by the kernel
169*4882a593Smuzhiyun  * and/or prom and the platform has set salinfo_platform_oemdata to the address
170*4882a593Smuzhiyun  * of a platform specific routine then call that routine.  salinfo_platform_oemdata
171*4882a593Smuzhiyun  * vmalloc's and formats its output area, returning the address of the text
172*4882a593Smuzhiyun  * and its strlen.  Returns 0 for success, -ve for error.  The callback is
173*4882a593Smuzhiyun  * invoked on the cpu that generated the error record.
174*4882a593Smuzhiyun  */
175*4882a593Smuzhiyun int (*salinfo_platform_oemdata)(const u8 *sect_header, u8 **oemdata, u64 *oemdata_size);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun struct salinfo_platform_oemdata_parms {
178*4882a593Smuzhiyun 	const u8 *efi_guid;
179*4882a593Smuzhiyun 	u8 **oemdata;
180*4882a593Smuzhiyun 	u64 *oemdata_size;
181*4882a593Smuzhiyun };
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun static long
salinfo_platform_oemdata_cpu(void * context)184*4882a593Smuzhiyun salinfo_platform_oemdata_cpu(void *context)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun 	struct salinfo_platform_oemdata_parms *parms = context;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	return salinfo_platform_oemdata(parms->efi_guid, parms->oemdata, parms->oemdata_size);
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun static void
shift1_data_saved(struct salinfo_data * data,int shift)192*4882a593Smuzhiyun shift1_data_saved (struct salinfo_data *data, int shift)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun 	memcpy(data->data_saved+shift, data->data_saved+shift+1,
195*4882a593Smuzhiyun 	       (ARRAY_SIZE(data->data_saved) - (shift+1)) * sizeof(data->data_saved[0]));
196*4882a593Smuzhiyun 	memset(data->data_saved + ARRAY_SIZE(data->data_saved) - 1, 0,
197*4882a593Smuzhiyun 	       sizeof(data->data_saved[0]));
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun /* This routine is invoked in interrupt context.  Note: mca.c enables
201*4882a593Smuzhiyun  * interrupts before calling this code for CMC/CPE.  MCA and INIT events are
202*4882a593Smuzhiyun  * not irq safe, do not call any routines that use spinlocks, they may deadlock.
203*4882a593Smuzhiyun  * MCA and INIT records are recorded, a timer event will look for any
204*4882a593Smuzhiyun  * outstanding events and wake up the user space code.
205*4882a593Smuzhiyun  *
206*4882a593Smuzhiyun  * The buffer passed from mca.c points to the output from ia64_log_get. This is
207*4882a593Smuzhiyun  * a persistent buffer but its contents can change between the interrupt and
208*4882a593Smuzhiyun  * when user space processes the record.  Save the record id to identify
209*4882a593Smuzhiyun  * changes.  If the buffer is NULL then just update the bitmap.
210*4882a593Smuzhiyun  */
211*4882a593Smuzhiyun void
salinfo_log_wakeup(int type,u8 * buffer,u64 size,int irqsafe)212*4882a593Smuzhiyun salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun 	struct salinfo_data *data = salinfo_data + type;
215*4882a593Smuzhiyun 	struct salinfo_data_saved *data_saved;
216*4882a593Smuzhiyun 	unsigned long flags = 0;
217*4882a593Smuzhiyun 	int i;
218*4882a593Smuzhiyun 	int saved_size = ARRAY_SIZE(data->data_saved);
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	BUG_ON(type >= ARRAY_SIZE(salinfo_log_name));
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	if (irqsafe)
223*4882a593Smuzhiyun 		spin_lock_irqsave(&data_saved_lock, flags);
224*4882a593Smuzhiyun 	if (buffer) {
225*4882a593Smuzhiyun 		for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) {
226*4882a593Smuzhiyun 			if (!data_saved->buffer)
227*4882a593Smuzhiyun 				break;
228*4882a593Smuzhiyun 		}
229*4882a593Smuzhiyun 		if (i == saved_size) {
230*4882a593Smuzhiyun 			if (!data->saved_num) {
231*4882a593Smuzhiyun 				shift1_data_saved(data, 0);
232*4882a593Smuzhiyun 				data_saved = data->data_saved + saved_size - 1;
233*4882a593Smuzhiyun 			} else
234*4882a593Smuzhiyun 				data_saved = NULL;
235*4882a593Smuzhiyun 		}
236*4882a593Smuzhiyun 		if (data_saved) {
237*4882a593Smuzhiyun 			data_saved->cpu = smp_processor_id();
238*4882a593Smuzhiyun 			data_saved->id = ((sal_log_record_header_t *)buffer)->id;
239*4882a593Smuzhiyun 			data_saved->size = size;
240*4882a593Smuzhiyun 			data_saved->buffer = buffer;
241*4882a593Smuzhiyun 		}
242*4882a593Smuzhiyun 	}
243*4882a593Smuzhiyun 	cpumask_set_cpu(smp_processor_id(), &data->cpu_event);
244*4882a593Smuzhiyun 	if (irqsafe) {
245*4882a593Smuzhiyun 		wake_up_interruptible(&data->read_wait);
246*4882a593Smuzhiyun 		spin_unlock_irqrestore(&data_saved_lock, flags);
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun /* Check for outstanding MCA/INIT records every minute (arbitrary) */
251*4882a593Smuzhiyun #define SALINFO_TIMER_DELAY (60*HZ)
252*4882a593Smuzhiyun static struct timer_list salinfo_timer;
253*4882a593Smuzhiyun extern void ia64_mlogbuf_dump(void);
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun static void
salinfo_timeout_check(struct salinfo_data * data)256*4882a593Smuzhiyun salinfo_timeout_check(struct salinfo_data *data)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun 	if (!data->open)
259*4882a593Smuzhiyun 		return;
260*4882a593Smuzhiyun 	if (!cpumask_empty(&data->cpu_event))
261*4882a593Smuzhiyun 		wake_up_interruptible(&data->read_wait);
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun static void
salinfo_timeout(struct timer_list * unused)265*4882a593Smuzhiyun salinfo_timeout(struct timer_list *unused)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun 	ia64_mlogbuf_dump();
268*4882a593Smuzhiyun 	salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_MCA);
269*4882a593Smuzhiyun 	salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_INIT);
270*4882a593Smuzhiyun 	salinfo_timer.expires = jiffies + SALINFO_TIMER_DELAY;
271*4882a593Smuzhiyun 	add_timer(&salinfo_timer);
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun static int
salinfo_event_open(struct inode * inode,struct file * file)275*4882a593Smuzhiyun salinfo_event_open(struct inode *inode, struct file *file)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	if (!capable(CAP_SYS_ADMIN))
278*4882a593Smuzhiyun 		return -EPERM;
279*4882a593Smuzhiyun 	return 0;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun static ssize_t
salinfo_event_read(struct file * file,char __user * buffer,size_t count,loff_t * ppos)283*4882a593Smuzhiyun salinfo_event_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun 	struct salinfo_data *data = PDE_DATA(file_inode(file));
286*4882a593Smuzhiyun 	char cmd[32];
287*4882a593Smuzhiyun 	size_t size;
288*4882a593Smuzhiyun 	int i, n, cpu = -1;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun retry:
291*4882a593Smuzhiyun 	if (cpumask_empty(&data->cpu_event)) {
292*4882a593Smuzhiyun 		if (file->f_flags & O_NONBLOCK)
293*4882a593Smuzhiyun 			return -EAGAIN;
294*4882a593Smuzhiyun 		if (wait_event_interruptible(data->read_wait,
295*4882a593Smuzhiyun 					     !cpumask_empty(&data->cpu_event)))
296*4882a593Smuzhiyun 			return -EINTR;
297*4882a593Smuzhiyun 	}
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	n = data->cpu_check;
300*4882a593Smuzhiyun 	for (i = 0; i < nr_cpu_ids; i++) {
301*4882a593Smuzhiyun 		if (cpumask_test_cpu(n, &data->cpu_event)) {
302*4882a593Smuzhiyun 			if (!cpu_online(n)) {
303*4882a593Smuzhiyun 				cpumask_clear_cpu(n, &data->cpu_event);
304*4882a593Smuzhiyun 				continue;
305*4882a593Smuzhiyun 			}
306*4882a593Smuzhiyun 			cpu = n;
307*4882a593Smuzhiyun 			break;
308*4882a593Smuzhiyun 		}
309*4882a593Smuzhiyun 		if (++n == nr_cpu_ids)
310*4882a593Smuzhiyun 			n = 0;
311*4882a593Smuzhiyun 	}
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	if (cpu == -1)
314*4882a593Smuzhiyun 		goto retry;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	ia64_mlogbuf_dump();
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	/* for next read, start checking at next CPU */
319*4882a593Smuzhiyun 	data->cpu_check = cpu;
320*4882a593Smuzhiyun 	if (++data->cpu_check == nr_cpu_ids)
321*4882a593Smuzhiyun 		data->cpu_check = 0;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	snprintf(cmd, sizeof(cmd), "read %d\n", cpu);
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	size = strlen(cmd);
326*4882a593Smuzhiyun 	if (size > count)
327*4882a593Smuzhiyun 		size = count;
328*4882a593Smuzhiyun 	if (copy_to_user(buffer, cmd, size))
329*4882a593Smuzhiyun 		return -EFAULT;
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	return size;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun static const struct proc_ops salinfo_event_proc_ops = {
335*4882a593Smuzhiyun 	.proc_open	= salinfo_event_open,
336*4882a593Smuzhiyun 	.proc_read	= salinfo_event_read,
337*4882a593Smuzhiyun 	.proc_lseek	= noop_llseek,
338*4882a593Smuzhiyun };
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun static int
salinfo_log_open(struct inode * inode,struct file * file)341*4882a593Smuzhiyun salinfo_log_open(struct inode *inode, struct file *file)
342*4882a593Smuzhiyun {
343*4882a593Smuzhiyun 	struct salinfo_data *data = PDE_DATA(inode);
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	if (!capable(CAP_SYS_ADMIN))
346*4882a593Smuzhiyun 		return -EPERM;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	spin_lock(&data_lock);
349*4882a593Smuzhiyun 	if (data->open) {
350*4882a593Smuzhiyun 		spin_unlock(&data_lock);
351*4882a593Smuzhiyun 		return -EBUSY;
352*4882a593Smuzhiyun 	}
353*4882a593Smuzhiyun 	data->open = 1;
354*4882a593Smuzhiyun 	spin_unlock(&data_lock);
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	if (data->state == STATE_NO_DATA &&
357*4882a593Smuzhiyun 	    !(data->log_buffer = vmalloc(ia64_sal_get_state_info_size(data->type)))) {
358*4882a593Smuzhiyun 		data->open = 0;
359*4882a593Smuzhiyun 		return -ENOMEM;
360*4882a593Smuzhiyun 	}
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	return 0;
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun static int
salinfo_log_release(struct inode * inode,struct file * file)366*4882a593Smuzhiyun salinfo_log_release(struct inode *inode, struct file *file)
367*4882a593Smuzhiyun {
368*4882a593Smuzhiyun 	struct salinfo_data *data = PDE_DATA(inode);
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	if (data->state == STATE_NO_DATA) {
371*4882a593Smuzhiyun 		vfree(data->log_buffer);
372*4882a593Smuzhiyun 		vfree(data->oemdata);
373*4882a593Smuzhiyun 		data->log_buffer = NULL;
374*4882a593Smuzhiyun 		data->oemdata = NULL;
375*4882a593Smuzhiyun 	}
376*4882a593Smuzhiyun 	spin_lock(&data_lock);
377*4882a593Smuzhiyun 	data->open = 0;
378*4882a593Smuzhiyun 	spin_unlock(&data_lock);
379*4882a593Smuzhiyun 	return 0;
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun static long
salinfo_log_read_cpu(void * context)383*4882a593Smuzhiyun salinfo_log_read_cpu(void *context)
384*4882a593Smuzhiyun {
385*4882a593Smuzhiyun 	struct salinfo_data *data = context;
386*4882a593Smuzhiyun 	sal_log_record_header_t *rh;
387*4882a593Smuzhiyun 	data->log_size = ia64_sal_get_state_info(data->type, (u64 *) data->log_buffer);
388*4882a593Smuzhiyun 	rh = (sal_log_record_header_t *)(data->log_buffer);
389*4882a593Smuzhiyun 	/* Clear corrected errors as they are read from SAL */
390*4882a593Smuzhiyun 	if (rh->severity == sal_log_severity_corrected)
391*4882a593Smuzhiyun 		ia64_sal_clear_state_info(data->type);
392*4882a593Smuzhiyun 	return 0;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun static void
salinfo_log_new_read(int cpu,struct salinfo_data * data)396*4882a593Smuzhiyun salinfo_log_new_read(int cpu, struct salinfo_data *data)
397*4882a593Smuzhiyun {
398*4882a593Smuzhiyun 	struct salinfo_data_saved *data_saved;
399*4882a593Smuzhiyun 	unsigned long flags;
400*4882a593Smuzhiyun 	int i;
401*4882a593Smuzhiyun 	int saved_size = ARRAY_SIZE(data->data_saved);
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	data->saved_num = 0;
404*4882a593Smuzhiyun 	spin_lock_irqsave(&data_saved_lock, flags);
405*4882a593Smuzhiyun retry:
406*4882a593Smuzhiyun 	for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) {
407*4882a593Smuzhiyun 		if (data_saved->buffer && data_saved->cpu == cpu) {
408*4882a593Smuzhiyun 			sal_log_record_header_t *rh = (sal_log_record_header_t *)(data_saved->buffer);
409*4882a593Smuzhiyun 			data->log_size = data_saved->size;
410*4882a593Smuzhiyun 			memcpy(data->log_buffer, rh, data->log_size);
411*4882a593Smuzhiyun 			barrier();	/* id check must not be moved */
412*4882a593Smuzhiyun 			if (rh->id == data_saved->id) {
413*4882a593Smuzhiyun 				data->saved_num = i+1;
414*4882a593Smuzhiyun 				break;
415*4882a593Smuzhiyun 			}
416*4882a593Smuzhiyun 			/* saved record changed by mca.c since interrupt, discard it */
417*4882a593Smuzhiyun 			shift1_data_saved(data, i);
418*4882a593Smuzhiyun 			goto retry;
419*4882a593Smuzhiyun 		}
420*4882a593Smuzhiyun 	}
421*4882a593Smuzhiyun 	spin_unlock_irqrestore(&data_saved_lock, flags);
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	if (!data->saved_num)
424*4882a593Smuzhiyun 		work_on_cpu_safe(cpu, salinfo_log_read_cpu, data);
425*4882a593Smuzhiyun 	if (!data->log_size) {
426*4882a593Smuzhiyun 		data->state = STATE_NO_DATA;
427*4882a593Smuzhiyun 		cpumask_clear_cpu(cpu, &data->cpu_event);
428*4882a593Smuzhiyun 	} else {
429*4882a593Smuzhiyun 		data->state = STATE_LOG_RECORD;
430*4882a593Smuzhiyun 	}
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun static ssize_t
salinfo_log_read(struct file * file,char __user * buffer,size_t count,loff_t * ppos)434*4882a593Smuzhiyun salinfo_log_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
435*4882a593Smuzhiyun {
436*4882a593Smuzhiyun 	struct salinfo_data *data = PDE_DATA(file_inode(file));
437*4882a593Smuzhiyun 	u8 *buf;
438*4882a593Smuzhiyun 	u64 bufsize;
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	if (data->state == STATE_LOG_RECORD) {
441*4882a593Smuzhiyun 		buf = data->log_buffer;
442*4882a593Smuzhiyun 		bufsize = data->log_size;
443*4882a593Smuzhiyun 	} else if (data->state == STATE_OEMDATA) {
444*4882a593Smuzhiyun 		buf = data->oemdata;
445*4882a593Smuzhiyun 		bufsize = data->oemdata_size;
446*4882a593Smuzhiyun 	} else {
447*4882a593Smuzhiyun 		buf = NULL;
448*4882a593Smuzhiyun 		bufsize = 0;
449*4882a593Smuzhiyun 	}
450*4882a593Smuzhiyun 	return simple_read_from_buffer(buffer, count, ppos, buf, bufsize);
451*4882a593Smuzhiyun }
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun static long
salinfo_log_clear_cpu(void * context)454*4882a593Smuzhiyun salinfo_log_clear_cpu(void *context)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun 	struct salinfo_data *data = context;
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	ia64_sal_clear_state_info(data->type);
459*4882a593Smuzhiyun 	return 0;
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun static int
salinfo_log_clear(struct salinfo_data * data,int cpu)463*4882a593Smuzhiyun salinfo_log_clear(struct salinfo_data *data, int cpu)
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun 	sal_log_record_header_t *rh;
466*4882a593Smuzhiyun 	unsigned long flags;
467*4882a593Smuzhiyun 	spin_lock_irqsave(&data_saved_lock, flags);
468*4882a593Smuzhiyun 	data->state = STATE_NO_DATA;
469*4882a593Smuzhiyun 	if (!cpumask_test_cpu(cpu, &data->cpu_event)) {
470*4882a593Smuzhiyun 		spin_unlock_irqrestore(&data_saved_lock, flags);
471*4882a593Smuzhiyun 		return 0;
472*4882a593Smuzhiyun 	}
473*4882a593Smuzhiyun 	cpumask_clear_cpu(cpu, &data->cpu_event);
474*4882a593Smuzhiyun 	if (data->saved_num) {
475*4882a593Smuzhiyun 		shift1_data_saved(data, data->saved_num - 1);
476*4882a593Smuzhiyun 		data->saved_num = 0;
477*4882a593Smuzhiyun 	}
478*4882a593Smuzhiyun 	spin_unlock_irqrestore(&data_saved_lock, flags);
479*4882a593Smuzhiyun 	rh = (sal_log_record_header_t *)(data->log_buffer);
480*4882a593Smuzhiyun 	/* Corrected errors have already been cleared from SAL */
481*4882a593Smuzhiyun 	if (rh->severity != sal_log_severity_corrected)
482*4882a593Smuzhiyun 		work_on_cpu_safe(cpu, salinfo_log_clear_cpu, data);
483*4882a593Smuzhiyun 	/* clearing a record may make a new record visible */
484*4882a593Smuzhiyun 	salinfo_log_new_read(cpu, data);
485*4882a593Smuzhiyun 	if (data->state == STATE_LOG_RECORD) {
486*4882a593Smuzhiyun 		spin_lock_irqsave(&data_saved_lock, flags);
487*4882a593Smuzhiyun 		cpumask_set_cpu(cpu, &data->cpu_event);
488*4882a593Smuzhiyun 		wake_up_interruptible(&data->read_wait);
489*4882a593Smuzhiyun 		spin_unlock_irqrestore(&data_saved_lock, flags);
490*4882a593Smuzhiyun 	}
491*4882a593Smuzhiyun 	return 0;
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun static ssize_t
salinfo_log_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)495*4882a593Smuzhiyun salinfo_log_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
496*4882a593Smuzhiyun {
497*4882a593Smuzhiyun 	struct salinfo_data *data = PDE_DATA(file_inode(file));
498*4882a593Smuzhiyun 	char cmd[32];
499*4882a593Smuzhiyun 	size_t size;
500*4882a593Smuzhiyun 	u32 offset;
501*4882a593Smuzhiyun 	int cpu;
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	size = sizeof(cmd);
504*4882a593Smuzhiyun 	if (count < size)
505*4882a593Smuzhiyun 		size = count;
506*4882a593Smuzhiyun 	if (copy_from_user(cmd, buffer, size))
507*4882a593Smuzhiyun 		return -EFAULT;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	if (sscanf(cmd, "read %d", &cpu) == 1) {
510*4882a593Smuzhiyun 		salinfo_log_new_read(cpu, data);
511*4882a593Smuzhiyun 	} else if (sscanf(cmd, "clear %d", &cpu) == 1) {
512*4882a593Smuzhiyun 		int ret;
513*4882a593Smuzhiyun 		if ((ret = salinfo_log_clear(data, cpu)))
514*4882a593Smuzhiyun 			count = ret;
515*4882a593Smuzhiyun 	} else if (sscanf(cmd, "oemdata %d %d", &cpu, &offset) == 2) {
516*4882a593Smuzhiyun 		if (data->state != STATE_LOG_RECORD && data->state != STATE_OEMDATA)
517*4882a593Smuzhiyun 			return -EINVAL;
518*4882a593Smuzhiyun 		if (offset > data->log_size - sizeof(efi_guid_t))
519*4882a593Smuzhiyun 			return -EINVAL;
520*4882a593Smuzhiyun 		data->state = STATE_OEMDATA;
521*4882a593Smuzhiyun 		if (salinfo_platform_oemdata) {
522*4882a593Smuzhiyun 			struct salinfo_platform_oemdata_parms parms = {
523*4882a593Smuzhiyun 				.efi_guid = data->log_buffer + offset,
524*4882a593Smuzhiyun 				.oemdata = &data->oemdata,
525*4882a593Smuzhiyun 				.oemdata_size = &data->oemdata_size
526*4882a593Smuzhiyun 			};
527*4882a593Smuzhiyun 			count = work_on_cpu_safe(cpu, salinfo_platform_oemdata_cpu,
528*4882a593Smuzhiyun 						 &parms);
529*4882a593Smuzhiyun 		} else
530*4882a593Smuzhiyun 			data->oemdata_size = 0;
531*4882a593Smuzhiyun 	} else
532*4882a593Smuzhiyun 		return -EINVAL;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	return count;
535*4882a593Smuzhiyun }
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun static const struct proc_ops salinfo_data_proc_ops = {
538*4882a593Smuzhiyun 	.proc_open	= salinfo_log_open,
539*4882a593Smuzhiyun 	.proc_release	= salinfo_log_release,
540*4882a593Smuzhiyun 	.proc_read	= salinfo_log_read,
541*4882a593Smuzhiyun 	.proc_write	= salinfo_log_write,
542*4882a593Smuzhiyun 	.proc_lseek	= default_llseek,
543*4882a593Smuzhiyun };
544*4882a593Smuzhiyun 
salinfo_cpu_online(unsigned int cpu)545*4882a593Smuzhiyun static int salinfo_cpu_online(unsigned int cpu)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	unsigned int i, end = ARRAY_SIZE(salinfo_data);
548*4882a593Smuzhiyun 	struct salinfo_data *data;
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	spin_lock_irq(&data_saved_lock);
551*4882a593Smuzhiyun 	for (i = 0, data = salinfo_data; i < end; ++i, ++data) {
552*4882a593Smuzhiyun 		cpumask_set_cpu(cpu, &data->cpu_event);
553*4882a593Smuzhiyun 		wake_up_interruptible(&data->read_wait);
554*4882a593Smuzhiyun 	}
555*4882a593Smuzhiyun 	spin_unlock_irq(&data_saved_lock);
556*4882a593Smuzhiyun 	return 0;
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun 
salinfo_cpu_pre_down(unsigned int cpu)559*4882a593Smuzhiyun static int salinfo_cpu_pre_down(unsigned int cpu)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun 	unsigned int i, end = ARRAY_SIZE(salinfo_data);
562*4882a593Smuzhiyun 	struct salinfo_data *data;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	spin_lock_irq(&data_saved_lock);
565*4882a593Smuzhiyun 	for (i = 0, data = salinfo_data; i < end; ++i, ++data) {
566*4882a593Smuzhiyun 		struct salinfo_data_saved *data_saved;
567*4882a593Smuzhiyun 		int j = ARRAY_SIZE(data->data_saved) - 1;
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 		for (data_saved = data->data_saved + j; j >= 0;
570*4882a593Smuzhiyun 		     --j, --data_saved) {
571*4882a593Smuzhiyun 			if (data_saved->buffer && data_saved->cpu == cpu)
572*4882a593Smuzhiyun 				shift1_data_saved(data, j);
573*4882a593Smuzhiyun 		}
574*4882a593Smuzhiyun 		cpumask_clear_cpu(cpu, &data->cpu_event);
575*4882a593Smuzhiyun 	}
576*4882a593Smuzhiyun 	spin_unlock_irq(&data_saved_lock);
577*4882a593Smuzhiyun 	return 0;
578*4882a593Smuzhiyun }
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun /*
581*4882a593Smuzhiyun  * 'data' contains an integer that corresponds to the feature we're
582*4882a593Smuzhiyun  * testing
583*4882a593Smuzhiyun  */
proc_salinfo_show(struct seq_file * m,void * v)584*4882a593Smuzhiyun static int proc_salinfo_show(struct seq_file *m, void *v)
585*4882a593Smuzhiyun {
586*4882a593Smuzhiyun 	unsigned long data = (unsigned long)v;
587*4882a593Smuzhiyun 	seq_puts(m, (sal_platform_features & data) ? "1\n" : "0\n");
588*4882a593Smuzhiyun 	return 0;
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun static int __init
salinfo_init(void)592*4882a593Smuzhiyun salinfo_init(void)
593*4882a593Smuzhiyun {
594*4882a593Smuzhiyun 	struct proc_dir_entry *salinfo_dir; /* /proc/sal dir entry */
595*4882a593Smuzhiyun 	struct proc_dir_entry **sdir = salinfo_proc_entries; /* keeps track of every entry */
596*4882a593Smuzhiyun 	struct proc_dir_entry *dir, *entry;
597*4882a593Smuzhiyun 	struct salinfo_data *data;
598*4882a593Smuzhiyun 	int i;
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 	salinfo_dir = proc_mkdir("sal", NULL);
601*4882a593Smuzhiyun 	if (!salinfo_dir)
602*4882a593Smuzhiyun 		return 0;
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	for (i=0; i < NR_SALINFO_ENTRIES; i++) {
605*4882a593Smuzhiyun 		/* pass the feature bit in question as misc data */
606*4882a593Smuzhiyun 		*sdir++ = proc_create_single_data(salinfo_entries[i].name, 0,
607*4882a593Smuzhiyun 				salinfo_dir, proc_salinfo_show,
608*4882a593Smuzhiyun 				(void *)salinfo_entries[i].feature);
609*4882a593Smuzhiyun 	}
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(salinfo_log_name); i++) {
612*4882a593Smuzhiyun 		data = salinfo_data + i;
613*4882a593Smuzhiyun 		data->type = i;
614*4882a593Smuzhiyun 		init_waitqueue_head(&data->read_wait);
615*4882a593Smuzhiyun 		dir = proc_mkdir(salinfo_log_name[i], salinfo_dir);
616*4882a593Smuzhiyun 		if (!dir)
617*4882a593Smuzhiyun 			continue;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 		entry = proc_create_data("event", S_IRUSR, dir,
620*4882a593Smuzhiyun 					 &salinfo_event_proc_ops, data);
621*4882a593Smuzhiyun 		if (!entry)
622*4882a593Smuzhiyun 			continue;
623*4882a593Smuzhiyun 		*sdir++ = entry;
624*4882a593Smuzhiyun 
625*4882a593Smuzhiyun 		entry = proc_create_data("data", S_IRUSR | S_IWUSR, dir,
626*4882a593Smuzhiyun 					 &salinfo_data_proc_ops, data);
627*4882a593Smuzhiyun 		if (!entry)
628*4882a593Smuzhiyun 			continue;
629*4882a593Smuzhiyun 		*sdir++ = entry;
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun 		*sdir++ = dir;
632*4882a593Smuzhiyun 	}
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	*sdir++ = salinfo_dir;
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	timer_setup(&salinfo_timer, salinfo_timeout, 0);
637*4882a593Smuzhiyun 	salinfo_timer.expires = jiffies + SALINFO_TIMER_DELAY;
638*4882a593Smuzhiyun 	add_timer(&salinfo_timer);
639*4882a593Smuzhiyun 
640*4882a593Smuzhiyun 	i = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "ia64/salinfo:online",
641*4882a593Smuzhiyun 			      salinfo_cpu_online, salinfo_cpu_pre_down);
642*4882a593Smuzhiyun 	WARN_ON(i < 0);
643*4882a593Smuzhiyun 	return 0;
644*4882a593Smuzhiyun }
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun module_init(salinfo_init);
647