xref: /openbmc/linux/drivers/s390/char/diag_ftp.c (revision 8f933b1043e1e51f4776fc1ffe86752c7785fd4e)
1*8f933b10SRalf Hoppe /*
2*8f933b10SRalf Hoppe  *    DIAGNOSE X'2C4' instruction based HMC FTP services, useable on z/VM
3*8f933b10SRalf Hoppe  *
4*8f933b10SRalf Hoppe  *    Copyright IBM Corp. 2013
5*8f933b10SRalf Hoppe  *    Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6*8f933b10SRalf Hoppe  *
7*8f933b10SRalf Hoppe  */
8*8f933b10SRalf Hoppe 
9*8f933b10SRalf Hoppe #define KMSG_COMPONENT "hmcdrv"
10*8f933b10SRalf Hoppe #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11*8f933b10SRalf Hoppe 
12*8f933b10SRalf Hoppe #include <linux/kernel.h>
13*8f933b10SRalf Hoppe #include <linux/mm.h>
14*8f933b10SRalf Hoppe #include <linux/irq.h>
15*8f933b10SRalf Hoppe #include <linux/wait.h>
16*8f933b10SRalf Hoppe #include <linux/string.h>
17*8f933b10SRalf Hoppe #include <asm/ctl_reg.h>
18*8f933b10SRalf Hoppe 
19*8f933b10SRalf Hoppe #include "hmcdrv_ftp.h"
20*8f933b10SRalf Hoppe #include "diag_ftp.h"
21*8f933b10SRalf Hoppe 
22*8f933b10SRalf Hoppe /* DIAGNOSE X'2C4' return codes in Ry */
23*8f933b10SRalf Hoppe #define DIAG_FTP_RET_OK	0 /* HMC FTP started successfully */
24*8f933b10SRalf Hoppe #define DIAG_FTP_RET_EBUSY	4 /* HMC FTP service currently busy */
25*8f933b10SRalf Hoppe #define DIAG_FTP_RET_EIO	8 /* HMC FTP service I/O error */
26*8f933b10SRalf Hoppe /* and an artificial extension */
27*8f933b10SRalf Hoppe #define DIAG_FTP_RET_EPERM	2 /* HMC FTP service privilege error */
28*8f933b10SRalf Hoppe 
29*8f933b10SRalf Hoppe /* FTP service status codes (after INTR at guest real location 133) */
30*8f933b10SRalf Hoppe #define DIAG_FTP_STAT_OK	0U /* request completed successfully */
31*8f933b10SRalf Hoppe #define DIAG_FTP_STAT_PGCC	4U /* program check condition */
32*8f933b10SRalf Hoppe #define DIAG_FTP_STAT_PGIOE	8U /* paging I/O error */
33*8f933b10SRalf Hoppe #define DIAG_FTP_STAT_TIMEOUT	12U /* timeout */
34*8f933b10SRalf Hoppe #define DIAG_FTP_STAT_EBASE	16U /* base of error codes from SCLP */
35*8f933b10SRalf Hoppe #define DIAG_FTP_STAT_LDFAIL	(DIAG_FTP_STAT_EBASE + 1U) /* failed */
36*8f933b10SRalf Hoppe #define DIAG_FTP_STAT_LDNPERM	(DIAG_FTP_STAT_EBASE + 2U) /* not allowed */
37*8f933b10SRalf Hoppe #define DIAG_FTP_STAT_LDRUNS	(DIAG_FTP_STAT_EBASE + 3U) /* runs */
38*8f933b10SRalf Hoppe #define DIAG_FTP_STAT_LDNRUNS	(DIAG_FTP_STAT_EBASE + 4U) /* not runs */
39*8f933b10SRalf Hoppe 
40*8f933b10SRalf Hoppe /**
41*8f933b10SRalf Hoppe  * struct diag_ftp_ldfpl - load file FTP parameter list (LDFPL)
42*8f933b10SRalf Hoppe  * @bufaddr: real buffer address (at 4k boundary)
43*8f933b10SRalf Hoppe  * @buflen: length of buffer
44*8f933b10SRalf Hoppe  * @offset: dir/file offset
45*8f933b10SRalf Hoppe  * @intparm: interruption parameter (unused)
46*8f933b10SRalf Hoppe  * @transferred: bytes transferred
47*8f933b10SRalf Hoppe  * @fsize: file size, filled on GET
48*8f933b10SRalf Hoppe  * @failaddr: failing address
49*8f933b10SRalf Hoppe  * @spare: padding
50*8f933b10SRalf Hoppe  * @fident: file name - ASCII
51*8f933b10SRalf Hoppe  */
52*8f933b10SRalf Hoppe struct diag_ftp_ldfpl {
53*8f933b10SRalf Hoppe 	u64 bufaddr;
54*8f933b10SRalf Hoppe 	u64 buflen;
55*8f933b10SRalf Hoppe 	u64 offset;
56*8f933b10SRalf Hoppe 	u64 intparm;
57*8f933b10SRalf Hoppe 	u64 transferred;
58*8f933b10SRalf Hoppe 	u64 fsize;
59*8f933b10SRalf Hoppe 	u64 failaddr;
60*8f933b10SRalf Hoppe 	u64 spare;
61*8f933b10SRalf Hoppe 	u8 fident[HMCDRV_FTP_FIDENT_MAX];
62*8f933b10SRalf Hoppe } __packed;
63*8f933b10SRalf Hoppe 
64*8f933b10SRalf Hoppe static DECLARE_COMPLETION(diag_ftp_rx_complete);
65*8f933b10SRalf Hoppe static int diag_ftp_subcode;
66*8f933b10SRalf Hoppe 
67*8f933b10SRalf Hoppe /**
68*8f933b10SRalf Hoppe  * diag_ftp_handler() - FTP services IRQ handler
69*8f933b10SRalf Hoppe  * @extirq: external interrupt (sub-) code
70*8f933b10SRalf Hoppe  * @param32: 32-bit interruption parameter from &struct diag_ftp_ldfpl
71*8f933b10SRalf Hoppe  * @param64: unused (for 64-bit interrupt parameters)
72*8f933b10SRalf Hoppe  */
73*8f933b10SRalf Hoppe static void diag_ftp_handler(struct ext_code extirq,
74*8f933b10SRalf Hoppe 			     unsigned int param32,
75*8f933b10SRalf Hoppe 			     unsigned long param64)
76*8f933b10SRalf Hoppe {
77*8f933b10SRalf Hoppe 	if ((extirq.subcode >> 8) != 8)
78*8f933b10SRalf Hoppe 		return; /* not a FTP services sub-code */
79*8f933b10SRalf Hoppe 
80*8f933b10SRalf Hoppe 	inc_irq_stat(IRQEXT_FTP);
81*8f933b10SRalf Hoppe 	diag_ftp_subcode = extirq.subcode & 0xffU;
82*8f933b10SRalf Hoppe 	complete(&diag_ftp_rx_complete);
83*8f933b10SRalf Hoppe }
84*8f933b10SRalf Hoppe 
85*8f933b10SRalf Hoppe /**
86*8f933b10SRalf Hoppe  * diag_ftp_2c4() - DIAGNOSE X'2C4' service call
87*8f933b10SRalf Hoppe  * @fpl: pointer to prepared LDFPL
88*8f933b10SRalf Hoppe  * @cmd: FTP command to be executed
89*8f933b10SRalf Hoppe  *
90*8f933b10SRalf Hoppe  * Performs a DIAGNOSE X'2C4' call with (input/output) FTP parameter list
91*8f933b10SRalf Hoppe  * @fpl and FTP function code @cmd. In case of an error the function does
92*8f933b10SRalf Hoppe  * nothing and returns an (negative) error code.
93*8f933b10SRalf Hoppe  *
94*8f933b10SRalf Hoppe  * Notes:
95*8f933b10SRalf Hoppe  * 1. This function only initiates a transfer, so the caller must wait
96*8f933b10SRalf Hoppe  *    for completion (asynchronous execution).
97*8f933b10SRalf Hoppe  * 2. The FTP parameter list @fpl must be aligned to a double-word boundary.
98*8f933b10SRalf Hoppe  * 3. fpl->bufaddr must be a real address, 4k aligned
99*8f933b10SRalf Hoppe  */
100*8f933b10SRalf Hoppe static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl,
101*8f933b10SRalf Hoppe 			enum hmcdrv_ftp_cmdid cmd)
102*8f933b10SRalf Hoppe {
103*8f933b10SRalf Hoppe 	int rc;
104*8f933b10SRalf Hoppe 
105*8f933b10SRalf Hoppe 	asm volatile(
106*8f933b10SRalf Hoppe 		"	diag	%[addr],%[cmd],0x2c4\n"
107*8f933b10SRalf Hoppe 		"0:	j	2f\n"
108*8f933b10SRalf Hoppe 		"1:	la	%[rc],%[err]\n"
109*8f933b10SRalf Hoppe 		"2:\n"
110*8f933b10SRalf Hoppe 		EX_TABLE(0b, 1b)
111*8f933b10SRalf Hoppe 		: [rc] "=d" (rc), "+m" (*fpl)
112*8f933b10SRalf Hoppe 		: [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)),
113*8f933b10SRalf Hoppe 		  [err] "i" (DIAG_FTP_RET_EPERM)
114*8f933b10SRalf Hoppe 		: "cc");
115*8f933b10SRalf Hoppe 
116*8f933b10SRalf Hoppe 	switch (rc) {
117*8f933b10SRalf Hoppe 	case DIAG_FTP_RET_OK:
118*8f933b10SRalf Hoppe 		return 0;
119*8f933b10SRalf Hoppe 	case DIAG_FTP_RET_EBUSY:
120*8f933b10SRalf Hoppe 		return -EBUSY;
121*8f933b10SRalf Hoppe 	case DIAG_FTP_RET_EPERM:
122*8f933b10SRalf Hoppe 		return -EPERM;
123*8f933b10SRalf Hoppe 	case DIAG_FTP_RET_EIO:
124*8f933b10SRalf Hoppe 	default:
125*8f933b10SRalf Hoppe 		return -EIO;
126*8f933b10SRalf Hoppe 	}
127*8f933b10SRalf Hoppe }
128*8f933b10SRalf Hoppe 
129*8f933b10SRalf Hoppe /**
130*8f933b10SRalf Hoppe  * diag_ftp_cmd() - executes a DIAG X'2C4' FTP command, targeting a HMC
131*8f933b10SRalf Hoppe  * @ftp: pointer to FTP command specification
132*8f933b10SRalf Hoppe  * @fsize: return of file size (or NULL if undesirable)
133*8f933b10SRalf Hoppe  *
134*8f933b10SRalf Hoppe  * Attention: Notice that this function is not reentrant - so the caller
135*8f933b10SRalf Hoppe  * must ensure locking.
136*8f933b10SRalf Hoppe  *
137*8f933b10SRalf Hoppe  * Return: number of bytes read/written or a (negative) error code
138*8f933b10SRalf Hoppe  */
139*8f933b10SRalf Hoppe ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
140*8f933b10SRalf Hoppe {
141*8f933b10SRalf Hoppe 	struct diag_ftp_ldfpl *ldfpl;
142*8f933b10SRalf Hoppe 	ssize_t len;
143*8f933b10SRalf Hoppe #ifdef DEBUG
144*8f933b10SRalf Hoppe 	unsigned long start_jiffies;
145*8f933b10SRalf Hoppe 
146*8f933b10SRalf Hoppe 	pr_debug("starting DIAG X'2C4' on '%s', requesting %zd bytes\n",
147*8f933b10SRalf Hoppe 		 ftp->fname, ftp->len);
148*8f933b10SRalf Hoppe 	start_jiffies = jiffies;
149*8f933b10SRalf Hoppe #endif
150*8f933b10SRalf Hoppe 	init_completion(&diag_ftp_rx_complete);
151*8f933b10SRalf Hoppe 
152*8f933b10SRalf Hoppe 	ldfpl = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
153*8f933b10SRalf Hoppe 	if (!ldfpl) {
154*8f933b10SRalf Hoppe 		len = -ENOMEM;
155*8f933b10SRalf Hoppe 		goto out;
156*8f933b10SRalf Hoppe 	}
157*8f933b10SRalf Hoppe 
158*8f933b10SRalf Hoppe 	len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident));
159*8f933b10SRalf Hoppe 	if (len >= HMCDRV_FTP_FIDENT_MAX) {
160*8f933b10SRalf Hoppe 		len = -EINVAL;
161*8f933b10SRalf Hoppe 		goto out_free;
162*8f933b10SRalf Hoppe 	}
163*8f933b10SRalf Hoppe 
164*8f933b10SRalf Hoppe 	ldfpl->transferred = 0;
165*8f933b10SRalf Hoppe 	ldfpl->fsize = 0;
166*8f933b10SRalf Hoppe 	ldfpl->offset = ftp->ofs;
167*8f933b10SRalf Hoppe 	ldfpl->buflen = ftp->len;
168*8f933b10SRalf Hoppe 	ldfpl->bufaddr = virt_to_phys(ftp->buf);
169*8f933b10SRalf Hoppe 
170*8f933b10SRalf Hoppe 	len = diag_ftp_2c4(ldfpl, ftp->id);
171*8f933b10SRalf Hoppe 	if (len)
172*8f933b10SRalf Hoppe 		goto out_free;
173*8f933b10SRalf Hoppe 
174*8f933b10SRalf Hoppe 	/*
175*8f933b10SRalf Hoppe 	 * There is no way to cancel the running diag X'2C4', the code
176*8f933b10SRalf Hoppe 	 * needs to wait unconditionally until the transfer is complete.
177*8f933b10SRalf Hoppe 	 */
178*8f933b10SRalf Hoppe 	wait_for_completion(&diag_ftp_rx_complete);
179*8f933b10SRalf Hoppe 
180*8f933b10SRalf Hoppe #ifdef DEBUG
181*8f933b10SRalf Hoppe 	pr_debug("completed DIAG X'2C4' after %lu ms\n",
182*8f933b10SRalf Hoppe 		 (jiffies - start_jiffies) * 1000 / HZ);
183*8f933b10SRalf Hoppe 	pr_debug("status of DIAG X'2C4' is %u, with %lld/%lld bytes\n",
184*8f933b10SRalf Hoppe 		 diag_ftp_subcode, ldfpl->transferred, ldfpl->fsize);
185*8f933b10SRalf Hoppe #endif
186*8f933b10SRalf Hoppe 
187*8f933b10SRalf Hoppe 	switch (diag_ftp_subcode) {
188*8f933b10SRalf Hoppe 	case DIAG_FTP_STAT_OK: /* success */
189*8f933b10SRalf Hoppe 		len = ldfpl->transferred;
190*8f933b10SRalf Hoppe 		if (fsize)
191*8f933b10SRalf Hoppe 			*fsize = ldfpl->fsize;
192*8f933b10SRalf Hoppe 		break;
193*8f933b10SRalf Hoppe 	case DIAG_FTP_STAT_LDNPERM:
194*8f933b10SRalf Hoppe 		len = -EPERM;
195*8f933b10SRalf Hoppe 		break;
196*8f933b10SRalf Hoppe 	case DIAG_FTP_STAT_LDRUNS:
197*8f933b10SRalf Hoppe 		len = -EBUSY;
198*8f933b10SRalf Hoppe 		break;
199*8f933b10SRalf Hoppe 	case DIAG_FTP_STAT_LDFAIL:
200*8f933b10SRalf Hoppe 		len = -ENOENT; /* no such file or media */
201*8f933b10SRalf Hoppe 		break;
202*8f933b10SRalf Hoppe 	default:
203*8f933b10SRalf Hoppe 		len = -EIO;
204*8f933b10SRalf Hoppe 		break;
205*8f933b10SRalf Hoppe 	}
206*8f933b10SRalf Hoppe 
207*8f933b10SRalf Hoppe out_free:
208*8f933b10SRalf Hoppe 	free_page((unsigned long) ldfpl);
209*8f933b10SRalf Hoppe out:
210*8f933b10SRalf Hoppe 	return len;
211*8f933b10SRalf Hoppe }
212*8f933b10SRalf Hoppe 
213*8f933b10SRalf Hoppe /**
214*8f933b10SRalf Hoppe  * diag_ftp_startup() - startup of FTP services, when running on z/VM
215*8f933b10SRalf Hoppe  *
216*8f933b10SRalf Hoppe  * Return: 0 on success, else an (negative) error code
217*8f933b10SRalf Hoppe  */
218*8f933b10SRalf Hoppe int diag_ftp_startup(void)
219*8f933b10SRalf Hoppe {
220*8f933b10SRalf Hoppe 	int rc;
221*8f933b10SRalf Hoppe 
222*8f933b10SRalf Hoppe 	rc = register_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
223*8f933b10SRalf Hoppe 	if (rc)
224*8f933b10SRalf Hoppe 		return rc;
225*8f933b10SRalf Hoppe 
226*8f933b10SRalf Hoppe 	ctl_set_bit(0, 63 - 22);
227*8f933b10SRalf Hoppe 	return 0;
228*8f933b10SRalf Hoppe }
229*8f933b10SRalf Hoppe 
230*8f933b10SRalf Hoppe /**
231*8f933b10SRalf Hoppe  * diag_ftp_shutdown() - shutdown of FTP services, when running on z/VM
232*8f933b10SRalf Hoppe  */
233*8f933b10SRalf Hoppe void diag_ftp_shutdown(void)
234*8f933b10SRalf Hoppe {
235*8f933b10SRalf Hoppe 	ctl_clear_bit(0, 63 - 22);
236*8f933b10SRalf Hoppe 	unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
237*8f933b10SRalf Hoppe }
238