Skip to content

Commit 2994650

Browse files
author
Fox Snowpatch
committed
1 parent bcf2bbe commit 2994650

File tree

3 files changed

+371
-1
lines changed

3 files changed

+371
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2+
#ifndef _UAPI_PAPR_PLATFORM_DUMP_H_
3+
#define _UAPI_PAPR_PLATFORM_DUMP_H_
4+
5+
#include <asm/ioctl.h>
6+
#include <asm/papr-miscdev.h>
7+
8+
/*
9+
* ioctl for /dev/papr-platform-dump. Returns a VPD handle fd corresponding to
10+
* the location code.
11+
*/
12+
#define PAPR_PLATFORM_DUMP_IOC_CREATE_HANDLE _IOW(PAPR_MISCDEV_IOC_ID, 3, __u64)
13+
14+
#endif /* _UAPI_PAPR_PLATFORM_DUMP_H_ */

arch/powerpc/platforms/pseries/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ ccflags-$(CONFIG_PPC_PSERIES_DEBUG) += -DDEBUG
33

44
obj-y := lpar.o hvCall.o nvram.o reconfig.o \
55
of_helpers.o rtas-work-area.o papr-sysparm.o \
6-
papr-vpd.o \
6+
papr-vpd.o papr-platform-dump.o \
77
setup.o iommu.o event_sources.o ras.o \
88
firmware.o power.o dlpar.o mobility.o rng.o \
99
pci.o pci_dlpar.o eeh_pseries.o msi.o \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#define pr_fmt(fmt) "papr-platform-dump: " fmt
4+
5+
#include <linux/anon_inodes.h>
6+
#include <linux/file.h>
7+
#include <linux/fs.h>
8+
#include <linux/init.h>
9+
#include <linux/kernel.h>
10+
#include <linux/miscdevice.h>
11+
#include <asm/machdep.h>
12+
#include <asm/rtas-work-area.h>
13+
#include <asm/rtas.h>
14+
#include <uapi/asm/papr-platform-dump.h>
15+
16+
/*
17+
* Function-specific return values for ibm,platform-dump, derived from
18+
* PAPR+ v2.13 7.3.3.4.1 "ibm,platform-dump RTAS Call".
19+
*/
20+
#define RTAS_IBM_PLATFORM_DUMP_COMPLETE 0 /* Complete dump retrieved. */
21+
#define RTAS_IBM_PLATFORM_DUMP_CONTINUE 1 /* Continue dump */
22+
#define RTAS_NOT_AUTHORIZED -9002 /* Not Authorized */
23+
24+
#define RTAS_IBM_PLATFORM_DUMP_START 2 /* Linux status to start dump */
25+
26+
/**
27+
* struct ibm_platform_dump_params - Parameters (in and out) for
28+
* ibm,platform-dump
29+
* @work_area: In: work area buffer for results.
30+
* @buf_length: In: work area buffer length in bytes
31+
* @dump_tag: In: Dump_Tag representing an id of the dump being processed
32+
* @sequence: In: Sequence number. Out: Next sequence number.
33+
* @written: Out: Bytes written by ibm,platform-dump to @work_area.
34+
* @status: Out: RTAS call status.
35+
* @list: Maintain the list of dumps are in progress. Can retrieve
36+
* multiple dumps with different dump IDs at the same time,
37+
* but not with the same dump ID. This list is used to
38+
* determine whether the dump for the same ID is in progress.
39+
*/
40+
struct ibm_platform_dump_params {
41+
struct rtas_work_area *work_area;
42+
u32 buf_length;
43+
u32 dump_tag_hi;
44+
u32 dump_tag_lo;
45+
u32 sequence_hi;
46+
u32 sequence_lo;
47+
u32 bytes_ret_hi;
48+
u32 bytes_ret_lo;
49+
s32 status;
50+
struct list_head list;
51+
};
52+
53+
/*
54+
* Multiple dumps with different dump IDs can be retrieved at the same
55+
* time, but not with dame dump ID. platform_dump_list_mutex and
56+
* platform_dump_list are used to prevent this behavior.
57+
*/
58+
static DEFINE_MUTEX(platform_dump_list_mutex);
59+
static LIST_HEAD(platform_dump_list);
60+
61+
/**
62+
* rtas_ibm_platform_dump() - Call ibm,platform-dump to fill a work area
63+
* buffer.
64+
* @params: See &struct ibm_platform_dump_params.
65+
* @buf_addr: Address of dump buffer (work_area)
66+
* @buf_length: Length of the buffer in bytes (min. 1024)
67+
*
68+
* Calls ibm,platform-dump until it errors or successfully deposits data
69+
* into the supplied work area. Handles RTAS retry statuses. Maps RTAS
70+
* error statuses to reasonable errno values.
71+
*
72+
* Can request multiple dumps with different dump IDs at the same time,
73+
* but not with the same dump ID which is prevented with the check in
74+
* the ioctl code (papr_platform_dump_create_handle()).
75+
*
76+
* The caller should inspect @params.status to determine whether more
77+
* calls are needed to complete the sequence.
78+
*
79+
* Context: May sleep.
80+
* Return: -ve on error, 0 for dump complete and 1 for continue dump
81+
*/
82+
static int rtas_ibm_platform_dump(struct ibm_platform_dump_params *params,
83+
phys_addr_t buf_addr, u32 buf_length)
84+
{
85+
u32 rets[4];
86+
s32 fwrc;
87+
int ret = 0;
88+
89+
do {
90+
fwrc = rtas_call(rtas_function_token(RTAS_FN_IBM_PLATFORM_DUMP),
91+
6, 5,
92+
rets,
93+
params->dump_tag_hi,
94+
params->dump_tag_lo,
95+
params->sequence_hi,
96+
params->sequence_lo,
97+
buf_addr,
98+
buf_length);
99+
} while (rtas_busy_delay(fwrc));
100+
101+
switch (fwrc) {
102+
case RTAS_HARDWARE_ERROR:
103+
ret = -EIO;
104+
break;
105+
case RTAS_NOT_AUTHORIZED:
106+
ret = -EPERM;
107+
break;
108+
case RTAS_IBM_PLATFORM_DUMP_CONTINUE:
109+
case RTAS_IBM_PLATFORM_DUMP_COMPLETE:
110+
params->sequence_hi = rets[0];
111+
params->sequence_lo = rets[1];
112+
params->bytes_ret_hi = rets[2];
113+
params->bytes_ret_lo = rets[3];
114+
break;
115+
default:
116+
ret = -EIO;
117+
pr_err_ratelimited("unexpected ibm,platform-dump status %d\n",
118+
fwrc);
119+
break;
120+
}
121+
122+
params->status = fwrc;
123+
return ret;
124+
}
125+
126+
/*
127+
* Platform dump is used with multiple RTAS calls to retrieve the
128+
* complete dump for the provided dump ID. Once the complete dump is
129+
* retrieved, the hypervisor returns dump complete status (0) for the
130+
* last RTAS call and expects the caller issues one more HCALL with
131+
* NULL buffer to invalidate the dump so that the hypervisor can remove
132+
* the dump.
133+
*
134+
* After the specific dump is invalidated in the hypervisor, expect the
135+
* dump complete status for the new sequence - the user space initiates
136+
* new request for the same dump ID.
137+
*/
138+
static ssize_t papr_platform_dump_handle_read(struct file *file,
139+
char __user *buf, size_t size, loff_t *off)
140+
{
141+
struct ibm_platform_dump_params *params = file->private_data;
142+
u64 total_bytes;
143+
s32 fwrc;
144+
145+
/*
146+
* Dump already completed with the previous read calls.
147+
* In case if the user space issues further reads, returns
148+
* -EINVAL.
149+
*/
150+
if (!params->buf_length) {
151+
pr_warn_once("Platform dump completed for dump ID %llu\n",
152+
(u64) (((u64)params->dump_tag_hi << 32) |
153+
params->dump_tag_lo));
154+
return -EINVAL;
155+
}
156+
157+
if (size < SZ_1K) {
158+
pr_err_once("Buffer length should be minimum 1024 bytes\n");
159+
return -EINVAL;
160+
}
161+
162+
/*
163+
* The hypervisor returns status 0 if no more data available to
164+
* download. Then expects the last RTAS call with NULL buffer
165+
* to invalidate the dump which means dump will be freed in the
166+
* hypervisor.
167+
*/
168+
if (params->status == RTAS_IBM_PLATFORM_DUMP_COMPLETE) {
169+
params->buf_length = 0;
170+
fwrc = rtas_ibm_platform_dump(params, 0, 0);
171+
/*
172+
* Returns 0 (success) to the user space so that user
173+
* space read stops.
174+
*/
175+
return fwrc;
176+
} else if (size > params->buf_length) {
177+
/*
178+
* Allocate 4K work area. So if the user requests > 4K,
179+
* resize the buffer length.
180+
*/
181+
size = params->buf_length;
182+
}
183+
184+
fwrc = rtas_ibm_platform_dump(params,
185+
rtas_work_area_phys(params->work_area),
186+
size);
187+
if (fwrc < 0)
188+
return fwrc;
189+
190+
total_bytes = (u64) (((u64)params->bytes_ret_hi << 32) |
191+
params->bytes_ret_lo);
192+
193+
/*
194+
* Kernel or firmware bug, do not continue.
195+
*/
196+
if (WARN(total_bytes > size, "possible write beyond end of work area"))
197+
return -EFAULT;
198+
199+
if (copy_to_user(buf, rtas_work_area_raw_buf(params->work_area),
200+
total_bytes))
201+
return -EFAULT;
202+
203+
return total_bytes;
204+
}
205+
206+
static int papr_platform_dump_handle_release(struct inode *inode,
207+
struct file *file)
208+
{
209+
struct ibm_platform_dump_params *params = file->private_data;
210+
s32 fwrc = 0;
211+
212+
if (params->work_area)
213+
rtas_work_area_free(params->work_area);
214+
215+
mutex_lock(&platform_dump_list_mutex);
216+
list_del(&params->list);
217+
mutex_unlock(&platform_dump_list_mutex);
218+
219+
kfree(params);
220+
221+
return fwrc;
222+
}
223+
224+
static const struct file_operations papr_platform_dump_handle_ops = {
225+
.read = papr_platform_dump_handle_read,
226+
.release = papr_platform_dump_handle_release,
227+
};
228+
229+
/**
230+
* papr_platform_dump_create_handle() - Create a fd-based handle for
231+
* reading platform dump
232+
*
233+
* Handler for PAPR_PLATFORM_DUMP_IOC_CREATE_HANDLE ioctl command
234+
* Allocates RTAS parameter struct and work area and attached to the
235+
* file descriptor for reading by user space with the multiple RTAS
236+
* calls until the dump is completed. This memory allocation is freed
237+
* when the file is released.
238+
*
239+
* Multiple dump requests with different IDs are allowed at the same
240+
* time, but not with the same dump ID. So if the user space is
241+
* already opened file descriptor for the specific dump ID, return
242+
* -EALREADY for the next request.
243+
*
244+
* @dump_tag: Dump ID for the dump requested to retrieve from the
245+
* hypervisor
246+
*
247+
* Return: The installed fd number if successful, -ve errno otherwise.
248+
*/
249+
static long papr_platform_dump_create_handle(u64 dump_tag)
250+
{
251+
struct ibm_platform_dump_params *params;
252+
u64 param_dump_tag;
253+
struct file *file;
254+
long err;
255+
int fd;
256+
257+
/*
258+
* Return failure if the user space is already opened FD for
259+
* the specific dump ID. This check will prevent multiple dump
260+
* requests for the same dump ID at the same time. Generally
261+
* should not expect this, but in case.
262+
*/
263+
list_for_each_entry(params, &platform_dump_list, list) {
264+
param_dump_tag = (u64) (((u64)params->dump_tag_hi << 32) |
265+
params->bytes_ret_lo);
266+
if (dump_tag == param_dump_tag) {
267+
pr_err("Platform dump for ID(%llu) is already in progress\n",
268+
dump_tag);
269+
return -EALREADY;
270+
}
271+
}
272+
273+
params = kzalloc(sizeof(struct ibm_platform_dump_params),
274+
GFP_KERNEL_ACCOUNT);
275+
if (!params)
276+
return -ENOMEM;
277+
278+
params->work_area = rtas_work_area_alloc(SZ_4K);
279+
params->buf_length = SZ_4K;
280+
params->dump_tag_hi = (u32)(dump_tag >> 32);
281+
params->dump_tag_lo = (u32)(dump_tag & 0x00000000ffffffffULL);
282+
params->status = RTAS_IBM_PLATFORM_DUMP_START;
283+
284+
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
285+
if (fd < 0) {
286+
err = fd;
287+
goto put_fd;
288+
}
289+
290+
file = anon_inode_getfile("[papr-platform-dump]",
291+
&papr_platform_dump_handle_ops,
292+
(void *)params, O_RDONLY);
293+
if (IS_ERR(file)) {
294+
err = PTR_ERR(file);
295+
goto put_fd;
296+
}
297+
298+
file->f_mode |= FMODE_LSEEK | FMODE_PREAD;
299+
fd_install(fd, file);
300+
301+
list_add(&params->list, &platform_dump_list);
302+
303+
pr_info("%s (%d) initiated platform dump for dump tag %llu\n",
304+
current->comm, current->pid, dump_tag);
305+
return fd;
306+
put_fd:
307+
rtas_work_area_free(params->work_area);
308+
kfree(params);
309+
put_unused_fd(fd);
310+
return err;
311+
}
312+
313+
/*
314+
* Top-level ioctl handler for /dev/papr-platform-dump.
315+
*/
316+
static long papr_platform_dump_dev_ioctl(struct file *filp, unsigned int ioctl,
317+
unsigned long arg)
318+
{
319+
u64 __user *argp = (void __user *)arg;
320+
u64 dump_tag;
321+
long ret;
322+
323+
if (get_user(dump_tag, argp))
324+
return -EFAULT;
325+
326+
switch (ioctl) {
327+
case PAPR_PLATFORM_DUMP_IOC_CREATE_HANDLE:
328+
mutex_lock(&platform_dump_list_mutex);
329+
ret = papr_platform_dump_create_handle(dump_tag);
330+
mutex_unlock(&platform_dump_list_mutex);
331+
break;
332+
default:
333+
ret = -ENOIOCTLCMD;
334+
break;
335+
}
336+
return ret;
337+
}
338+
339+
static const struct file_operations papr_platform_dump_ops = {
340+
.unlocked_ioctl = papr_platform_dump_dev_ioctl,
341+
};
342+
343+
static struct miscdevice papr_platform_dump_dev = {
344+
.minor = MISC_DYNAMIC_MINOR,
345+
.name = "papr-platform-dump",
346+
.fops = &papr_platform_dump_ops,
347+
};
348+
349+
static __init int papr_platform_dump_init(void)
350+
{
351+
if (!rtas_function_implemented(RTAS_FN_IBM_PLATFORM_DUMP))
352+
return -ENODEV;
353+
354+
return misc_register(&papr_platform_dump_dev);
355+
}
356+
machine_device_initcall(pseries, papr_platform_dump_init);

0 commit comments

Comments
 (0)