blob: b408d415bd8c763e8b19a576f5d672517cc6b19b [file] [log] [blame]
Ken Cox12e364b2014-03-04 07:58:07 -06001/* parser.c
2 *
3 * Copyright © 2010 - 2013 UNISYS CORPORATION
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14 * NON INFRINGEMENT. See the GNU General Public License for more
15 * details.
16 */
17
18#include "parser.h"
19#include "memregion.h"
20#include "controlvmchannel.h"
21#include <linux/ctype.h>
22#include <linux/mm.h>
23
24#define MYDRVNAME "visorchipset_parser"
25#define CURRENT_FILE_PC VISOR_CHIPSET_PC_parser_c
26
27/* We will refuse to allocate more than this many bytes to copy data from
28 * incoming payloads. This serves as a throttling mechanism.
29 */
30#define MAX_CONTROLVM_PAYLOAD_BYTES (1024*128)
31static ulong Controlvm_Payload_Bytes_Buffered;
32
33struct PARSER_CONTEXT_Tag {
34 ulong allocbytes;
35 ulong param_bytes;
36 u8 *curr;
37 ulong bytes_remaining;
38 BOOL byte_stream;
39 char data[0];
40};
41
42static PARSER_CONTEXT *
43parser_init_guts(U64 addr, U32 bytes, BOOL isLocal,
44 BOOL hasStandardPayloadHeader, BOOL *tryAgain)
45{
46 int allocbytes = sizeof(PARSER_CONTEXT) + bytes;
47 PARSER_CONTEXT *rc = NULL;
48 PARSER_CONTEXT *ctx = NULL;
49 MEMREGION *rgn = NULL;
50 ULTRA_CONTROLVM_PARAMETERS_HEADER *phdr = NULL;
51
52 if (tryAgain)
53 *tryAgain = FALSE;
54 if (!hasStandardPayloadHeader)
55 /* alloc and 0 extra byte to ensure payload is
56 * '\0'-terminated
57 */
58 allocbytes++;
59 if ((Controlvm_Payload_Bytes_Buffered + bytes)
60 > MAX_CONTROLVM_PAYLOAD_BYTES) {
61 ERRDRV("%s (%s:%d) - prevented allocation of %d bytes to prevent exceeding throttling max (%d)",
62 __func__, __FILE__, __LINE__, allocbytes,
63 MAX_CONTROLVM_PAYLOAD_BYTES);
64 if (tryAgain)
65 *tryAgain = TRUE;
Ken Coxd9355f82014-03-19 13:06:22 -050066 rc = NULL;
67 goto Away;
Ken Cox12e364b2014-03-04 07:58:07 -060068 }
Andreea-Cristina Bernat97a84f12014-03-14 04:20:06 +020069 ctx = kzalloc(allocbytes, GFP_KERNEL|__GFP_NORETRY);
Ken Cox12e364b2014-03-04 07:58:07 -060070 if (ctx == NULL) {
71 ERRDRV("%s (%s:%d) - failed to allocate %d bytes",
72 __func__, __FILE__, __LINE__, allocbytes);
73 if (tryAgain)
74 *tryAgain = TRUE;
Ken Coxd9355f82014-03-19 13:06:22 -050075 rc = NULL;
76 goto Away;
Ken Cox12e364b2014-03-04 07:58:07 -060077 }
Andreea-Cristina Bernat97a84f12014-03-14 04:20:06 +020078
Ken Cox12e364b2014-03-04 07:58:07 -060079 ctx->allocbytes = allocbytes;
80 ctx->param_bytes = bytes;
81 ctx->curr = NULL;
82 ctx->bytes_remaining = 0;
83 ctx->byte_stream = FALSE;
84 if (isLocal) {
85 void *p;
86 if (addr > virt_to_phys(high_memory - 1)) {
87 ERRDRV("%s - bad local address (0x%-16.16Lx for %lu)",
88 __func__,
89 (unsigned long long) addr, (ulong) bytes);
Ken Coxd9355f82014-03-19 13:06:22 -050090 rc = NULL;
91 goto Away;
Ken Cox12e364b2014-03-04 07:58:07 -060092 }
93 p = __va((ulong) (addr));
94 memcpy(ctx->data, p, bytes);
95 } else {
Ken Cox927c7922014-03-05 14:52:25 -060096 rgn = visor_memregion_create(addr, bytes);
Ken Coxd9355f82014-03-19 13:06:22 -050097 if (!rgn) {
98 rc = NULL;
99 goto Away;
100 }
101 if (visor_memregion_read(rgn, 0, ctx->data, bytes) < 0) {
102 rc = NULL;
103 goto Away;
104 }
Ken Cox12e364b2014-03-04 07:58:07 -0600105 }
106 if (!hasStandardPayloadHeader) {
107 ctx->byte_stream = TRUE;
Ken Coxd9355f82014-03-19 13:06:22 -0500108 rc = ctx;
109 goto Away;
Ken Cox12e364b2014-03-04 07:58:07 -0600110 }
111 phdr = (ULTRA_CONTROLVM_PARAMETERS_HEADER *) (ctx->data);
112 if (phdr->TotalLength != bytes) {
113 ERRDRV("%s - bad total length %lu (should be %lu)",
114 __func__,
115 (ulong) (phdr->TotalLength), (ulong) (bytes));
Ken Coxd9355f82014-03-19 13:06:22 -0500116 rc = NULL;
117 goto Away;
Ken Cox12e364b2014-03-04 07:58:07 -0600118 }
119 if (phdr->TotalLength < phdr->HeaderLength) {
120 ERRDRV("%s - total length < header length (%lu < %lu)",
121 __func__,
122 (ulong) (phdr->TotalLength),
123 (ulong) (phdr->HeaderLength));
Ken Coxd9355f82014-03-19 13:06:22 -0500124 rc = NULL;
125 goto Away;
Ken Cox12e364b2014-03-04 07:58:07 -0600126 }
127 if (phdr->HeaderLength < sizeof(ULTRA_CONTROLVM_PARAMETERS_HEADER)) {
128 ERRDRV("%s - header is too small (%lu < %lu)",
129 __func__,
130 (ulong) (phdr->HeaderLength),
131 (ulong) (sizeof(ULTRA_CONTROLVM_PARAMETERS_HEADER)));
Ken Coxd9355f82014-03-19 13:06:22 -0500132 rc = NULL;
133 goto Away;
Ken Cox12e364b2014-03-04 07:58:07 -0600134 }
135
Ken Coxd9355f82014-03-19 13:06:22 -0500136 rc = ctx;
Ken Cox12e364b2014-03-04 07:58:07 -0600137Away:
138 if (rgn) {
Ken Cox927c7922014-03-05 14:52:25 -0600139 visor_memregion_destroy(rgn);
Ken Cox12e364b2014-03-04 07:58:07 -0600140 rgn = NULL;
141 }
142 if (rc)
143 Controlvm_Payload_Bytes_Buffered += ctx->param_bytes;
144 else {
145 if (ctx) {
146 parser_done(ctx);
147 ctx = NULL;
148 }
149 }
150 return rc;
151}
152
153PARSER_CONTEXT *
154parser_init(U64 addr, U32 bytes, BOOL isLocal, BOOL *tryAgain)
155{
156 return parser_init_guts(addr, bytes, isLocal, TRUE, tryAgain);
157}
158
159/* Call this instead of parser_init() if the payload area consists of just
160 * a sequence of bytes, rather than a ULTRA_CONTROLVM_PARAMETERS_HEADER
161 * structures. Afterwards, you can call parser_simpleString_get() or
162 * parser_byteStream_get() to obtain the data.
163 */
164PARSER_CONTEXT *
165parser_init_byteStream(U64 addr, U32 bytes, BOOL isLocal, BOOL *tryAgain)
166{
167 return parser_init_guts(addr, bytes, isLocal, FALSE, tryAgain);
168}
169
170/* Obtain '\0'-terminated copy of string in payload area.
171 */
172char *
173parser_simpleString_get(PARSER_CONTEXT *ctx)
174{
175 if (!ctx->byte_stream)
176 return NULL;
177 return ctx->data; /* note this IS '\0'-terminated, because of
178 * the num of bytes we alloc+clear in
179 * parser_init_byteStream() */
180}
181
182/* Obtain a copy of the buffer in the payload area.
183 */
184void *
185parser_byteStream_get(PARSER_CONTEXT *ctx, ulong *nbytes)
186{
187 if (!ctx->byte_stream)
188 return NULL;
189 if (nbytes)
190 *nbytes = ctx->param_bytes;
191 return (void *) ctx->data;
192}
193
194GUID
195parser_id_get(PARSER_CONTEXT *ctx)
196{
197 ULTRA_CONTROLVM_PARAMETERS_HEADER *phdr = NULL;
198
199 if (ctx == NULL) {
200 ERRDRV("%s (%s:%d) - no context",
201 __func__, __FILE__, __LINE__);
202 return Guid0;
203 }
204 phdr = (ULTRA_CONTROLVM_PARAMETERS_HEADER *) (ctx->data);
205 return phdr->Id;
206}
207
208void
209parser_param_start(PARSER_CONTEXT *ctx, PARSER_WHICH_STRING which_string)
210{
211 ULTRA_CONTROLVM_PARAMETERS_HEADER *phdr = NULL;
212
213 if (ctx == NULL) {
214 ERRDRV("%s (%s:%d) - no context",
215 __func__, __FILE__, __LINE__);
Ken Cox097f4c12014-03-19 13:06:23 -0500216 goto Away;
Ken Cox12e364b2014-03-04 07:58:07 -0600217 }
218 phdr = (ULTRA_CONTROLVM_PARAMETERS_HEADER *) (ctx->data);
219 switch (which_string) {
220 case PARSERSTRING_INITIATOR:
221 ctx->curr = ctx->data + phdr->InitiatorOffset;
222 ctx->bytes_remaining = phdr->InitiatorLength;
223 break;
224 case PARSERSTRING_TARGET:
225 ctx->curr = ctx->data + phdr->TargetOffset;
226 ctx->bytes_remaining = phdr->TargetLength;
227 break;
228 case PARSERSTRING_CONNECTION:
229 ctx->curr = ctx->data + phdr->ConnectionOffset;
230 ctx->bytes_remaining = phdr->ConnectionLength;
231 break;
232 case PARSERSTRING_NAME:
233 ctx->curr = ctx->data + phdr->NameOffset;
234 ctx->bytes_remaining = phdr->NameLength;
235 break;
236 default:
237 ERRDRV("%s - bad which_string %d", __func__, which_string);
Ken Cox12e364b2014-03-04 07:58:07 -0600238 break;
239 }
Ken Cox12e364b2014-03-04 07:58:07 -0600240
241Away:
242 return;
243}
244
245void
246parser_done(PARSER_CONTEXT *ctx)
247{
248 if (!ctx)
249 return;
250 Controlvm_Payload_Bytes_Buffered -= ctx->param_bytes;
251 kfree(ctx);
252}
253
254/** Return length of string not counting trailing spaces. */
255static int
256string_length_no_trail(char *s, int len)
257{
258 int i = len - 1;
259 while (i >= 0) {
260 if (!isspace(s[i]))
261 return i + 1;
262 i--;
263 }
264 return 0;
265}
266
267/** Grab the next name and value out of the parameter buffer.
268 * The entire parameter buffer looks like this:
269 * <name>=<value>\0
270 * <name>=<value>\0
271 * ...
272 * \0
273 * If successful, the next <name> value is returned within the supplied
274 * <nam> buffer (the value is always upper-cased), and the corresponding
275 * <value> is returned within a kmalloc()ed buffer, whose pointer is
276 * provided as the return value of this function.
277 * (The total number of bytes allocated is strlen(<value>)+1.)
278 *
279 * NULL is returned to indicate failure, which can occur for several reasons:
280 * - all <name>=<value> pairs have already been processed
281 * - bad parameter
282 * - parameter buffer ends prematurely (couldn't find an '=' or '\0' within
283 * the confines of the parameter buffer)
284 * - the <nam> buffer is not large enough to hold the <name> of the next
285 * parameter
286 */
287void *
288parser_param_get(PARSER_CONTEXT *ctx, char *nam, int namesize)
289{
290 u8 *pscan, *pnam = nam;
291 ulong nscan;
292 int value_length = -1, orig_value_length = -1;
293 void *value = NULL;
294 int i;
295 int closing_quote = 0;
296
297 if (!ctx)
298 return NULL;
299 pscan = ctx->curr;
300 nscan = ctx->bytes_remaining;
301 if (nscan == 0)
302 return NULL;
303 if (*pscan == '\0')
304 /* This is the normal return point after you have processed
305 * all of the <name>=<value> pairs in a syntactically-valid
306 * parameter buffer.
307 */
308 return NULL;
309
310 /* skip whitespace */
311 while (isspace(*pscan)) {
312 pscan++;
313 nscan--;
314 if (nscan == 0)
315 return NULL;
316 }
317
318 while (*pscan != ':') {
319 if (namesize <= 0) {
320 ERRDRV("%s - name too big", __func__);
321 return NULL;
322 }
323 *pnam = toupper(*pscan);
324 pnam++;
325 namesize--;
326 pscan++;
327 nscan--;
328 if (nscan == 0) {
329 ERRDRV("%s - unexpected end of input parsing name",
330 __func__);
331 return NULL;
332 }
333 }
334 if (namesize <= 0) {
335 ERRDRV("%s - name too big", __func__);
336 return NULL;
337 }
338 *pnam = '\0';
339 nam[string_length_no_trail(nam, strlen(nam))] = '\0';
340
341 /* point to char immediately after ":" in "<name>:<value>" */
342 pscan++;
343 nscan--;
344 /* skip whitespace */
345 while (isspace(*pscan)) {
346 pscan++;
347 nscan--;
348 if (nscan == 0) {
349 ERRDRV("%s - unexpected end of input looking for value",
350 __func__);
351 return NULL;
352 }
353 }
354 if (nscan == 0) {
355 ERRDRV("%s - unexpected end of input looking for value",
356 __func__);
357 return NULL;
358 }
359 if (*pscan == '\'' || *pscan == '"') {
360 closing_quote = *pscan;
361 pscan++;
362 nscan--;
363 if (nscan == 0) {
364 ERRDRV("%s - unexpected end of input after %c",
365 __func__, closing_quote);
366 return NULL;
367 }
368 }
369
370 /* look for a separator character, terminator character, or
371 * end of data
372 */
373 for (i = 0, value_length = -1; i < nscan; i++) {
374 if (closing_quote) {
375 if (pscan[i] == '\0') {
376 ERRDRV("%s - unexpected end of input parsing quoted value", __func__);
377 return NULL;
378 }
379 if (pscan[i] == closing_quote) {
380 value_length = i;
381 break;
382 }
383 } else
384 if (pscan[i] == ',' || pscan[i] == ';'
385 || pscan[i] == '\0') {
386 value_length = i;
387 break;
388 }
389 }
390 if (value_length < 0) {
391 if (closing_quote) {
392 ERRDRV("%s - unexpected end of input parsing quoted value", __func__);
393 return NULL;
394 }
395 value_length = nscan;
396 }
397 orig_value_length = value_length;
398 if (closing_quote == 0)
399 value_length = string_length_no_trail(pscan, orig_value_length);
400 value = kmalloc(value_length + 1, GFP_KERNEL|__GFP_NORETRY);
401 if (value == NULL)
402 return NULL;
403 memcpy(value, pscan, value_length);
404 ((u8 *) (value))[value_length] = '\0';
405
406 pscan += orig_value_length;
407 nscan -= orig_value_length;
408
409 /* skip past separator or closing quote */
410 if (nscan > 0) {
411 if (*pscan != '\0') {
412 pscan++;
413 nscan--;
414 }
415 }
416
417 if (closing_quote && (nscan > 0)) {
418 /* we still need to skip around the real separator if present */
419 /* first, skip whitespace */
420 while (isspace(*pscan)) {
421 pscan++;
422 nscan--;
423 if (nscan == 0)
424 break;
425 }
426 if (nscan > 0) {
427 if (*pscan == ',' || *pscan == ';') {
428 pscan++;
429 nscan--;
430 } else if (*pscan != '\0') {
431 ERRDRV("%s - missing separator after quoted string", __func__);
432 kfree(value);
433 value = NULL;
434 return NULL;
435 }
436 }
437 }
438 ctx->curr = pscan;
439 ctx->bytes_remaining = nscan;
440 return value;
441}
442
443void *
444parser_string_get(PARSER_CONTEXT *ctx)
445{
446 u8 *pscan;
447 ulong nscan;
448 int value_length = -1;
449 void *value = NULL;
450 int i;
451
452 if (!ctx)
453 return NULL;
454 pscan = ctx->curr;
455 nscan = ctx->bytes_remaining;
456 if (nscan == 0)
457 return NULL;
458 if (!pscan)
459 return NULL;
460 for (i = 0, value_length = -1; i < nscan; i++)
461 if (pscan[i] == '\0') {
462 value_length = i;
463 break;
464 }
465 if (value_length < 0) /* '\0' was not included in the length */
466 value_length = nscan;
467 value = kmalloc(value_length + 1, GFP_KERNEL|__GFP_NORETRY);
468 if (value == NULL)
469 return NULL;
470 if (value_length > 0)
471 memcpy(value, pscan, value_length);
472 ((u8 *) (value))[value_length] = '\0';
473 return value;
474}