diff options
author | Christophe Lombard <clombard@linux.vnet.ibm.com> | 2016-03-04 06:26:38 -0500 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2016-03-09 07:39:56 -0500 |
commit | 594ff7d067ca42676e27e2a7b5dcc0ff039d08ca (patch) | |
tree | 45824c4fb92c9450b86bfbb9ff19a0dd17cd2c93 | |
parent | 4752876c71701b7663a5ded789058ab2c05f7d0f (diff) |
cxl: Support to flash a new image on the adapter from a guest
The new flash.c file contains the logic to flash a new image on the
adapter, through a hcall. It is an iterative process, with chunks of
data of 1M at a time. There are also 2 phases: write and verify. The
flash operation itself is driven from a user-land tool.
Once flashing is successful, an rtas call is made to update the device
tree with the new properties values for the adapter and the AFU(s)
Add a new char device for the adapter, so that the flash tool can
access the card, even if there is no valid AFU on it.
Co-authored-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Signed-off-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com>
Reviewed-by: Manoj Kumar <manoj@linux.vnet.ibm.com>
Acked-by: Ian Munsie <imunsie@au1.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r-- | Documentation/powerpc/cxl.txt | 55 | ||||
-rw-r--r-- | drivers/misc/cxl/Makefile | 2 | ||||
-rw-r--r-- | drivers/misc/cxl/base.c | 7 | ||||
-rw-r--r-- | drivers/misc/cxl/cxl.h | 6 | ||||
-rw-r--r-- | drivers/misc/cxl/file.c | 11 | ||||
-rw-r--r-- | drivers/misc/cxl/flash.c | 538 | ||||
-rw-r--r-- | drivers/misc/cxl/guest.c | 15 | ||||
-rw-r--r-- | include/uapi/misc/cxl.h | 24 |
8 files changed, 653 insertions, 5 deletions
diff --git a/Documentation/powerpc/cxl.txt b/Documentation/powerpc/cxl.txt index 205c1b81625c..d5506ba0fef7 100644 --- a/Documentation/powerpc/cxl.txt +++ b/Documentation/powerpc/cxl.txt | |||
@@ -116,6 +116,8 @@ Work Element Descriptor (WED) | |||
116 | User API | 116 | User API |
117 | ======== | 117 | ======== |
118 | 118 | ||
119 | 1. AFU character devices | ||
120 | |||
119 | For AFUs operating in AFU directed mode, two character device | 121 | For AFUs operating in AFU directed mode, two character device |
120 | files will be created. /dev/cxl/afu0.0m will correspond to a | 122 | files will be created. /dev/cxl/afu0.0m will correspond to a |
121 | master context and /dev/cxl/afu0.0s will correspond to a slave | 123 | master context and /dev/cxl/afu0.0s will correspond to a slave |
@@ -362,6 +364,59 @@ read | |||
362 | reserved fields: | 364 | reserved fields: |
363 | For future extensions and padding | 365 | For future extensions and padding |
364 | 366 | ||
367 | |||
368 | 2. Card character device (powerVM guest only) | ||
369 | |||
370 | In a powerVM guest, an extra character device is created for the | ||
371 | card. The device is only used to write (flash) a new image on the | ||
372 | FPGA accelerator. Once the image is written and verified, the | ||
373 | device tree is updated and the card is reset to reload the updated | ||
374 | image. | ||
375 | |||
376 | open | ||
377 | ---- | ||
378 | |||
379 | Opens the device and allocates a file descriptor to be used with | ||
380 | the rest of the API. The device can only be opened once. | ||
381 | |||
382 | ioctl | ||
383 | ----- | ||
384 | |||
385 | CXL_IOCTL_DOWNLOAD_IMAGE: | ||
386 | CXL_IOCTL_VALIDATE_IMAGE: | ||
387 | Starts and controls flashing a new FPGA image. Partial | ||
388 | reconfiguration is not supported (yet), so the image must contain | ||
389 | a copy of the PSL and AFU(s). Since an image can be quite large, | ||
390 | the caller may have to iterate, splitting the image in smaller | ||
391 | chunks. | ||
392 | |||
393 | Takes a pointer to a struct cxl_adapter_image: | ||
394 | struct cxl_adapter_image { | ||
395 | __u64 flags; | ||
396 | __u64 data; | ||
397 | __u64 len_data; | ||
398 | __u64 len_image; | ||
399 | __u64 reserved1; | ||
400 | __u64 reserved2; | ||
401 | __u64 reserved3; | ||
402 | __u64 reserved4; | ||
403 | }; | ||
404 | |||
405 | flags: | ||
406 | These flags indicate which optional fields are present in | ||
407 | this struct. Currently all fields are mandatory. | ||
408 | |||
409 | data: | ||
410 | Pointer to a buffer with part of the image to write to the | ||
411 | card. | ||
412 | |||
413 | len_data: | ||
414 | Size of the buffer pointed to by data. | ||
415 | |||
416 | len_image: | ||
417 | Full size of the image. | ||
418 | |||
419 | |||
365 | Sysfs Class | 420 | Sysfs Class |
366 | =========== | 421 | =========== |
367 | 422 | ||
diff --git a/drivers/misc/cxl/Makefile b/drivers/misc/cxl/Makefile index a2f49cf4a168..8a55c1aa11aa 100644 --- a/drivers/misc/cxl/Makefile +++ b/drivers/misc/cxl/Makefile | |||
@@ -4,7 +4,7 @@ ccflags-$(CONFIG_PPC_WERROR) += -Werror | |||
4 | cxl-y += main.o file.o irq.o fault.o native.o | 4 | cxl-y += main.o file.o irq.o fault.o native.o |
5 | cxl-y += context.o sysfs.o debugfs.o pci.o trace.o | 5 | cxl-y += context.o sysfs.o debugfs.o pci.o trace.o |
6 | cxl-y += vphb.o api.o | 6 | cxl-y += vphb.o api.o |
7 | cxl-$(CONFIG_PPC_PSERIES) += guest.o of.o hcalls.o | 7 | cxl-$(CONFIG_PPC_PSERIES) += flash.o guest.o of.o hcalls.o |
8 | obj-$(CONFIG_CXL) += cxl.o | 8 | obj-$(CONFIG_CXL) += cxl.o |
9 | obj-$(CONFIG_CXL_BASE) += base.o | 9 | obj-$(CONFIG_CXL_BASE) += base.o |
10 | 10 | ||
diff --git a/drivers/misc/cxl/base.c b/drivers/misc/cxl/base.c index a9f0dd3255a2..957f4dd23f40 100644 --- a/drivers/misc/cxl/base.c +++ b/drivers/misc/cxl/base.c | |||
@@ -84,3 +84,10 @@ void unregister_cxl_calls(struct cxl_calls *calls) | |||
84 | synchronize_rcu(); | 84 | synchronize_rcu(); |
85 | } | 85 | } |
86 | EXPORT_SYMBOL_GPL(unregister_cxl_calls); | 86 | EXPORT_SYMBOL_GPL(unregister_cxl_calls); |
87 | |||
88 | int cxl_update_properties(struct device_node *dn, | ||
89 | struct property *new_prop) | ||
90 | { | ||
91 | return of_update_property(dn, new_prop); | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(cxl_update_properties); | ||
diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index a7e75f1cc903..24bd4cab02c2 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h | |||
@@ -324,6 +324,10 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; | |||
324 | #define CXL_MODE_TIME_SLICED 0x4 | 324 | #define CXL_MODE_TIME_SLICED 0x4 |
325 | #define CXL_SUPPORTED_MODES (CXL_MODE_DEDICATED | CXL_MODE_DIRECTED) | 325 | #define CXL_SUPPORTED_MODES (CXL_MODE_DEDICATED | CXL_MODE_DIRECTED) |
326 | 326 | ||
327 | #define CXL_DEV_MINORS 13 /* 1 control + 4 AFUs * 3 (dedicated/master/shared) */ | ||
328 | #define CXL_CARD_MINOR(adapter) (adapter->adapter_num * CXL_DEV_MINORS) | ||
329 | #define CXL_DEVT_ADAPTER(dev) (MINOR(dev) / CXL_DEV_MINORS) | ||
330 | |||
327 | enum cxl_context_status { | 331 | enum cxl_context_status { |
328 | CLOSED, | 332 | CLOSED, |
329 | OPENED, | 333 | OPENED, |
@@ -692,12 +696,14 @@ struct cxl_calls { | |||
692 | }; | 696 | }; |
693 | int register_cxl_calls(struct cxl_calls *calls); | 697 | int register_cxl_calls(struct cxl_calls *calls); |
694 | void unregister_cxl_calls(struct cxl_calls *calls); | 698 | void unregister_cxl_calls(struct cxl_calls *calls); |
699 | int cxl_update_properties(struct device_node *dn, struct property *new_prop); | ||
695 | 700 | ||
696 | void cxl_remove_adapter_nr(struct cxl *adapter); | 701 | void cxl_remove_adapter_nr(struct cxl *adapter); |
697 | 702 | ||
698 | int cxl_alloc_spa(struct cxl_afu *afu); | 703 | int cxl_alloc_spa(struct cxl_afu *afu); |
699 | void cxl_release_spa(struct cxl_afu *afu); | 704 | void cxl_release_spa(struct cxl_afu *afu); |
700 | 705 | ||
706 | dev_t cxl_get_dev(void); | ||
701 | int cxl_file_init(void); | 707 | int cxl_file_init(void); |
702 | void cxl_file_exit(void); | 708 | void cxl_file_exit(void); |
703 | int cxl_register_adapter(struct cxl *adapter); | 709 | int cxl_register_adapter(struct cxl *adapter); |
diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index df4d49a6c67a..e16046292dd6 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c | |||
@@ -26,9 +26,7 @@ | |||
26 | #include "trace.h" | 26 | #include "trace.h" |
27 | 27 | ||
28 | #define CXL_NUM_MINORS 256 /* Total to reserve */ | 28 | #define CXL_NUM_MINORS 256 /* Total to reserve */ |
29 | #define CXL_DEV_MINORS 13 /* 1 control + 4 AFUs * 3 (dedicated/master/shared) */ | ||
30 | 29 | ||
31 | #define CXL_CARD_MINOR(adapter) (adapter->adapter_num * CXL_DEV_MINORS) | ||
32 | #define CXL_AFU_MINOR_D(afu) (CXL_CARD_MINOR(afu->adapter) + 1 + (3 * afu->slice)) | 30 | #define CXL_AFU_MINOR_D(afu) (CXL_CARD_MINOR(afu->adapter) + 1 + (3 * afu->slice)) |
33 | #define CXL_AFU_MINOR_M(afu) (CXL_AFU_MINOR_D(afu) + 1) | 31 | #define CXL_AFU_MINOR_M(afu) (CXL_AFU_MINOR_D(afu) + 1) |
34 | #define CXL_AFU_MINOR_S(afu) (CXL_AFU_MINOR_D(afu) + 2) | 32 | #define CXL_AFU_MINOR_S(afu) (CXL_AFU_MINOR_D(afu) + 2) |
@@ -36,7 +34,6 @@ | |||
36 | #define CXL_AFU_MKDEV_M(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_M(afu)) | 34 | #define CXL_AFU_MKDEV_M(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_M(afu)) |
37 | #define CXL_AFU_MKDEV_S(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_S(afu)) | 35 | #define CXL_AFU_MKDEV_S(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_S(afu)) |
38 | 36 | ||
39 | #define CXL_DEVT_ADAPTER(dev) (MINOR(dev) / CXL_DEV_MINORS) | ||
40 | #define CXL_DEVT_AFU(dev) ((MINOR(dev) % CXL_DEV_MINORS - 1) / 3) | 37 | #define CXL_DEVT_AFU(dev) ((MINOR(dev) % CXL_DEV_MINORS - 1) / 3) |
41 | 38 | ||
42 | #define CXL_DEVT_IS_CARD(dev) (MINOR(dev) % CXL_DEV_MINORS == 0) | 39 | #define CXL_DEVT_IS_CARD(dev) (MINOR(dev) % CXL_DEV_MINORS == 0) |
@@ -446,7 +443,8 @@ static const struct file_operations afu_master_fops = { | |||
446 | 443 | ||
447 | static char *cxl_devnode(struct device *dev, umode_t *mode) | 444 | static char *cxl_devnode(struct device *dev, umode_t *mode) |
448 | { | 445 | { |
449 | if (CXL_DEVT_IS_CARD(dev->devt)) { | 446 | if (cpu_has_feature(CPU_FTR_HVMODE) && |
447 | CXL_DEVT_IS_CARD(dev->devt)) { | ||
450 | /* | 448 | /* |
451 | * These minor numbers will eventually be used to program the | 449 | * These minor numbers will eventually be used to program the |
452 | * PSL and AFUs once we have dynamic reprogramming support | 450 | * PSL and AFUs once we have dynamic reprogramming support |
@@ -547,6 +545,11 @@ int cxl_register_adapter(struct cxl *adapter) | |||
547 | return device_register(&adapter->dev); | 545 | return device_register(&adapter->dev); |
548 | } | 546 | } |
549 | 547 | ||
548 | dev_t cxl_get_dev(void) | ||
549 | { | ||
550 | return cxl_dev; | ||
551 | } | ||
552 | |||
550 | int __init cxl_file_init(void) | 553 | int __init cxl_file_init(void) |
551 | { | 554 | { |
552 | int rc; | 555 | int rc; |
diff --git a/drivers/misc/cxl/flash.c b/drivers/misc/cxl/flash.c new file mode 100644 index 000000000000..68dd0b7da471 --- /dev/null +++ b/drivers/misc/cxl/flash.c | |||
@@ -0,0 +1,538 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | #include <linux/fs.h> | ||
3 | #include <linux/semaphore.h> | ||
4 | #include <linux/slab.h> | ||
5 | #include <linux/uaccess.h> | ||
6 | #include <asm/rtas.h> | ||
7 | |||
8 | #include "cxl.h" | ||
9 | #include "hcalls.h" | ||
10 | |||
11 | #define DOWNLOAD_IMAGE 1 | ||
12 | #define VALIDATE_IMAGE 2 | ||
13 | |||
14 | struct ai_header { | ||
15 | u16 version; | ||
16 | u8 reserved0[6]; | ||
17 | u16 vendor; | ||
18 | u16 device; | ||
19 | u16 subsystem_vendor; | ||
20 | u16 subsystem; | ||
21 | u64 image_offset; | ||
22 | u64 image_length; | ||
23 | u8 reserved1[96]; | ||
24 | }; | ||
25 | |||
26 | static struct semaphore sem; | ||
27 | unsigned long *buffer[CXL_AI_MAX_ENTRIES]; | ||
28 | struct sg_list *le; | ||
29 | static u64 continue_token; | ||
30 | static unsigned int transfer; | ||
31 | |||
32 | struct update_props_workarea { | ||
33 | __be32 phandle; | ||
34 | __be32 state; | ||
35 | __be64 reserved; | ||
36 | __be32 nprops; | ||
37 | } __packed; | ||
38 | |||
39 | struct update_nodes_workarea { | ||
40 | __be32 state; | ||
41 | __be64 unit_address; | ||
42 | __be32 reserved; | ||
43 | } __packed; | ||
44 | |||
45 | #define DEVICE_SCOPE 3 | ||
46 | #define NODE_ACTION_MASK 0xff000000 | ||
47 | #define NODE_COUNT_MASK 0x00ffffff | ||
48 | #define OPCODE_DELETE 0x01000000 | ||
49 | #define OPCODE_UPDATE 0x02000000 | ||
50 | #define OPCODE_ADD 0x03000000 | ||
51 | |||
52 | static int rcall(int token, char *buf, s32 scope) | ||
53 | { | ||
54 | int rc; | ||
55 | |||
56 | spin_lock(&rtas_data_buf_lock); | ||
57 | |||
58 | memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE); | ||
59 | rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope); | ||
60 | memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); | ||
61 | |||
62 | spin_unlock(&rtas_data_buf_lock); | ||
63 | return rc; | ||
64 | } | ||
65 | |||
66 | static int update_property(struct device_node *dn, const char *name, | ||
67 | u32 vd, char *value) | ||
68 | { | ||
69 | struct property *new_prop; | ||
70 | u32 *val; | ||
71 | int rc; | ||
72 | |||
73 | new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL); | ||
74 | if (!new_prop) | ||
75 | return -ENOMEM; | ||
76 | |||
77 | new_prop->name = kstrdup(name, GFP_KERNEL); | ||
78 | if (!new_prop->name) { | ||
79 | kfree(new_prop); | ||
80 | return -ENOMEM; | ||
81 | } | ||
82 | |||
83 | new_prop->length = vd; | ||
84 | new_prop->value = kzalloc(new_prop->length, GFP_KERNEL); | ||
85 | if (!new_prop->value) { | ||
86 | kfree(new_prop->name); | ||
87 | kfree(new_prop); | ||
88 | return -ENOMEM; | ||
89 | } | ||
90 | memcpy(new_prop->value, value, vd); | ||
91 | |||
92 | val = (u32 *)new_prop->value; | ||
93 | rc = cxl_update_properties(dn, new_prop); | ||
94 | pr_devel("%s: update property (%s, length: %i, value: %#x)\n", | ||
95 | dn->name, name, vd, be32_to_cpu(*val)); | ||
96 | |||
97 | if (rc) { | ||
98 | kfree(new_prop->name); | ||
99 | kfree(new_prop->value); | ||
100 | kfree(new_prop); | ||
101 | } | ||
102 | return rc; | ||
103 | } | ||
104 | |||
105 | static int update_node(__be32 phandle, s32 scope) | ||
106 | { | ||
107 | struct update_props_workarea *upwa; | ||
108 | struct device_node *dn; | ||
109 | int i, rc, ret; | ||
110 | char *prop_data; | ||
111 | char *buf; | ||
112 | int token; | ||
113 | u32 nprops; | ||
114 | u32 vd; | ||
115 | |||
116 | token = rtas_token("ibm,update-properties"); | ||
117 | if (token == RTAS_UNKNOWN_SERVICE) | ||
118 | return -EINVAL; | ||
119 | |||
120 | buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); | ||
121 | if (!buf) | ||
122 | return -ENOMEM; | ||
123 | |||
124 | dn = of_find_node_by_phandle(be32_to_cpu(phandle)); | ||
125 | if (!dn) { | ||
126 | kfree(buf); | ||
127 | return -ENOENT; | ||
128 | } | ||
129 | |||
130 | upwa = (struct update_props_workarea *)&buf[0]; | ||
131 | upwa->phandle = phandle; | ||
132 | do { | ||
133 | rc = rcall(token, buf, scope); | ||
134 | if (rc < 0) | ||
135 | break; | ||
136 | |||
137 | prop_data = buf + sizeof(*upwa); | ||
138 | nprops = be32_to_cpu(upwa->nprops); | ||
139 | |||
140 | if (*prop_data == 0) { | ||
141 | prop_data++; | ||
142 | vd = be32_to_cpu(*(__be32 *)prop_data); | ||
143 | prop_data += vd + sizeof(vd); | ||
144 | nprops--; | ||
145 | } | ||
146 | |||
147 | for (i = 0; i < nprops; i++) { | ||
148 | char *prop_name; | ||
149 | |||
150 | prop_name = prop_data; | ||
151 | prop_data += strlen(prop_name) + 1; | ||
152 | vd = be32_to_cpu(*(__be32 *)prop_data); | ||
153 | prop_data += sizeof(vd); | ||
154 | |||
155 | if ((vd != 0x00000000) && (vd != 0x80000000)) { | ||
156 | ret = update_property(dn, prop_name, vd, | ||
157 | prop_data); | ||
158 | if (ret) | ||
159 | pr_err("cxl: Could not update property %s - %i\n", | ||
160 | prop_name, ret); | ||
161 | |||
162 | prop_data += vd; | ||
163 | } | ||
164 | } | ||
165 | } while (rc == 1); | ||
166 | |||
167 | of_node_put(dn); | ||
168 | kfree(buf); | ||
169 | return rc; | ||
170 | } | ||
171 | |||
172 | static int update_devicetree(struct cxl *adapter, s32 scope) | ||
173 | { | ||
174 | struct update_nodes_workarea *unwa; | ||
175 | u32 action, node_count; | ||
176 | int token, rc, i; | ||
177 | __be32 *data, drc_index, phandle; | ||
178 | char *buf; | ||
179 | |||
180 | token = rtas_token("ibm,update-nodes"); | ||
181 | if (token == RTAS_UNKNOWN_SERVICE) | ||
182 | return -EINVAL; | ||
183 | |||
184 | buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); | ||
185 | if (!buf) | ||
186 | return -ENOMEM; | ||
187 | |||
188 | unwa = (struct update_nodes_workarea *)&buf[0]; | ||
189 | unwa->unit_address = cpu_to_be64(adapter->guest->handle); | ||
190 | do { | ||
191 | rc = rcall(token, buf, scope); | ||
192 | if (rc && rc != 1) | ||
193 | break; | ||
194 | |||
195 | data = (__be32 *)buf + 4; | ||
196 | while (be32_to_cpu(*data) & NODE_ACTION_MASK) { | ||
197 | action = be32_to_cpu(*data) & NODE_ACTION_MASK; | ||
198 | node_count = be32_to_cpu(*data) & NODE_COUNT_MASK; | ||
199 | pr_devel("device reconfiguration - action: %#x, nodes: %#x\n", | ||
200 | action, node_count); | ||
201 | data++; | ||
202 | |||
203 | for (i = 0; i < node_count; i++) { | ||
204 | phandle = *data++; | ||
205 | |||
206 | switch (action) { | ||
207 | case OPCODE_DELETE: | ||
208 | /* nothing to do */ | ||
209 | break; | ||
210 | case OPCODE_UPDATE: | ||
211 | update_node(phandle, scope); | ||
212 | break; | ||
213 | case OPCODE_ADD: | ||
214 | /* nothing to do, just move pointer */ | ||
215 | drc_index = *data++; | ||
216 | break; | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | } while (rc == 1); | ||
221 | |||
222 | kfree(buf); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static int handle_image(struct cxl *adapter, int operation, | ||
227 | long (*fct)(u64, u64, u64, u64 *), | ||
228 | struct cxl_adapter_image *ai) | ||
229 | { | ||
230 | size_t mod, s_copy, len_chunk = 0; | ||
231 | struct ai_header *header = NULL; | ||
232 | unsigned int entries = 0, i; | ||
233 | void *dest, *from; | ||
234 | int rc = 0, need_header; | ||
235 | |||
236 | /* base adapter image header */ | ||
237 | need_header = (ai->flags & CXL_AI_NEED_HEADER); | ||
238 | if (need_header) { | ||
239 | header = kzalloc(sizeof(struct ai_header), GFP_KERNEL); | ||
240 | if (!header) | ||
241 | return -ENOMEM; | ||
242 | header->version = cpu_to_be16(1); | ||
243 | header->vendor = cpu_to_be16(adapter->guest->vendor); | ||
244 | header->device = cpu_to_be16(adapter->guest->device); | ||
245 | header->subsystem_vendor = cpu_to_be16(adapter->guest->subsystem_vendor); | ||
246 | header->subsystem = cpu_to_be16(adapter->guest->subsystem); | ||
247 | header->image_offset = cpu_to_be64(CXL_AI_HEADER_SIZE); | ||
248 | header->image_length = cpu_to_be64(ai->len_image); | ||
249 | } | ||
250 | |||
251 | /* number of entries in the list */ | ||
252 | len_chunk = ai->len_data; | ||
253 | if (need_header) | ||
254 | len_chunk += CXL_AI_HEADER_SIZE; | ||
255 | |||
256 | entries = len_chunk / CXL_AI_BUFFER_SIZE; | ||
257 | mod = len_chunk % CXL_AI_BUFFER_SIZE; | ||
258 | if (mod) | ||
259 | entries++; | ||
260 | |||
261 | if (entries > CXL_AI_MAX_ENTRIES) { | ||
262 | rc = -EINVAL; | ||
263 | goto err; | ||
264 | } | ||
265 | |||
266 | /* < -- MAX_CHUNK_SIZE = 4096 * 256 = 1048576 bytes --> | ||
267 | * chunk 0 ---------------------------------------------------- | ||
268 | * | header | data | | ||
269 | * ---------------------------------------------------- | ||
270 | * chunk 1 ---------------------------------------------------- | ||
271 | * | data | | ||
272 | * ---------------------------------------------------- | ||
273 | * .... | ||
274 | * chunk n ---------------------------------------------------- | ||
275 | * | data | | ||
276 | * ---------------------------------------------------- | ||
277 | */ | ||
278 | from = (void *) ai->data; | ||
279 | for (i = 0; i < entries; i++) { | ||
280 | dest = buffer[i]; | ||
281 | s_copy = CXL_AI_BUFFER_SIZE; | ||
282 | |||
283 | if ((need_header) && (i == 0)) { | ||
284 | /* add adapter image header */ | ||
285 | memcpy(buffer[i], header, sizeof(struct ai_header)); | ||
286 | s_copy = CXL_AI_BUFFER_SIZE - CXL_AI_HEADER_SIZE; | ||
287 | dest += CXL_AI_HEADER_SIZE; /* image offset */ | ||
288 | } | ||
289 | if ((i == (entries - 1)) && mod) | ||
290 | s_copy = mod; | ||
291 | |||
292 | /* copy data */ | ||
293 | if (copy_from_user(dest, from, s_copy)) | ||
294 | goto err; | ||
295 | |||
296 | /* fill in the list */ | ||
297 | le[i].phys_addr = cpu_to_be64(virt_to_phys(buffer[i])); | ||
298 | le[i].len = cpu_to_be64(CXL_AI_BUFFER_SIZE); | ||
299 | if ((i == (entries - 1)) && mod) | ||
300 | le[i].len = cpu_to_be64(mod); | ||
301 | from += s_copy; | ||
302 | } | ||
303 | pr_devel("%s (op: %i, need header: %i, entries: %i, token: %#llx)\n", | ||
304 | __func__, operation, need_header, entries, continue_token); | ||
305 | |||
306 | /* | ||
307 | * download/validate the adapter image to the coherent | ||
308 | * platform facility | ||
309 | */ | ||
310 | rc = fct(adapter->guest->handle, virt_to_phys(le), entries, | ||
311 | &continue_token); | ||
312 | if (rc == 0) /* success of download/validation operation */ | ||
313 | continue_token = 0; | ||
314 | |||
315 | err: | ||
316 | kfree(header); | ||
317 | |||
318 | return rc; | ||
319 | } | ||
320 | |||
321 | static int transfer_image(struct cxl *adapter, int operation, | ||
322 | struct cxl_adapter_image *ai) | ||
323 | { | ||
324 | int rc = 0; | ||
325 | int afu; | ||
326 | |||
327 | switch (operation) { | ||
328 | case DOWNLOAD_IMAGE: | ||
329 | rc = handle_image(adapter, operation, | ||
330 | &cxl_h_download_adapter_image, ai); | ||
331 | if (rc < 0) { | ||
332 | pr_devel("resetting adapter\n"); | ||
333 | cxl_h_reset_adapter(adapter->guest->handle); | ||
334 | } | ||
335 | return rc; | ||
336 | |||
337 | case VALIDATE_IMAGE: | ||
338 | rc = handle_image(adapter, operation, | ||
339 | &cxl_h_validate_adapter_image, ai); | ||
340 | if (rc < 0) { | ||
341 | pr_devel("resetting adapter\n"); | ||
342 | cxl_h_reset_adapter(adapter->guest->handle); | ||
343 | return rc; | ||
344 | } | ||
345 | if (rc == 0) { | ||
346 | pr_devel("remove curent afu\n"); | ||
347 | for (afu = 0; afu < adapter->slices; afu++) | ||
348 | cxl_guest_remove_afu(adapter->afu[afu]); | ||
349 | |||
350 | pr_devel("resetting adapter\n"); | ||
351 | cxl_h_reset_adapter(adapter->guest->handle); | ||
352 | |||
353 | /* The entire image has now been | ||
354 | * downloaded and the validation has | ||
355 | * been successfully performed. | ||
356 | * After that, the partition should call | ||
357 | * ibm,update-nodes and | ||
358 | * ibm,update-properties to receive the | ||
359 | * current configuration | ||
360 | */ | ||
361 | rc = update_devicetree(adapter, DEVICE_SCOPE); | ||
362 | transfer = 1; | ||
363 | } | ||
364 | return rc; | ||
365 | } | ||
366 | |||
367 | return -EINVAL; | ||
368 | } | ||
369 | |||
370 | static long ioctl_transfer_image(struct cxl *adapter, int operation, | ||
371 | struct cxl_adapter_image __user *uai) | ||
372 | { | ||
373 | struct cxl_adapter_image ai; | ||
374 | |||
375 | pr_devel("%s\n", __func__); | ||
376 | |||
377 | if (copy_from_user(&ai, uai, sizeof(struct cxl_adapter_image))) | ||
378 | return -EFAULT; | ||
379 | |||
380 | /* | ||
381 | * Make sure reserved fields and bits are set to 0 | ||
382 | */ | ||
383 | if (ai.reserved1 || ai.reserved2 || ai.reserved3 || ai.reserved4 || | ||
384 | (ai.flags & ~CXL_AI_ALL)) | ||
385 | return -EINVAL; | ||
386 | |||
387 | return transfer_image(adapter, operation, &ai); | ||
388 | } | ||
389 | |||
390 | static int device_open(struct inode *inode, struct file *file) | ||
391 | { | ||
392 | int adapter_num = CXL_DEVT_ADAPTER(inode->i_rdev); | ||
393 | struct cxl *adapter; | ||
394 | int rc = 0, i; | ||
395 | |||
396 | pr_devel("in %s\n", __func__); | ||
397 | |||
398 | BUG_ON(sizeof(struct ai_header) != CXL_AI_HEADER_SIZE); | ||
399 | |||
400 | /* Allows one process to open the device by using a semaphore */ | ||
401 | if (down_interruptible(&sem) != 0) | ||
402 | return -EPERM; | ||
403 | |||
404 | if (!(adapter = get_cxl_adapter(adapter_num))) | ||
405 | return -ENODEV; | ||
406 | |||
407 | file->private_data = adapter; | ||
408 | continue_token = 0; | ||
409 | transfer = 0; | ||
410 | |||
411 | for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) | ||
412 | buffer[i] = NULL; | ||
413 | |||
414 | /* aligned buffer containing list entries which describes up to | ||
415 | * 1 megabyte of data (256 entries of 4096 bytes each) | ||
416 | * Logical real address of buffer 0 - Buffer 0 length in bytes | ||
417 | * Logical real address of buffer 1 - Buffer 1 length in bytes | ||
418 | * Logical real address of buffer 2 - Buffer 2 length in bytes | ||
419 | * .... | ||
420 | * .... | ||
421 | * Logical real address of buffer N - Buffer N length in bytes | ||
422 | */ | ||
423 | le = (struct sg_list *)get_zeroed_page(GFP_KERNEL); | ||
424 | if (!le) { | ||
425 | rc = -ENOMEM; | ||
426 | goto err; | ||
427 | } | ||
428 | |||
429 | for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) { | ||
430 | buffer[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL); | ||
431 | if (!buffer[i]) { | ||
432 | rc = -ENOMEM; | ||
433 | goto err1; | ||
434 | } | ||
435 | } | ||
436 | |||
437 | return 0; | ||
438 | |||
439 | err1: | ||
440 | for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) { | ||
441 | if (buffer[i]) | ||
442 | free_page((unsigned long) buffer[i]); | ||
443 | } | ||
444 | |||
445 | if (le) | ||
446 | free_page((unsigned long) le); | ||
447 | err: | ||
448 | put_device(&adapter->dev); | ||
449 | |||
450 | return rc; | ||
451 | } | ||
452 | |||
453 | static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
454 | { | ||
455 | struct cxl *adapter = file->private_data; | ||
456 | |||
457 | pr_devel("in %s\n", __func__); | ||
458 | |||
459 | if (cmd == CXL_IOCTL_DOWNLOAD_IMAGE) | ||
460 | return ioctl_transfer_image(adapter, | ||
461 | DOWNLOAD_IMAGE, | ||
462 | (struct cxl_adapter_image __user *)arg); | ||
463 | else if (cmd == CXL_IOCTL_VALIDATE_IMAGE) | ||
464 | return ioctl_transfer_image(adapter, | ||
465 | VALIDATE_IMAGE, | ||
466 | (struct cxl_adapter_image __user *)arg); | ||
467 | else | ||
468 | return -EINVAL; | ||
469 | } | ||
470 | |||
471 | static long device_compat_ioctl(struct file *file, unsigned int cmd, | ||
472 | unsigned long arg) | ||
473 | { | ||
474 | return device_ioctl(file, cmd, arg); | ||
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); | ||
493 | put_device(&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(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 = device_compat_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, 1); | ||
536 | err: | ||
537 | return rc; | ||
538 | } | ||
diff --git a/drivers/misc/cxl/guest.c b/drivers/misc/cxl/guest.c index b1b8ac5195e7..816113d9d19b 100644 --- a/drivers/misc/cxl/guest.c +++ b/drivers/misc/cxl/guest.c | |||
@@ -889,6 +889,7 @@ void cxl_guest_remove_adapter(struct cxl *adapter) | |||
889 | 889 | ||
890 | cxl_sysfs_adapter_remove(adapter); | 890 | cxl_sysfs_adapter_remove(adapter); |
891 | 891 | ||
892 | cxl_guest_remove_chardev(adapter); | ||
892 | device_unregister(&adapter->dev); | 893 | device_unregister(&adapter->dev); |
893 | } | 894 | } |
894 | 895 | ||
@@ -926,6 +927,9 @@ struct cxl *cxl_guest_init_adapter(struct device_node *np, struct platform_devic | |||
926 | if ((rc = properties_look_ok(adapter))) | 927 | if ((rc = properties_look_ok(adapter))) |
927 | goto err1; | 928 | goto err1; |
928 | 929 | ||
930 | if ((rc = cxl_guest_add_chardev(adapter))) | ||
931 | goto err1; | ||
932 | |||
929 | /* | 933 | /* |
930 | * After we call this function we must not free the adapter directly, | 934 | * After we call this function we must not free the adapter directly, |
931 | * even if it returns an error! | 935 | * even if it returns an error! |
@@ -941,12 +945,23 @@ struct cxl *cxl_guest_init_adapter(struct device_node *np, struct platform_devic | |||
941 | err_put1: | 945 | err_put1: |
942 | device_unregister(&adapter->dev); | 946 | device_unregister(&adapter->dev); |
943 | free = false; | 947 | free = false; |
948 | cxl_guest_remove_chardev(adapter); | ||
944 | err1: | 949 | err1: |
945 | if (free) | 950 | if (free) |
946 | free_adapter(adapter); | 951 | free_adapter(adapter); |
947 | return ERR_PTR(rc); | 952 | return ERR_PTR(rc); |
948 | } | 953 | } |
949 | 954 | ||
955 | void cxl_guest_reload_module(struct cxl *adapter) | ||
956 | { | ||
957 | struct platform_device *pdev; | ||
958 | |||
959 | pdev = adapter->guest->pdev; | ||
960 | cxl_guest_remove_adapter(adapter); | ||
961 | |||
962 | cxl_of_probe(pdev); | ||
963 | } | ||
964 | |||
950 | const struct cxl_backend_ops cxl_guest_ops = { | 965 | const struct cxl_backend_ops cxl_guest_ops = { |
951 | .module = THIS_MODULE, | 966 | .module = THIS_MODULE, |
952 | .adapter_reset = guest_reset, | 967 | .adapter_reset = guest_reset, |
diff --git a/include/uapi/misc/cxl.h b/include/uapi/misc/cxl.h index 1e889aa8a36e..8cd334f99ddc 100644 --- a/include/uapi/misc/cxl.h +++ b/include/uapi/misc/cxl.h | |||
@@ -55,11 +55,35 @@ struct cxl_afu_id { | |||
55 | __u64 reserved6; | 55 | __u64 reserved6; |
56 | }; | 56 | }; |
57 | 57 | ||
58 | /* base adapter image header is included in the image */ | ||
59 | #define CXL_AI_NEED_HEADER 0x0000000000000001ULL | ||
60 | #define CXL_AI_ALL CXL_AI_NEED_HEADER | ||
61 | |||
62 | #define CXL_AI_HEADER_SIZE 128 | ||
63 | #define CXL_AI_BUFFER_SIZE 4096 | ||
64 | #define CXL_AI_MAX_ENTRIES 256 | ||
65 | #define CXL_AI_MAX_CHUNK_SIZE (CXL_AI_BUFFER_SIZE * CXL_AI_MAX_ENTRIES) | ||
66 | |||
67 | struct cxl_adapter_image { | ||
68 | __u64 flags; | ||
69 | __u64 data; | ||
70 | __u64 len_data; | ||
71 | __u64 len_image; | ||
72 | __u64 reserved1; | ||
73 | __u64 reserved2; | ||
74 | __u64 reserved3; | ||
75 | __u64 reserved4; | ||
76 | }; | ||
77 | |||
58 | /* ioctl numbers */ | 78 | /* ioctl numbers */ |
59 | #define CXL_MAGIC 0xCA | 79 | #define CXL_MAGIC 0xCA |
80 | /* AFU devices */ | ||
60 | #define CXL_IOCTL_START_WORK _IOW(CXL_MAGIC, 0x00, struct cxl_ioctl_start_work) | 81 | #define CXL_IOCTL_START_WORK _IOW(CXL_MAGIC, 0x00, struct cxl_ioctl_start_work) |
61 | #define CXL_IOCTL_GET_PROCESS_ELEMENT _IOR(CXL_MAGIC, 0x01, __u32) | 82 | #define CXL_IOCTL_GET_PROCESS_ELEMENT _IOR(CXL_MAGIC, 0x01, __u32) |
62 | #define CXL_IOCTL_GET_AFU_ID _IOR(CXL_MAGIC, 0x02, struct cxl_afu_id) | 83 | #define CXL_IOCTL_GET_AFU_ID _IOR(CXL_MAGIC, 0x02, struct cxl_afu_id) |
84 | /* adapter devices */ | ||
85 | #define CXL_IOCTL_DOWNLOAD_IMAGE _IOW(CXL_MAGIC, 0x0A, struct cxl_adapter_image) | ||
86 | #define CXL_IOCTL_VALIDATE_IMAGE _IOW(CXL_MAGIC, 0x0B, struct cxl_adapter_image) | ||
63 | 87 | ||
64 | #define CXL_READ_MIN_SIZE 0x1000 /* 4K */ | 88 | #define CXL_READ_MIN_SIZE 0x1000 /* 4K */ |
65 | 89 | ||