-
Notifications
You must be signed in to change notification settings - Fork 133
Expand file tree
/
Copy pathsim.c
More file actions
677 lines (578 loc) · 20.2 KB
/
sim.c
File metadata and controls
677 lines (578 loc) · 20.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "oscompat.h"
#include "sha2.h"
#include "sim.h"
/*
* Response XML templates sent by the simulated device.
*
* The configure response echoes back MaxPayloadSizeToTargetInBytes matching
* the host's initial value (1 MiB), so firehose_try_configure() never
* triggers a second configure exchange.
*/
#define SIM_ACK \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" \
"<data><response value=\"ACK\" rawmode=\"false\" /></data>"
#define SIM_ACK_RAWMODE \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" \
"<data><response value=\"ACK\" rawmode=\"true\" /></data>"
#define SIM_CONFIGURE_ACK \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" \
"<data><response value=\"ACK\" MemoryName=\"ufs\"" \
" MinVersionSupported=\"1\" Version=\"1\"" \
" MaxPayloadSizeToTargetInBytes=\"1048576\"" \
" MaxPayloadSizeToTargetInBytesSupported=\"1048576\"" \
" MaxXMLSizeInBytes=\"4096\" /></data>"
/* sim_state tracks whether the sim is in XML command mode or raw binary mode */
enum sim_state {
SIM_STATE_XML, /* normal state: dequeue queued XML responses */
SIM_STATE_RAW_IN, /* receiving raw binary data from host (program) */
SIM_STATE_RAW_OUT, /* sending raw binary data to host (read) */
};
struct sim_response {
char *data;
size_t len;
struct sim_response *next;
};
/*
* VIP hash validation state.
*
* sim_vip_recv_table() extracts the data-chunk SHA256 hashes from each
* received VIP table (signed MBN or raw chained binary) and stores them in
* vip_hashes[]. sim_vip_check_chunk() computes SHA256 of each incoming chunk
* and compares it against the next stored entry.
*
* Maximum capacity:
* signed table: MAX_DIGESTS_PER_SIGNED_FILE - 1 = 53 chunk hashes
* chained tables: MAX_CHAINED_FILES x (MAX_DIGESTS_PER_CHAINED_FILE - 1)
* = 32 x 255 = 8160 chunk hashes
* total: 8213 entries x SHA256_DIGEST_LENGTH bytes = ~256 KiB
* (heap-allocated together with the enclosing struct qdl_device_sim)
*/
#define SIM_VIP_MAX_HASHES \
((MAX_DIGESTS_PER_SIGNED_FILE - 1) + \
MAX_CHAINED_FILES * (MAX_DIGESTS_PER_CHAINED_FILE - 1))
struct qdl_device_sim {
struct qdl_device base;
struct vip_table_generator *vip_gen;
bool create_digests;
/* Response queue consumed by sim_read() */
struct sim_response *resp_head;
struct sim_response *resp_tail;
enum sim_state state;
size_t raw_remaining; /* bytes of raw data left to transfer */
bool closed; /* set after power command to terminate reads fast */
/* VIP hash validation */
uint8_t vip_hashes[SIM_VIP_MAX_HASHES][SHA256_DIGEST_LENGTH];
size_t vip_hash_count; /* entries populated from received VIP tables */
size_t vip_hash_idx; /* index of the next hash to verify */
/* Chain hash linking each VIP table to the next */
uint8_t vip_chain_hash[SHA256_DIGEST_LENGTH];
bool vip_has_chain_hash;
};
static void sim_enqueue(struct qdl_device_sim *qdl_sim, const char *xml)
{
struct sim_response *resp;
resp = malloc(sizeof(*resp));
if (!resp)
err(1, "sim: failed to allocate response entry");
resp->data = strdup(xml);
if (!resp->data)
err(1, "sim: failed to duplicate response string");
resp->len = strlen(xml);
resp->next = NULL;
if (qdl_sim->resp_tail)
qdl_sim->resp_tail->next = resp;
else
qdl_sim->resp_head = resp;
qdl_sim->resp_tail = resp;
}
static void sim_enqueue_log(struct qdl_device_sim *qdl_sim, const char *handler)
{
char buf[256];
snprintf(buf, sizeof(buf),
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
"<data><log value=\"INFO: Calling handler for %s\" /></data>",
handler);
sim_enqueue(qdl_sim, buf);
}
static unsigned int sim_get_uint_attr(xmlNode *node, const char *name)
{
xmlChar *val;
unsigned int ret;
val = xmlGetProp(node, (xmlChar *)name);
if (!val)
return 0;
ret = (unsigned int)strtoul((char *)val, NULL, 10);
xmlFree(val);
return ret;
}
/*
* Qualcomm MBN header structures (little-endian, packed).
*
* The signed VIP table (DigestsToSign.bin.mbn) uses one of these layouts.
* V5 and V6 share the same image_size/code_size offsets; V7 reorders the
* fields. Only V5 and V6 are supported for VIP table parsing today.
*/
struct mbn_header_v5 {
uint32_t image_id;
uint32_t header_version;
uint32_t qti_signature_size;
uint32_t qti_cert_chain_size;
uint32_t image_size;
uint32_t code_size;
uint32_t signature_ptr;
uint32_t signature_size;
uint32_t cert_chain_ptr;
uint32_t cert_chain_size;
} __attribute__((packed));
struct mbn_header_v6 {
uint32_t image_id;
uint32_t header_version;
uint32_t qti_signature_size;
uint32_t qti_cert_chain_size;
uint32_t image_size;
uint32_t code_size;
uint32_t signature_ptr;
uint32_t signature_size;
uint32_t cert_chain_ptr;
uint32_t cert_chain_size;
uint32_t qti_metadata_size;
uint32_t metadata_size;
} __attribute__((packed));
#define MBN_HEADER_VERSION_5 5
#define MBN_HEADER_VERSION_6 6
/*
* sim_vip_signed_payload() - locate the hash payload in a signed VIP MBN table
*
* DigestsToSign.bin.mbn is a Qualcomm MBN image with a packed header followed
* by the hash data, a signature, and certificate chains. The header_version
* field identifies the layout; V5 and V6 share the same image_size/code_size
* offsets. The payload begins at (file_size - image_size) and spans code_size
* bytes.
*
* Returns a pointer into @buf for the payload, or NULL if not recognised.
* Sets @out_len to the payload byte count on success.
*/
static const uint8_t *sim_vip_signed_payload(const uint8_t *buf, size_t len,
size_t *out_len)
{
struct mbn_header_v6 hdr;
size_t offset;
if (len < sizeof(struct mbn_header_v5))
return NULL;
memcpy(&hdr, buf, sizeof(struct mbn_header_v5));
if (hdr.header_version != MBN_HEADER_VERSION_5 &&
hdr.header_version != MBN_HEADER_VERSION_6)
return NULL;
if (hdr.code_size == 0 ||
hdr.code_size % SHA256_DIGEST_LENGTH != 0 ||
hdr.code_size > MAX_DIGESTS_PER_SIGNED_FILE * SHA256_DIGEST_LENGTH ||
hdr.image_size < hdr.code_size || hdr.image_size > len)
return NULL;
offset = len - hdr.image_size;
if (offset < sizeof(struct mbn_header_v5) || offset + hdr.code_size > len)
return NULL;
*out_len = hdr.code_size;
return buf + offset;
}
/*
* sim_vip_recv_table() - parse a received VIP table and store chunk hashes
*
* @is_signed: true -> DigestsToSign.bin.mbn (MBN-wrapped, signed)
* false -> ChainedTableOfDigests<n>.bin (raw binary)
*
* The last entry of a full signed table and of a non-final chained table is a
* chain hash (SHA256 of the following table) rather than a data-chunk hash.
* It is excluded from the stored list.
*
* A chained table is "final" when its raw byte count is not a multiple of
* SHA256_DIGEST_LENGTH: vip_gen_finalize() appends a single zero-padding byte
* to the final chained table so that it cannot be a USB-512-byte multiple.
*/
static void sim_vip_recv_table(struct qdl_device_sim *qdl_sim,
const void *buf, size_t len, bool is_signed)
{
const uint8_t *hashes;
size_t count, data_count;
bool has_chain_hash;
if (is_signed) {
size_t payload_len;
hashes = sim_vip_signed_payload(buf, len, &payload_len);
if (!hashes) {
ux_err("sim: VIP signed table: unrecognised format "
"(%zu bytes)\n", len);
return;
}
count = payload_len / SHA256_DIGEST_LENGTH;
/* Full table uses the last slot for the chain hash */
has_chain_hash = (count == MAX_DIGESTS_PER_SIGNED_FILE);
/* Save chain hash so the first chained table can be verified */
if (has_chain_hash) {
memcpy(qdl_sim->vip_chain_hash,
hashes + (count - 1) * SHA256_DIGEST_LENGTH,
SHA256_DIGEST_LENGTH);
qdl_sim->vip_has_chain_hash = true;
}
} else {
/* Verify this table against the chain hash from the previous table */
if (qdl_sim->vip_has_chain_hash) {
uint8_t hash[SHA256_DIGEST_LENGTH];
char got_hex[SHA256_DIGEST_STRING_LENGTH];
char exp_hex[SHA256_DIGEST_STRING_LENGTH];
SHA2_CTX ctx;
size_t i;
SHA256Init(&ctx);
SHA256Update(&ctx, buf, len);
SHA256Final(hash, &ctx);
if (memcmp(hash, qdl_sim->vip_chain_hash,
SHA256_DIGEST_LENGTH) != 0) {
for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
sprintf(got_hex + i * 2, "%02x", hash[i]);
sprintf(exp_hex + i * 2,
"%02x",
qdl_sim->vip_chain_hash[i]);
}
got_hex[SHA256_DIGEST_STRING_LENGTH - 1] = '\0';
exp_hex[SHA256_DIGEST_STRING_LENGTH - 1] = '\0';
ux_err("sim: VIP chained table chain hash mismatch\n"
" computed: %s\n"
" expected: %s\n",
got_hex, exp_hex);
} else {
ux_debug("sim: VIP chained table chain hash OK\n");
}
qdl_sim->vip_has_chain_hash = false;
}
hashes = buf;
if (len % SHA256_DIGEST_LENGTH != 0) {
/*
* Trailing zero-padding byte -> final chained table.
* All floor(len / 32) entries are chunk hashes.
*/
count = len / SHA256_DIGEST_LENGTH;
has_chain_hash = false;
} else {
/*
* Size is a multiple of 32 -> non-final chained table.
* The last entry is a chain hash to the next table.
*/
count = len / SHA256_DIGEST_LENGTH;
has_chain_hash = (count == MAX_DIGESTS_PER_CHAINED_FILE);
}
/* Save chain hash so the next chained table can be verified */
if (has_chain_hash) {
memcpy(qdl_sim->vip_chain_hash,
hashes + (count - 1) * SHA256_DIGEST_LENGTH,
SHA256_DIGEST_LENGTH);
qdl_sim->vip_has_chain_hash = true;
}
}
data_count = has_chain_hash ? count - 1 : count;
if (qdl_sim->vip_hash_count + data_count > SIM_VIP_MAX_HASHES) {
ux_err("sim: VIP table overflow (%zu + %zu > %u), "
"truncating\n",
qdl_sim->vip_hash_count, data_count, SIM_VIP_MAX_HASHES);
data_count = SIM_VIP_MAX_HASHES - qdl_sim->vip_hash_count;
}
memcpy(qdl_sim->vip_hashes[qdl_sim->vip_hash_count],
hashes, data_count * SHA256_DIGEST_LENGTH);
qdl_sim->vip_hash_count += data_count;
ux_debug("sim: loaded %zu VIP chunk hashes from %s table "
"(total: %zu)\n",
data_count, is_signed ? "signed" : "chained",
qdl_sim->vip_hash_count);
}
/*
* sim_vip_check_chunk() - SHA256 @buf/@len and verify against the next entry
*
* Called once per VIP chunk (one XML command write or one raw-data write).
* A mismatch is reported via ux_err() but does not abort, so that all
* mismatches across a full flash run are visible at once.
*/
static void sim_vip_check_chunk(struct qdl_device_sim *qdl_sim,
const void *buf, size_t len)
{
uint8_t hash[SHA256_DIGEST_LENGTH];
char got_hex[SHA256_DIGEST_STRING_LENGTH];
char exp_hex[SHA256_DIGEST_STRING_LENGTH];
const uint8_t *expected;
SHA2_CTX ctx;
size_t i;
if (qdl_sim->create_digests || !qdl_sim->vip_hash_count)
return;
SHA256Init(&ctx);
SHA256Update(&ctx, buf, len);
SHA256Final(hash, &ctx);
if (qdl_sim->vip_hash_idx >= qdl_sim->vip_hash_count) {
ux_err("sim: VIP chunk %zu has no entry in the digest table "
"(table has %zu entries)\n",
qdl_sim->vip_hash_idx, qdl_sim->vip_hash_count);
qdl_sim->vip_hash_idx++;
return;
}
expected = qdl_sim->vip_hashes[qdl_sim->vip_hash_idx];
if (memcmp(hash, expected, SHA256_DIGEST_LENGTH) == 0) {
ux_debug("sim: VIP chunk %zu hash OK\n",
qdl_sim->vip_hash_idx);
qdl_sim->vip_hash_idx++;
return;
}
for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
sprintf(got_hex + i * 2, "%02x", hash[i]);
sprintf(exp_hex + i * 2, "%02x", expected[i]);
}
got_hex[SHA256_DIGEST_STRING_LENGTH - 1] = '\0';
exp_hex[SHA256_DIGEST_STRING_LENGTH - 1] = '\0';
ux_err("sim: VIP hash mismatch for chunk %zu\n"
" computed: %s\n"
" expected: %s\n",
qdl_sim->vip_hash_idx, got_hex, exp_hex);
qdl_sim->vip_hash_idx++;
}
static int sim_open(struct qdl_device *qdl __unused,
const char *serial __unused)
{
ux_info("This is a dry-run execution of QDL. No actual flashing has been performed\n");
return 0;
}
static void sim_close(struct qdl_device *qdl __unused) {}
/*
* sim_read() - serve the next queued XML response or raw-out data
*
* Returns the number of bytes written to @buf, or a negative errno.
* Returns -ETIMEDOUT when the response queue is empty (mirroring the
* behaviour of the USB driver when no data arrives within the timeout).
* Returns -EIO after a power/reset command to let the caller drain quickly.
*/
static int sim_read(struct qdl_device *qdl, void *buf, size_t len,
unsigned int timeout __unused)
{
struct qdl_device_sim *qdl_sim = container_of(qdl, struct qdl_device_sim, base);
struct sim_response *resp;
size_t copy_len;
/*
* Always drain the response queue first. This matters for read
* commands: sim_write() sets SIM_STATE_RAW_OUT and queues the
* rawmode=true ACK at the same time, so the pending XML must be
* served before we start sending raw sector data.
*/
resp = qdl_sim->resp_head;
if (resp) {
copy_len = resp->len;
memcpy(buf, resp->data, copy_len);
qdl_sim->resp_head = resp->next;
if (!qdl_sim->resp_head)
qdl_sim->resp_tail = NULL;
free(resp->data);
free(resp);
return copy_len;
}
/*
* Queue empty and in raw-out mode: the host has received the
* rawmode=true ACK and is now reading sector data. Serve zeros
* and enqueue the final rawmode=false ACK when all sectors are done.
*/
if (qdl_sim->state == SIM_STATE_RAW_OUT) {
copy_len = MIN(len, qdl_sim->raw_remaining);
memset(buf, 0, copy_len);
qdl_sim->raw_remaining -= copy_len;
if (qdl_sim->raw_remaining == 0) {
sim_enqueue(qdl_sim, SIM_ACK);
qdl_sim->state = SIM_STATE_XML;
}
return copy_len;
}
/*
* Queue is empty. Return -EIO after power/reset so that callers
* which drain trailing log messages after the final ACK return
* immediately instead of spinning until the timeout expires.
*/
if (qdl_sim->closed)
return -EIO;
return -ETIMEDOUT;
}
/*
* sim_write() - accept a host write and queue the matching device response(s)
*
* In SIM_STATE_RAW_IN the simulator counts down the expected raw payload and
* queues the final rawmode=false ACK once the last byte arrives.
*
* In SIM_STATE_XML the XML command is parsed and one or more response messages
* are pushed onto the queue for sim_read() to serve back.
*
* In dry-run mode without digest generation, binary writes that fail XML
* parsing are treated as VIP digest tables: sim_vip_recv_table() extracts the
* chunk hashes and an ACK is enqueued so that the host's post-send
* firehose_read() succeeds. Every subsequent XML command write and every
* raw-data write is then verified against the stored hashes by
* sim_vip_check_chunk().
*/
static int sim_write(struct qdl_device *qdl, const void *buf, size_t len,
unsigned int timeout __unused)
{
struct qdl_device_sim *qdl_sim = container_of(qdl, struct qdl_device_sim, base);
unsigned int num_sectors, sector_size;
xmlNode *root, *node, *child;
xmlDoc *doc;
/*
* VIP table writes (both the signed MBN and chained raw binaries) may
* arrive while SIM_STATE_RAW_IN is active, interleaved between data
* chunks of the same program operation. Intercept them here using the
* sending_table flag set exclusively by vip_transfer_send_raw(), before
* the raw-data accounting branch below.
*/
if (!qdl_sim->create_digests &&
qdl_sim->base.vip_data.sending_table) {
bool is_signed = (qdl_sim->base.vip_data.state == VIP_INIT);
sim_vip_recv_table(qdl_sim, buf, len, is_signed);
sim_enqueue(qdl_sim, SIM_ACK);
return len;
}
/* Raw binary payload for an ongoing program operation */
if (qdl_sim->state == SIM_STATE_RAW_IN) {
/*
* Each qdl_write() call maps to exactly one VIP chunk
* (firehose_do_program() loops over max_payload_size chunks,
* calling vip_gen_chunk_init/update/store once per iteration).
* Hash the entire write and verify against the digest table.
*/
sim_vip_check_chunk(qdl_sim, buf, len);
if (len >= qdl_sim->raw_remaining) {
sim_enqueue(qdl_sim, SIM_ACK);
qdl_sim->state = SIM_STATE_XML;
qdl_sim->raw_remaining = 0;
} else {
qdl_sim->raw_remaining -= len;
}
return len;
}
/* XML command: parse and enqueue appropriate response(s) */
doc = xmlReadMemory(buf, len, NULL, NULL,
XML_PARSE_NOWARNING | XML_PARSE_NOERROR);
if (!doc)
return len;
/*
* Valid XML command: verify it against the digest table before
* dispatching so that the hash index stays in step with the
* vip_gen_chunk_store() calls made during the dry-run that
* produced the table.
*/
sim_vip_check_chunk(qdl_sim, buf, len);
root = xmlDocGetRootElement(doc);
/* Locate the <data> element */
for (node = root; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE &&
xmlStrcmp(node->name, (xmlChar *)"data") == 0)
break;
}
if (!node)
goto out;
/* First child element is the command */
for (child = node->children;
child && child->type != XML_ELEMENT_NODE;
child = child->next)
;
if (!child)
goto out;
if (xmlStrcmp(child->name, (xmlChar *)"configure") == 0) {
sim_enqueue_log(qdl_sim, "configure");
sim_enqueue(qdl_sim, SIM_CONFIGURE_ACK);
} else if (xmlStrcmp(child->name, (xmlChar *)"program") == 0) {
num_sectors = sim_get_uint_attr(child, "num_partition_sectors");
sector_size = sim_get_uint_attr(child, "SECTOR_SIZE_IN_BYTES");
sim_enqueue_log(qdl_sim, "program");
sim_enqueue(qdl_sim, SIM_ACK_RAWMODE);
qdl_sim->state = SIM_STATE_RAW_IN;
qdl_sim->raw_remaining = (size_t)num_sectors * sector_size;
} else if (xmlStrcmp(child->name, (xmlChar *)"read") == 0) {
num_sectors = sim_get_uint_attr(child, "num_partition_sectors");
sector_size = sim_get_uint_attr(child, "SECTOR_SIZE_IN_BYTES");
sim_enqueue_log(qdl_sim, "read");
sim_enqueue(qdl_sim, SIM_ACK_RAWMODE);
qdl_sim->state = SIM_STATE_RAW_OUT;
qdl_sim->raw_remaining = (size_t)num_sectors * sector_size;
} else if (xmlStrcmp(child->name, (xmlChar *)"erase") == 0) {
sim_enqueue_log(qdl_sim, "erase");
sim_enqueue(qdl_sim, SIM_ACK);
} else if (xmlStrcmp(child->name, (xmlChar *)"patch") == 0) {
sim_enqueue_log(qdl_sim, "patch");
sim_enqueue(qdl_sim, SIM_ACK);
} else if (xmlStrcmp(child->name, (xmlChar *)"setbootablestoragedrive") == 0) {
sim_enqueue_log(qdl_sim, "setbootablestoragedrive");
sim_enqueue(qdl_sim, SIM_ACK);
} else if (xmlStrcmp(child->name, (xmlChar *)"power") == 0) {
sim_enqueue_log(qdl_sim, "power");
sim_enqueue(qdl_sim, SIM_ACK);
/*
* Mark the sim closed so that the trailing drain read in
* firehose_reset() returns -EIO immediately rather than
* spinning until the timeout expires.
*/
qdl_sim->closed = true;
} else if (xmlStrcmp(child->name, (xmlChar *)"ufs") == 0) {
sim_enqueue_log(qdl_sim, "ufs");
sim_enqueue(qdl_sim, SIM_ACK);
} else {
/* Unknown command: respond with a generic ACK */
sim_enqueue(qdl_sim, SIM_ACK);
}
out:
xmlFreeDoc(doc);
return len;
}
static void sim_set_out_chunk_size(struct qdl_device *qdl __unused,
long size __unused)
{}
struct qdl_device *sim_init(void)
{
struct qdl_device *qdl = malloc(sizeof(struct qdl_device_sim));
if (!qdl)
return NULL;
memset(qdl, 0, sizeof(struct qdl_device_sim));
qdl->dev_type = QDL_DEVICE_SIM;
qdl->open = sim_open;
qdl->read = sim_read;
qdl->write = sim_write;
qdl->close = sim_close;
qdl->set_out_chunk_size = sim_set_out_chunk_size;
qdl->max_payload_size = 1048576;
/*
* Pre-set a non-zero sector size so that firehose_try_configure()
* skips the sector-size probe reads. Those probe commands would
* otherwise be hashed into the VIP digest table during dry-run
* digest generation (--createdigest), but they are never sent
* during the real VIP flash -- causing a hash mismatch.
*/
qdl->sector_size = 4096;
return qdl;
}
struct vip_table_generator *sim_get_vip_generator(struct qdl_device *qdl)
{
struct qdl_device_sim *qdl_sim;
if (qdl->dev_type != QDL_DEVICE_SIM)
return NULL;
qdl_sim = container_of(qdl, struct qdl_device_sim, base);
if (!qdl_sim->create_digests)
return NULL;
return qdl_sim->vip_gen;
}
bool sim_set_digest_generation(bool create_digests, struct qdl_device *qdl,
struct vip_table_generator *vip_gen)
{
struct qdl_device_sim *qdl_sim;
if (qdl->dev_type != QDL_DEVICE_SIM)
return false;
qdl_sim = container_of(qdl, struct qdl_device_sim, base);
qdl_sim->create_digests = create_digests;
qdl_sim->vip_gen = vip_gen;
return true;
}