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