]>
Commit | Line | Data |
---|---|---|
b79c0f4f | 1 | // SPDX-License-Identifier: GPL-2.0 |
fa96c883 | 2 | /* |
6f14cc18 | 3 | * Copyright (C) 2010 - 2015 UNISYS CORPORATION |
e423812a | 4 | * All rights reserved. |
e423812a KC |
5 | */ |
6 | ||
7 | /* | |
e19674ce | 8 | * This provides s-Par channel communication primitives, which are |
434cbf28 | 9 | * independent of the mechanism used to access the channel data. |
e423812a KC |
10 | */ |
11 | ||
99c805f4 | 12 | #include <linux/uuid.h> |
3103dc03 | 13 | #include <linux/io.h> |
eb6eb1e1 | 14 | #include <linux/slab.h> |
93d3ad90 | 15 | #include <linux/visorbus.h> |
99c805f4 | 16 | |
ae719092 | 17 | #include "visorbus_private.h" |
1fb3016e | 18 | #include "controlvmchannel.h" |
e423812a | 19 | |
b35fae7e | 20 | #define VISOR_DRV_NAME "visorchannel" |
e423812a | 21 | |
2b8ec7da | 22 | #define VISOR_CONSOLEVIDEO_CHANNEL_GUID \ |
b32c5cb8 AS |
23 | GUID_INIT(0x3cd6e705, 0xd6a2, 0x4aa5, \ |
24 | 0xad, 0x5c, 0x7b, 0x8, 0x88, 0x9d, 0xff, 0xe2) | |
c2a60109 | 25 | |
b32c5cb8 | 26 | static const guid_t visor_video_guid = VISOR_CONSOLEVIDEO_CHANNEL_GUID; |
99c805f4 | 27 | |
383df64e | 28 | struct visorchannel { |
d5b3f1dc | 29 | u64 physaddr; |
434cbf28 | 30 | ulong nbytes; |
3103dc03 | 31 | void *mapped; |
99c805f4 | 32 | bool requested; |
9fd1b95a | 33 | struct channel_header chan_hdr; |
b32c5cb8 | 34 | guid_t guid; |
0496bedf | 35 | /* |
cbe7e02f DK |
36 | * channel creator knows if more than one thread will be inserting or |
37 | * removing | |
0496bedf SW |
38 | */ |
39 | bool needs_lock; | |
40 | /* protect head writes in chan_hdr */ | |
41 | spinlock_t insert_lock; | |
42 | /* protect tail writes in chan_hdr */ | |
43 | spinlock_t remove_lock; | |
b32c5cb8 AS |
44 | guid_t type; |
45 | guid_t inst; | |
e423812a KC |
46 | }; |
47 | ||
f230ba68 | 48 | void visorchannel_destroy(struct visorchannel *channel) |
e423812a | 49 | { |
fc11a550 | 50 | if (!channel) |
e423812a | 51 | return; |
a3b726c1 | 52 | |
434cbf28 | 53 | if (channel->mapped) { |
3103dc03 | 54 | memunmap(channel->mapped); |
99c805f4 DK |
55 | if (channel->requested) |
56 | release_mem_region(channel->physaddr, channel->nbytes); | |
0dbb3fb6 | 57 | } |
e423812a KC |
58 | kfree(channel); |
59 | } | |
e423812a | 60 | |
f230ba68 | 61 | u64 visorchannel_get_physaddr(struct visorchannel *channel) |
e423812a | 62 | { |
434cbf28 | 63 | return channel->physaddr; |
e423812a | 64 | } |
e423812a | 65 | |
f230ba68 | 66 | ulong visorchannel_get_nbytes(struct visorchannel *channel) |
e423812a | 67 | { |
2b9bcf81 | 68 | return channel->nbytes; |
e423812a | 69 | } |
e423812a | 70 | |
b32c5cb8 | 71 | char *visorchannel_guid_id(const guid_t *guid, char *s) |
e423812a | 72 | { |
90addb02 BR |
73 | sprintf(s, "%pUL", guid); |
74 | return s; | |
e423812a | 75 | } |
e423812a | 76 | |
f230ba68 | 77 | char *visorchannel_id(struct visorchannel *channel, char *s) |
e423812a | 78 | { |
b32c5cb8 | 79 | return visorchannel_guid_id(&channel->guid, s); |
e423812a | 80 | } |
e423812a | 81 | |
f230ba68 | 82 | char *visorchannel_zoneid(struct visorchannel *channel, char *s) |
e423812a | 83 | { |
b32c5cb8 | 84 | return visorchannel_guid_id(&channel->chan_hdr.zone_guid, s); |
e423812a | 85 | } |
e423812a | 86 | |
f230ba68 | 87 | u64 visorchannel_get_clientpartition(struct visorchannel *channel) |
e423812a | 88 | { |
a8a31f61 | 89 | return channel->chan_hdr.partition_handle; |
e423812a | 90 | } |
e423812a | 91 | |
f230ba68 CD |
92 | int visorchannel_set_clientpartition(struct visorchannel *channel, |
93 | u64 partition_handle) | |
4f6d8a97 DZ |
94 | { |
95 | channel->chan_hdr.partition_handle = partition_handle; | |
96 | return 0; | |
97 | } | |
4f6d8a97 | 98 | |
e19674ce | 99 | /** |
b32c5cb8 | 100 | * visorchannel_get_guid() - queries the GUID of the designated channel |
e19674ce DB |
101 | * @channel: the channel to query |
102 | * | |
b32c5cb8 | 103 | * Return: the GUID of the provided channel |
e19674ce | 104 | */ |
b32c5cb8 | 105 | const guid_t *visorchannel_get_guid(struct visorchannel *channel) |
e423812a | 106 | { |
b32c5cb8 | 107 | return &channel->guid; |
e423812a | 108 | } |
b32c5cb8 | 109 | EXPORT_SYMBOL_GPL(visorchannel_get_guid); |
e423812a | 110 | |
f230ba68 CD |
111 | int visorchannel_read(struct visorchannel *channel, ulong offset, void *dest, |
112 | ulong nbytes) | |
e423812a | 113 | { |
434cbf28 | 114 | if (offset + nbytes > channel->nbytes) |
36203e71 JS |
115 | return -EIO; |
116 | ||
8c3c1e47 | 117 | memcpy(dest, channel->mapped + offset, nbytes); |
36203e71 | 118 | return 0; |
e423812a | 119 | } |
e423812a | 120 | |
f230ba68 CD |
121 | int visorchannel_write(struct visorchannel *channel, ulong offset, void *dest, |
122 | ulong nbytes) | |
e423812a | 123 | { |
0abb60c1 JS |
124 | size_t chdr_size = sizeof(struct channel_header); |
125 | size_t copy_size; | |
69141bb8 | 126 | |
434cbf28 | 127 | if (offset + nbytes > channel->nbytes) |
ad44088f JS |
128 | return -EIO; |
129 | ||
0abb60c1 | 130 | if (offset < chdr_size) { |
56df900c | 131 | copy_size = min(chdr_size - offset, nbytes); |
d253058f | 132 | memcpy(((char *)(&channel->chan_hdr)) + offset, |
8c3c1e47 | 133 | dest, copy_size); |
0abb60c1 | 134 | } |
8c3c1e47 | 135 | memcpy(channel->mapped + offset, dest, nbytes); |
ad44088f | 136 | return 0; |
e423812a | 137 | } |
e423812a | 138 | |
f230ba68 | 139 | void *visorchannel_get_header(struct visorchannel *channel) |
e423812a | 140 | { |
5da77f37 | 141 | return &channel->chan_hdr; |
e423812a | 142 | } |
e423812a | 143 | |
e19674ce DB |
144 | /* |
145 | * Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a | |
146 | * channel header | |
e423812a | 147 | */ |
bc05a9c8 | 148 | static int sig_queue_offset(struct channel_header *chan_hdr, int q) |
c0616454 DK |
149 | { |
150 | return ((chan_hdr)->ch_space_offset + | |
151 | ((q) * sizeof(struct signal_queue_header))); | |
152 | } | |
e423812a | 153 | |
e19674ce DB |
154 | /* |
155 | * Return offset of a specific queue entry (data) from the beginning of a | |
156 | * channel header | |
e423812a | 157 | */ |
bc05a9c8 CIK |
158 | static int sig_data_offset(struct channel_header *chan_hdr, int q, |
159 | struct signal_queue_header *sig_hdr, int slot) | |
c0616454 DK |
160 | { |
161 | return (sig_queue_offset(chan_hdr, q) + sig_hdr->sig_base_offset + | |
162 | (slot * sig_hdr->signal_size)); | |
163 | } | |
e423812a | 164 | |
e19674ce | 165 | /* |
cbe7e02f DK |
166 | * Write the contents of a specific field within a SIGNAL_QUEUE_HEADER back into |
167 | * host memory | |
e423812a | 168 | */ |
c2a60109 DB |
169 | #define SIG_WRITE_FIELD(channel, queue, sig_hdr, FIELD) \ |
170 | visorchannel_write(channel, \ | |
c0616454 | 171 | sig_queue_offset(&channel->chan_hdr, queue) + \ |
1306c429 | 172 | offsetof(struct signal_queue_header, FIELD), \ |
c2a60109 | 173 | &((sig_hdr)->FIELD), \ |
1306c429 | 174 | sizeof((sig_hdr)->FIELD)) |
e423812a | 175 | |
f230ba68 CD |
176 | static int sig_read_header(struct visorchannel *channel, u32 queue, |
177 | struct signal_queue_header *sig_hdr) | |
e423812a | 178 | { |
0aca7844 | 179 | if (channel->chan_hdr.ch_space_offset < sizeof(struct channel_header)) |
1306c429 | 180 | return -EINVAL; |
e423812a KC |
181 | |
182 | /* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */ | |
1306c429 | 183 | return visorchannel_read(channel, |
c0616454 | 184 | sig_queue_offset(&channel->chan_hdr, queue), |
1306c429 | 185 | sig_hdr, sizeof(struct signal_queue_header)); |
e423812a KC |
186 | } |
187 | ||
f230ba68 CD |
188 | static int sig_read_data(struct visorchannel *channel, u32 queue, |
189 | struct signal_queue_header *sig_hdr, u32 slot, | |
190 | void *data) | |
e423812a | 191 | { |
c0616454 | 192 | int signal_data_offset = sig_data_offset(&channel->chan_hdr, queue, |
e423812a | 193 | sig_hdr, slot); |
e423812a | 194 | |
1306c429 DB |
195 | return visorchannel_read(channel, signal_data_offset, |
196 | data, sig_hdr->signal_size); | |
e423812a KC |
197 | } |
198 | ||
f230ba68 CD |
199 | static int sig_write_data(struct visorchannel *channel, u32 queue, |
200 | struct signal_queue_header *sig_hdr, u32 slot, | |
201 | void *data) | |
e423812a | 202 | { |
c0616454 | 203 | int signal_data_offset = sig_data_offset(&channel->chan_hdr, queue, |
a4ed0ba9 PB |
204 | sig_hdr, slot); |
205 | ||
1306c429 DB |
206 | return visorchannel_write(channel, signal_data_offset, |
207 | data, sig_hdr->signal_size); | |
e423812a KC |
208 | } |
209 | ||
f230ba68 CD |
210 | static int signalremove_inner(struct visorchannel *channel, u32 queue, |
211 | void *msg) | |
e423812a | 212 | { |
e0fed862 | 213 | struct signal_queue_header sig_hdr; |
1306c429 DB |
214 | int error; |
215 | ||
216 | error = sig_read_header(channel, queue, &sig_hdr); | |
217 | if (error) | |
218 | return error; | |
5d295bc3 | 219 | /* No signals to remove; have caller try again. */ |
b12fdf7d | 220 | if (sig_hdr.head == sig_hdr.tail) |
5d295bc3 | 221 | return -EAGAIN; |
153cf710 | 222 | sig_hdr.tail = (sig_hdr.tail + 1) % sig_hdr.max_slots; |
1306c429 DB |
223 | error = sig_read_data(channel, queue, &sig_hdr, sig_hdr.tail, msg); |
224 | if (error) | |
225 | return error; | |
153cf710 | 226 | sig_hdr.num_received++; |
e19674ce | 227 | /* |
cbe7e02f DK |
228 | * For each data field in SIGNAL_QUEUE_HEADER that was modified, update |
229 | * host memory. Required for channel sync. | |
e423812a | 230 | */ |
0496bedf | 231 | mb(); |
1306c429 DB |
232 | error = SIG_WRITE_FIELD(channel, queue, &sig_hdr, tail); |
233 | if (error) | |
234 | return error; | |
235 | error = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_received); | |
236 | if (error) | |
237 | return error; | |
1306c429 | 238 | return 0; |
b12fdf7d ZW |
239 | } |
240 | ||
e19674ce DB |
241 | /** |
242 | * visorchannel_signalremove() - removes a message from the designated | |
243 | * channel/queue | |
244 | * @channel: the channel the message will be removed from | |
245 | * @queue: the queue the message will be removed from | |
246 | * @msg: the message to remove | |
247 | * | |
f621a968 | 248 | * Return: integer error code indicating the status of the removal |
e19674ce | 249 | */ |
f230ba68 CD |
250 | int visorchannel_signalremove(struct visorchannel *channel, u32 queue, |
251 | void *msg) | |
b12fdf7d | 252 | { |
f621a968 | 253 | int rc; |
24ac1074 | 254 | unsigned long flags; |
b12fdf7d ZW |
255 | |
256 | if (channel->needs_lock) { | |
24ac1074 | 257 | spin_lock_irqsave(&channel->remove_lock, flags); |
b12fdf7d | 258 | rc = signalremove_inner(channel, queue, msg); |
24ac1074 | 259 | spin_unlock_irqrestore(&channel->remove_lock, flags); |
b12fdf7d ZW |
260 | } else { |
261 | rc = signalremove_inner(channel, queue, msg); | |
262 | } | |
e423812a | 263 | |
f621a968 | 264 | return rc; |
e423812a KC |
265 | } |
266 | EXPORT_SYMBOL_GPL(visorchannel_signalremove); | |
267 | ||
f230ba68 | 268 | static bool queue_empty(struct visorchannel *channel, u32 queue) |
f0208b71 CM |
269 | { |
270 | struct signal_queue_header sig_hdr; | |
271 | ||
272 | if (sig_read_header(channel, queue, &sig_hdr)) | |
273 | return true; | |
f0208b71 CM |
274 | return (sig_hdr.head == sig_hdr.tail); |
275 | } | |
276 | ||
7ec83df0 | 277 | /** |
cbe7e02f DK |
278 | * visorchannel_signalempty() - checks if the designated channel/queue contains |
279 | * any messages | |
7ec83df0 DB |
280 | * @channel: the channel to query |
281 | * @queue: the queue in the channel to query | |
282 | * | |
283 | * Return: boolean indicating whether any messages in the designated | |
284 | * channel/queue are present | |
285 | */ | |
f230ba68 | 286 | bool visorchannel_signalempty(struct visorchannel *channel, u32 queue) |
fdc792cd | 287 | { |
f0208b71 CM |
288 | bool rc; |
289 | unsigned long flags; | |
fdc792cd | 290 | |
f0208b71 CM |
291 | if (!channel->needs_lock) |
292 | return queue_empty(channel, queue); | |
f0208b71 CM |
293 | spin_lock_irqsave(&channel->remove_lock, flags); |
294 | rc = queue_empty(channel, queue); | |
295 | spin_unlock_irqrestore(&channel->remove_lock, flags); | |
fdc792cd NH |
296 | return rc; |
297 | } | |
298 | EXPORT_SYMBOL_GPL(visorchannel_signalempty); | |
299 | ||
f230ba68 CD |
300 | static int signalinsert_inner(struct visorchannel *channel, u32 queue, |
301 | void *msg) | |
e423812a | 302 | { |
e0fed862 | 303 | struct signal_queue_header sig_hdr; |
9b2cae6d | 304 | int err; |
e423812a | 305 | |
9b2cae6d DK |
306 | err = sig_read_header(channel, queue, &sig_hdr); |
307 | if (err) | |
308 | return err; | |
90cb147f | 309 | sig_hdr.head = (sig_hdr.head + 1) % sig_hdr.max_slots; |
153cf710 BR |
310 | if (sig_hdr.head == sig_hdr.tail) { |
311 | sig_hdr.num_overflows++; | |
9b2cae6d DK |
312 | err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_overflows); |
313 | if (err) | |
314 | return err; | |
1306c429 | 315 | return -EIO; |
e423812a | 316 | } |
9b2cae6d DK |
317 | err = sig_write_data(channel, queue, &sig_hdr, sig_hdr.head, msg); |
318 | if (err) | |
319 | return err; | |
153cf710 | 320 | sig_hdr.num_sent++; |
e19674ce | 321 | /* |
cbe7e02f DK |
322 | * For each data field in SIGNAL_QUEUE_HEADER that was modified, update |
323 | * host memory. Required for channel sync. | |
e423812a | 324 | */ |
0496bedf | 325 | mb(); |
9b2cae6d DK |
326 | err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, head); |
327 | if (err) | |
328 | return err; | |
329 | err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_sent); | |
330 | if (err) | |
331 | return err; | |
1306c429 | 332 | return 0; |
b12fdf7d ZW |
333 | } |
334 | ||
3a8bc4b7 | 335 | /* |
90476670 SW |
336 | * visorchannel_create() - creates the struct visorchannel abstraction for a |
337 | * data area in memory, but does NOT modify this data | |
338 | * area | |
3995c5da | 339 | * @physaddr: physical address of start of channel |
3995c5da | 340 | * @gfp: gfp_t to use when allocating memory for the data struct |
d7f1589a | 341 | * @guid: GUID that identifies channel type; |
3995c5da DK |
342 | * @needs_lock: must specify true if you have multiple threads of execution |
343 | * that will be calling visorchannel methods of this | |
344 | * visorchannel at the same time | |
345 | * | |
346 | * Return: pointer to visorchannel that was created if successful, | |
347 | * otherwise NULL | |
348 | */ | |
90476670 SW |
349 | struct visorchannel *visorchannel_create(u64 physaddr, gfp_t gfp, |
350 | const guid_t *guid, bool needs_lock) | |
3995c5da DK |
351 | { |
352 | struct visorchannel *channel; | |
353 | int err; | |
354 | size_t size = sizeof(struct channel_header); | |
355 | ||
356 | if (physaddr == 0) | |
357 | return NULL; | |
358 | ||
359 | channel = kzalloc(sizeof(*channel), gfp); | |
360 | if (!channel) | |
361 | return NULL; | |
3995c5da DK |
362 | channel->needs_lock = needs_lock; |
363 | spin_lock_init(&channel->insert_lock); | |
364 | spin_lock_init(&channel->remove_lock); | |
3995c5da | 365 | /* |
cbe7e02f DK |
366 | * Video driver constains the efi framebuffer so it will get a conflict |
367 | * resource when requesting its full mem region. Since we are only | |
368 | * using the efi framebuffer for video we can ignore this. Remember that | |
369 | * we haven't requested it so we don't try to release later on. | |
3995c5da | 370 | */ |
b35fae7e | 371 | channel->requested = request_mem_region(physaddr, size, VISOR_DRV_NAME); |
b32c5cb8 | 372 | if (!channel->requested && !guid_equal(guid, &visor_video_guid)) |
51646f4d DK |
373 | /* we only care about errors if this is not the video channel */ |
374 | goto err_destroy_channel; | |
3995c5da DK |
375 | channel->mapped = memremap(physaddr, size, MEMREMAP_WB); |
376 | if (!channel->mapped) { | |
377 | release_mem_region(physaddr, size); | |
378 | goto err_destroy_channel; | |
379 | } | |
3995c5da DK |
380 | channel->physaddr = physaddr; |
381 | channel->nbytes = size; | |
d7f1589a | 382 | err = visorchannel_read(channel, 0, &channel->chan_hdr, size); |
3995c5da DK |
383 | if (err) |
384 | goto err_destroy_channel; | |
d7f1589a | 385 | size = (ulong)channel->chan_hdr.size; |
3995c5da DK |
386 | memunmap(channel->mapped); |
387 | if (channel->requested) | |
388 | release_mem_region(channel->physaddr, channel->nbytes); | |
389 | channel->mapped = NULL; | |
d7f1589a DK |
390 | channel->requested = request_mem_region(channel->physaddr, size, |
391 | VISOR_DRV_NAME); | |
b32c5cb8 | 392 | if (!channel->requested && !guid_equal(guid, &visor_video_guid)) |
51646f4d DK |
393 | /* we only care about errors if this is not the video channel */ |
394 | goto err_destroy_channel; | |
d7f1589a | 395 | channel->mapped = memremap(channel->physaddr, size, MEMREMAP_WB); |
3995c5da | 396 | if (!channel->mapped) { |
d7f1589a | 397 | release_mem_region(channel->physaddr, size); |
3995c5da DK |
398 | goto err_destroy_channel; |
399 | } | |
d7f1589a | 400 | channel->nbytes = size; |
b32c5cb8 | 401 | guid_copy(&channel->guid, guid); |
3995c5da DK |
402 | return channel; |
403 | ||
404 | err_destroy_channel: | |
405 | visorchannel_destroy(channel); | |
406 | return NULL; | |
407 | } | |
408 | ||
e19674ce DB |
409 | /** |
410 | * visorchannel_signalinsert() - inserts a message into the designated | |
411 | * channel/queue | |
412 | * @channel: the channel the message will be added to | |
413 | * @queue: the queue the message will be added to | |
414 | * @msg: the message to insert | |
415 | * | |
264f7b8a | 416 | * Return: integer error code indicating the status of the insertion |
e19674ce | 417 | */ |
f230ba68 CD |
418 | int visorchannel_signalinsert(struct visorchannel *channel, u32 queue, |
419 | void *msg) | |
b12fdf7d | 420 | { |
264f7b8a | 421 | int rc; |
24ac1074 | 422 | unsigned long flags; |
b12fdf7d ZW |
423 | |
424 | if (channel->needs_lock) { | |
24ac1074 | 425 | spin_lock_irqsave(&channel->insert_lock, flags); |
b12fdf7d | 426 | rc = signalinsert_inner(channel, queue, msg); |
24ac1074 | 427 | spin_unlock_irqrestore(&channel->insert_lock, flags); |
b12fdf7d ZW |
428 | } else { |
429 | rc = signalinsert_inner(channel, queue, msg); | |
430 | } | |
e423812a | 431 | |
264f7b8a | 432 | return rc; |
e423812a KC |
433 | } |
434 | EXPORT_SYMBOL_GPL(visorchannel_signalinsert); |