1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/kernel.h> |
3 | #include <linux/fs.h> |
4 | #include <linux/semaphore.h> |
5 | #include <linux/slab.h> |
6 | #include <linux/uaccess.h> |
7 | #include <linux/of.h> |
8 | #include <asm/rtas.h> |
9 | |
10 | #include "cxl.h" |
11 | #include "hcalls.h" |
12 | |
13 | #define DOWNLOAD_IMAGE 1 |
14 | #define VALIDATE_IMAGE 2 |
15 | |
16 | struct { |
17 | u16 ; |
18 | u8 [6]; |
19 | u16 ; |
20 | u16 ; |
21 | u16 ; |
22 | u16 ; |
23 | u64 ; |
24 | u64 ; |
25 | u8 [96]; |
26 | }; |
27 | |
28 | static struct semaphore sem; |
29 | static unsigned long *buffer[CXL_AI_MAX_ENTRIES]; |
30 | static struct sg_list *le; |
31 | static u64 continue_token; |
32 | static unsigned int transfer; |
33 | |
34 | struct update_props_workarea { |
35 | __be32 phandle; |
36 | __be32 state; |
37 | __be64 reserved; |
38 | __be32 nprops; |
39 | } __packed; |
40 | |
41 | struct update_nodes_workarea { |
42 | __be32 state; |
43 | __be64 unit_address; |
44 | __be32 reserved; |
45 | } __packed; |
46 | |
47 | #define DEVICE_SCOPE 3 |
48 | #define NODE_ACTION_MASK 0xff000000 |
49 | #define NODE_COUNT_MASK 0x00ffffff |
50 | #define OPCODE_DELETE 0x01000000 |
51 | #define OPCODE_UPDATE 0x02000000 |
52 | #define OPCODE_ADD 0x03000000 |
53 | |
54 | static int rcall(int token, char *buf, s32 scope) |
55 | { |
56 | int rc; |
57 | |
58 | spin_lock(&rtas_data_buf_lock); |
59 | |
60 | memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE); |
61 | rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope); |
62 | memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); |
63 | |
64 | spin_unlock(&rtas_data_buf_lock); |
65 | return rc; |
66 | } |
67 | |
68 | static int update_property(struct device_node *dn, const char *name, |
69 | u32 vd, char *value) |
70 | { |
71 | struct property *new_prop; |
72 | u32 *val; |
73 | int rc; |
74 | |
75 | new_prop = kzalloc(size: sizeof(*new_prop), GFP_KERNEL); |
76 | if (!new_prop) |
77 | return -ENOMEM; |
78 | |
79 | new_prop->name = kstrdup(s: name, GFP_KERNEL); |
80 | if (!new_prop->name) { |
81 | kfree(objp: new_prop); |
82 | return -ENOMEM; |
83 | } |
84 | |
85 | new_prop->length = vd; |
86 | new_prop->value = kzalloc(size: new_prop->length, GFP_KERNEL); |
87 | if (!new_prop->value) { |
88 | kfree(objp: new_prop->name); |
89 | kfree(objp: new_prop); |
90 | return -ENOMEM; |
91 | } |
92 | memcpy(new_prop->value, value, vd); |
93 | |
94 | val = (u32 *)new_prop->value; |
95 | rc = cxl_update_properties(dn, new_prop); |
96 | pr_devel("%pOFn: update property (%s, length: %i, value: %#x)\n" , |
97 | dn, name, vd, be32_to_cpu(*val)); |
98 | |
99 | if (rc) { |
100 | kfree(objp: new_prop->name); |
101 | kfree(objp: new_prop->value); |
102 | kfree(objp: new_prop); |
103 | } |
104 | return rc; |
105 | } |
106 | |
107 | static int update_node(__be32 phandle, s32 scope) |
108 | { |
109 | struct update_props_workarea *upwa; |
110 | struct device_node *dn; |
111 | int i, rc, ret; |
112 | char *prop_data; |
113 | char *buf; |
114 | int token; |
115 | u32 nprops; |
116 | u32 vd; |
117 | |
118 | token = rtas_token("ibm,update-properties" ); |
119 | if (token == RTAS_UNKNOWN_SERVICE) |
120 | return -EINVAL; |
121 | |
122 | buf = kzalloc(size: RTAS_DATA_BUF_SIZE, GFP_KERNEL); |
123 | if (!buf) |
124 | return -ENOMEM; |
125 | |
126 | dn = of_find_node_by_phandle(be32_to_cpu(phandle)); |
127 | if (!dn) { |
128 | kfree(objp: buf); |
129 | return -ENOENT; |
130 | } |
131 | |
132 | upwa = (struct update_props_workarea *)&buf[0]; |
133 | upwa->phandle = phandle; |
134 | do { |
135 | rc = rcall(token, buf, scope); |
136 | if (rc < 0) |
137 | break; |
138 | |
139 | prop_data = buf + sizeof(*upwa); |
140 | nprops = be32_to_cpu(upwa->nprops); |
141 | |
142 | if (*prop_data == 0) { |
143 | prop_data++; |
144 | vd = be32_to_cpu(*(__be32 *)prop_data); |
145 | prop_data += vd + sizeof(vd); |
146 | nprops--; |
147 | } |
148 | |
149 | for (i = 0; i < nprops; i++) { |
150 | char *prop_name; |
151 | |
152 | prop_name = prop_data; |
153 | prop_data += strlen(prop_name) + 1; |
154 | vd = be32_to_cpu(*(__be32 *)prop_data); |
155 | prop_data += sizeof(vd); |
156 | |
157 | if ((vd != 0x00000000) && (vd != 0x80000000)) { |
158 | ret = update_property(dn, name: prop_name, vd, |
159 | value: prop_data); |
160 | if (ret) |
161 | pr_err("cxl: Could not update property %s - %i\n" , |
162 | prop_name, ret); |
163 | |
164 | prop_data += vd; |
165 | } |
166 | } |
167 | } while (rc == 1); |
168 | |
169 | of_node_put(node: dn); |
170 | kfree(objp: buf); |
171 | return rc; |
172 | } |
173 | |
174 | static int update_devicetree(struct cxl *adapter, s32 scope) |
175 | { |
176 | struct update_nodes_workarea *unwa; |
177 | u32 action, node_count; |
178 | int token, rc, i; |
179 | __be32 *data, phandle; |
180 | char *buf; |
181 | |
182 | token = rtas_token("ibm,update-nodes" ); |
183 | if (token == RTAS_UNKNOWN_SERVICE) |
184 | return -EINVAL; |
185 | |
186 | buf = kzalloc(size: RTAS_DATA_BUF_SIZE, GFP_KERNEL); |
187 | if (!buf) |
188 | return -ENOMEM; |
189 | |
190 | unwa = (struct update_nodes_workarea *)&buf[0]; |
191 | unwa->unit_address = cpu_to_be64(adapter->guest->handle); |
192 | do { |
193 | rc = rcall(token, buf, scope); |
194 | if (rc && rc != 1) |
195 | break; |
196 | |
197 | data = (__be32 *)buf + 4; |
198 | while (be32_to_cpu(*data) & NODE_ACTION_MASK) { |
199 | action = be32_to_cpu(*data) & NODE_ACTION_MASK; |
200 | node_count = be32_to_cpu(*data) & NODE_COUNT_MASK; |
201 | pr_devel("device reconfiguration - action: %#x, nodes: %#x\n" , |
202 | action, node_count); |
203 | data++; |
204 | |
205 | for (i = 0; i < node_count; i++) { |
206 | phandle = *data++; |
207 | |
208 | switch (action) { |
209 | case OPCODE_DELETE: |
210 | /* nothing to do */ |
211 | break; |
212 | case OPCODE_UPDATE: |
213 | update_node(phandle, scope); |
214 | break; |
215 | case OPCODE_ADD: |
216 | /* nothing to do, just move pointer */ |
217 | data++; |
218 | break; |
219 | } |
220 | } |
221 | } |
222 | } while (rc == 1); |
223 | |
224 | kfree(objp: buf); |
225 | return 0; |
226 | } |
227 | |
228 | static int handle_image(struct cxl *adapter, int operation, |
229 | long (*fct)(u64, u64, u64, u64 *), |
230 | struct cxl_adapter_image *ai) |
231 | { |
232 | size_t mod, s_copy, len_chunk = 0; |
233 | struct ai_header * = NULL; |
234 | unsigned int entries = 0, i; |
235 | void *dest, *from; |
236 | int rc = 0, ; |
237 | |
238 | /* base adapter image header */ |
239 | need_header = (ai->flags & CXL_AI_NEED_HEADER); |
240 | if (need_header) { |
241 | header = kzalloc(size: sizeof(struct ai_header), GFP_KERNEL); |
242 | if (!header) |
243 | return -ENOMEM; |
244 | header->version = cpu_to_be16(1); |
245 | header->vendor = cpu_to_be16(adapter->guest->vendor); |
246 | header->device = cpu_to_be16(adapter->guest->device); |
247 | header->subsystem_vendor = cpu_to_be16(adapter->guest->subsystem_vendor); |
248 | header->subsystem = cpu_to_be16(adapter->guest->subsystem); |
249 | header->image_offset = cpu_to_be64(CXL_AI_HEADER_SIZE); |
250 | header->image_length = cpu_to_be64(ai->len_image); |
251 | } |
252 | |
253 | /* number of entries in the list */ |
254 | len_chunk = ai->len_data; |
255 | if (need_header) |
256 | len_chunk += CXL_AI_HEADER_SIZE; |
257 | |
258 | entries = len_chunk / CXL_AI_BUFFER_SIZE; |
259 | mod = len_chunk % CXL_AI_BUFFER_SIZE; |
260 | if (mod) |
261 | entries++; |
262 | |
263 | if (entries > CXL_AI_MAX_ENTRIES) { |
264 | rc = -EINVAL; |
265 | goto err; |
266 | } |
267 | |
268 | /* < -- MAX_CHUNK_SIZE = 4096 * 256 = 1048576 bytes --> |
269 | * chunk 0 ---------------------------------------------------- |
270 | * | header | data | |
271 | * ---------------------------------------------------- |
272 | * chunk 1 ---------------------------------------------------- |
273 | * | data | |
274 | * ---------------------------------------------------- |
275 | * .... |
276 | * chunk n ---------------------------------------------------- |
277 | * | data | |
278 | * ---------------------------------------------------- |
279 | */ |
280 | from = (void *) ai->data; |
281 | for (i = 0; i < entries; i++) { |
282 | dest = buffer[i]; |
283 | s_copy = CXL_AI_BUFFER_SIZE; |
284 | |
285 | if ((need_header) && (i == 0)) { |
286 | /* add adapter image header */ |
287 | memcpy(buffer[i], header, sizeof(struct ai_header)); |
288 | s_copy = CXL_AI_BUFFER_SIZE - CXL_AI_HEADER_SIZE; |
289 | dest += CXL_AI_HEADER_SIZE; /* image offset */ |
290 | } |
291 | if ((i == (entries - 1)) && mod) |
292 | s_copy = mod; |
293 | |
294 | /* copy data */ |
295 | if (copy_from_user(to: dest, from, n: s_copy)) |
296 | goto err; |
297 | |
298 | /* fill in the list */ |
299 | le[i].phys_addr = cpu_to_be64(virt_to_phys(buffer[i])); |
300 | le[i].len = cpu_to_be64(CXL_AI_BUFFER_SIZE); |
301 | if ((i == (entries - 1)) && mod) |
302 | le[i].len = cpu_to_be64(mod); |
303 | from += s_copy; |
304 | } |
305 | pr_devel("%s (op: %i, need header: %i, entries: %i, token: %#llx)\n" , |
306 | __func__, operation, need_header, entries, continue_token); |
307 | |
308 | /* |
309 | * download/validate the adapter image to the coherent |
310 | * platform facility |
311 | */ |
312 | rc = fct(adapter->guest->handle, virt_to_phys(address: le), entries, |
313 | &continue_token); |
314 | if (rc == 0) /* success of download/validation operation */ |
315 | continue_token = 0; |
316 | |
317 | err: |
318 | kfree(objp: header); |
319 | |
320 | return rc; |
321 | } |
322 | |
323 | static int transfer_image(struct cxl *adapter, int operation, |
324 | struct cxl_adapter_image *ai) |
325 | { |
326 | int rc = 0; |
327 | int afu; |
328 | |
329 | switch (operation) { |
330 | case DOWNLOAD_IMAGE: |
331 | rc = handle_image(adapter, operation, |
332 | fct: &cxl_h_download_adapter_image, ai); |
333 | if (rc < 0) { |
334 | pr_devel("resetting adapter\n" ); |
335 | cxl_h_reset_adapter(unit_address: adapter->guest->handle); |
336 | } |
337 | return rc; |
338 | |
339 | case VALIDATE_IMAGE: |
340 | rc = handle_image(adapter, operation, |
341 | fct: &cxl_h_validate_adapter_image, ai); |
342 | if (rc < 0) { |
343 | pr_devel("resetting adapter\n" ); |
344 | cxl_h_reset_adapter(unit_address: adapter->guest->handle); |
345 | return rc; |
346 | } |
347 | if (rc == 0) { |
348 | pr_devel("remove current afu\n" ); |
349 | for (afu = 0; afu < adapter->slices; afu++) |
350 | cxl_guest_remove_afu(afu: adapter->afu[afu]); |
351 | |
352 | pr_devel("resetting adapter\n" ); |
353 | cxl_h_reset_adapter(unit_address: adapter->guest->handle); |
354 | |
355 | /* The entire image has now been |
356 | * downloaded and the validation has |
357 | * been successfully performed. |
358 | * After that, the partition should call |
359 | * ibm,update-nodes and |
360 | * ibm,update-properties to receive the |
361 | * current configuration |
362 | */ |
363 | rc = update_devicetree(adapter, DEVICE_SCOPE); |
364 | transfer = 1; |
365 | } |
366 | return rc; |
367 | } |
368 | |
369 | return -EINVAL; |
370 | } |
371 | |
372 | static long ioctl_transfer_image(struct cxl *adapter, int operation, |
373 | struct cxl_adapter_image __user *uai) |
374 | { |
375 | struct cxl_adapter_image ai; |
376 | |
377 | pr_devel("%s\n" , __func__); |
378 | |
379 | if (copy_from_user(to: &ai, from: uai, n: sizeof(struct cxl_adapter_image))) |
380 | return -EFAULT; |
381 | |
382 | /* |
383 | * Make sure reserved fields and bits are set to 0 |
384 | */ |
385 | if (ai.reserved1 || ai.reserved2 || ai.reserved3 || ai.reserved4 || |
386 | (ai.flags & ~CXL_AI_ALL)) |
387 | return -EINVAL; |
388 | |
389 | return transfer_image(adapter, operation, ai: &ai); |
390 | } |
391 | |
392 | static int device_open(struct inode *inode, struct file *file) |
393 | { |
394 | int adapter_num = CXL_DEVT_ADAPTER(inode->i_rdev); |
395 | struct cxl *adapter; |
396 | int rc = 0, i; |
397 | |
398 | pr_devel("in %s\n" , __func__); |
399 | |
400 | BUG_ON(sizeof(struct ai_header) != CXL_AI_HEADER_SIZE); |
401 | |
402 | /* Allows one process to open the device by using a semaphore */ |
403 | if (down_interruptible(sem: &sem) != 0) |
404 | return -EPERM; |
405 | |
406 | if (!(adapter = get_cxl_adapter(num: adapter_num))) { |
407 | rc = -ENODEV; |
408 | goto err_unlock; |
409 | } |
410 | |
411 | file->private_data = adapter; |
412 | continue_token = 0; |
413 | transfer = 0; |
414 | |
415 | for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) |
416 | buffer[i] = NULL; |
417 | |
418 | /* aligned buffer containing list entries which describes up to |
419 | * 1 megabyte of data (256 entries of 4096 bytes each) |
420 | * Logical real address of buffer 0 - Buffer 0 length in bytes |
421 | * Logical real address of buffer 1 - Buffer 1 length in bytes |
422 | * Logical real address of buffer 2 - Buffer 2 length in bytes |
423 | * .... |
424 | * .... |
425 | * Logical real address of buffer N - Buffer N length in bytes |
426 | */ |
427 | le = (struct sg_list *)get_zeroed_page(GFP_KERNEL); |
428 | if (!le) { |
429 | rc = -ENOMEM; |
430 | goto err; |
431 | } |
432 | |
433 | for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) { |
434 | buffer[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL); |
435 | if (!buffer[i]) { |
436 | rc = -ENOMEM; |
437 | goto err1; |
438 | } |
439 | } |
440 | |
441 | return 0; |
442 | |
443 | err1: |
444 | for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) { |
445 | if (buffer[i]) |
446 | free_page((unsigned long) buffer[i]); |
447 | } |
448 | |
449 | if (le) |
450 | free_page((unsigned long) le); |
451 | err: |
452 | put_device(dev: &adapter->dev); |
453 | err_unlock: |
454 | up(sem: &sem); |
455 | |
456 | return rc; |
457 | } |
458 | |
459 | static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
460 | { |
461 | struct cxl *adapter = file->private_data; |
462 | |
463 | pr_devel("in %s\n" , __func__); |
464 | |
465 | if (cmd == CXL_IOCTL_DOWNLOAD_IMAGE) |
466 | return ioctl_transfer_image(adapter, |
467 | DOWNLOAD_IMAGE, |
468 | uai: (struct cxl_adapter_image __user *)arg); |
469 | else if (cmd == CXL_IOCTL_VALIDATE_IMAGE) |
470 | return ioctl_transfer_image(adapter, |
471 | VALIDATE_IMAGE, |
472 | uai: (struct cxl_adapter_image __user *)arg); |
473 | else |
474 | return -EINVAL; |
475 | } |
476 | |
477 | static int device_close(struct inode *inode, struct file *file) |
478 | { |
479 | struct cxl *adapter = file->private_data; |
480 | int i; |
481 | |
482 | pr_devel("in %s\n" , __func__); |
483 | |
484 | for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) { |
485 | if (buffer[i]) |
486 | free_page((unsigned long) buffer[i]); |
487 | } |
488 | |
489 | if (le) |
490 | free_page((unsigned long) le); |
491 | |
492 | up(sem: &sem); |
493 | put_device(dev: &adapter->dev); |
494 | continue_token = 0; |
495 | |
496 | /* reload the module */ |
497 | if (transfer) |
498 | cxl_guest_reload_module(adapter); |
499 | else { |
500 | pr_devel("resetting adapter\n" ); |
501 | cxl_h_reset_adapter(unit_address: adapter->guest->handle); |
502 | } |
503 | |
504 | transfer = 0; |
505 | return 0; |
506 | } |
507 | |
508 | static const struct file_operations fops = { |
509 | .owner = THIS_MODULE, |
510 | .open = device_open, |
511 | .unlocked_ioctl = device_ioctl, |
512 | .compat_ioctl = compat_ptr_ioctl, |
513 | .release = device_close, |
514 | }; |
515 | |
516 | void cxl_guest_remove_chardev(struct cxl *adapter) |
517 | { |
518 | cdev_del(&adapter->guest->cdev); |
519 | } |
520 | |
521 | int cxl_guest_add_chardev(struct cxl *adapter) |
522 | { |
523 | dev_t devt; |
524 | int rc; |
525 | |
526 | devt = MKDEV(MAJOR(cxl_get_dev()), CXL_CARD_MINOR(adapter)); |
527 | cdev_init(&adapter->guest->cdev, &fops); |
528 | if ((rc = cdev_add(&adapter->guest->cdev, devt, 1))) { |
529 | dev_err(&adapter->dev, |
530 | "Unable to add chardev on adapter (card%i): %i\n" , |
531 | adapter->adapter_num, rc); |
532 | goto err; |
533 | } |
534 | adapter->dev.devt = devt; |
535 | sema_init(sem: &sem, val: 1); |
536 | err: |
537 | return rc; |
538 | } |
539 | |