2 * QTest testcase for parallel flash with AMD command set
4 * Copyright (c) 2019 Stephen Checkoway
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
10 #include "qemu/osdep.h"
14 * To test the pflash_cfi02 device, we run QEMU with the musicpal machine with
15 * a pflash drive. This enables us to test some flash configurations, but not
16 * all. In particular, we're limited to a 16-bit wide flash device.
19 #define MP_FLASH_SIZE_MAX (32 * 1024 * 1024)
20 #define BASE_ADDR (0x100000000ULL - MP_FLASH_SIZE_MAX)
22 #define UNIFORM_FLASH_SIZE (8 * 1024 * 1024)
23 #define UNIFORM_FLASH_SECTOR_SIZE (64 * 1024)
25 /* Use a newtype to keep flash addresses separate from byte addresses. */
29 #define FLASH_ADDR(x) ((faddr) { .addr = (x) })
31 #define CFI_ADDR FLASH_ADDR(0x55)
32 #define UNLOCK0_ADDR FLASH_ADDR(0x555)
33 #define UNLOCK1_ADDR FLASH_ADDR(0x2AA)
36 #define UNLOCK0_CMD 0xAA
37 #define UNLOCK1_CMD 0x55
38 #define SECOND_UNLOCK_CMD 0x80
39 #define AUTOSELECT_CMD 0x90
40 #define RESET_CMD 0xF0
41 #define PROGRAM_CMD 0xA0
42 #define SECTOR_ERASE_CMD 0x30
43 #define CHIP_ERASE_CMD 0x10
44 #define UNLOCK_BYPASS_CMD 0x20
45 #define UNLOCK_BYPASS_RESET_CMD 0x00
46 #define ERASE_SUSPEND_CMD 0xB0
47 #define ERASE_RESUME_CMD SECTOR_ERASE_CMD
52 /* Nonuniform block size. */
59 static char image_path
[] = "/tmp/qtest.XXXXXX";
62 * The pflash implementation allows some parameters to be unspecified. We want
63 * to test those configurations but we also need to know the real values in
64 * our testing code. So after we launch qemu, we'll need a new FlashConfig
65 * with the correct values filled in.
67 static FlashConfig
expand_config_defaults(const FlashConfig
*c
)
71 if (ret
.bank_width
== 0) {
74 if (ret
.nb_blocs
[0] == 0 && ret
.sector_len
[0] == 0) {
75 ret
.sector_len
[0] = UNIFORM_FLASH_SECTOR_SIZE
;
76 ret
.nb_blocs
[0] = UNIFORM_FLASH_SIZE
/ UNIFORM_FLASH_SECTOR_SIZE
;
79 /* XXX: Limitations of test harness. */
80 assert(ret
.bank_width
== 2);
85 * Return a bit mask suitable for extracting the least significant
86 * status/query response from an interleaved response.
88 static inline uint64_t device_mask(const FlashConfig
*c
)
94 * Return a bit mask exactly as long as the bank_width.
96 static inline uint64_t bank_mask(const FlashConfig
*c
)
98 if (c
->bank_width
== 8) {
101 return (1ULL << (c
->bank_width
* 8)) - 1ULL;
104 static inline void flash_write(const FlashConfig
*c
, uint64_t byte_addr
,
107 /* Sanity check our tests. */
108 assert((data
& ~bank_mask(c
)) == 0);
109 uint64_t addr
= BASE_ADDR
+ byte_addr
;
110 switch (c
->bank_width
) {
112 qtest_writeb(c
->qtest
, addr
, data
);
115 qtest_writew(c
->qtest
, addr
, data
);
118 qtest_writel(c
->qtest
, addr
, data
);
121 qtest_writeq(c
->qtest
, addr
, data
);
128 static inline uint64_t flash_read(const FlashConfig
*c
, uint64_t byte_addr
)
130 uint64_t addr
= BASE_ADDR
+ byte_addr
;
131 switch (c
->bank_width
) {
133 return qtest_readb(c
->qtest
, addr
);
135 return qtest_readw(c
->qtest
, addr
);
137 return qtest_readl(c
->qtest
, addr
);
139 return qtest_readq(c
->qtest
, addr
);
146 * Convert a flash address expressed in the maximum width of the device as a
149 static inline uint64_t as_byte_addr(const FlashConfig
*c
, faddr flash_addr
)
152 * Command addresses are always given as addresses in the maximum
153 * supported bus size for the flash chip. So an x8/x16 chip in x8 mode
154 * uses addresses 0xAAA and 0x555 to unlock because the least significant
155 * bit is ignored. (0x555 rather than 0x554 is traditional.)
157 * In general we need to multiply by the maximum device width.
159 return flash_addr
.addr
* c
->bank_width
;
163 * Return the command value or expected status replicated across all devices.
165 static inline uint64_t replicate(const FlashConfig
*c
, uint64_t data
)
167 /* Sanity check our tests. */
168 assert((data
& ~device_mask(c
)) == 0);
172 static inline void flash_cmd(const FlashConfig
*c
, faddr cmd_addr
,
175 flash_write(c
, as_byte_addr(c
, cmd_addr
), replicate(c
, cmd
));
178 static inline uint64_t flash_query(const FlashConfig
*c
, faddr query_addr
)
180 return flash_read(c
, as_byte_addr(c
, query_addr
));
183 static inline uint64_t flash_query_1(const FlashConfig
*c
, faddr query_addr
)
185 return flash_query(c
, query_addr
) & device_mask(c
);
188 static void unlock(const FlashConfig
*c
)
190 flash_cmd(c
, UNLOCK0_ADDR
, UNLOCK0_CMD
);
191 flash_cmd(c
, UNLOCK1_ADDR
, UNLOCK1_CMD
);
194 static void reset(const FlashConfig
*c
)
196 flash_cmd(c
, FLASH_ADDR(0), RESET_CMD
);
199 static void sector_erase(const FlashConfig
*c
, uint64_t byte_addr
)
202 flash_cmd(c
, UNLOCK0_ADDR
, SECOND_UNLOCK_CMD
);
204 flash_write(c
, byte_addr
, replicate(c
, SECTOR_ERASE_CMD
));
207 static void wait_for_completion(const FlashConfig
*c
, uint64_t byte_addr
)
209 /* If DQ6 is toggling, step the clock and ensure the toggle stops. */
210 const uint64_t dq6
= replicate(c
, 0x40);
211 if ((flash_read(c
, byte_addr
) & dq6
) ^ (flash_read(c
, byte_addr
) & dq6
)) {
212 /* Wait for erase or program to finish. */
213 qtest_clock_step_next(c
->qtest
);
214 /* Ensure that DQ6 has stopped toggling. */
215 g_assert_cmphex(flash_read(c
, byte_addr
), ==, flash_read(c
, byte_addr
));
219 static void bypass_program(const FlashConfig
*c
, uint64_t byte_addr
,
222 flash_cmd(c
, UNLOCK0_ADDR
, PROGRAM_CMD
);
223 flash_write(c
, byte_addr
, data
);
225 * Data isn't valid until DQ6 stops toggling. We don't model this as
226 * writes are immediate, but if this changes in the future, we can wait
227 * until the program is complete.
229 wait_for_completion(c
, byte_addr
);
232 static void program(const FlashConfig
*c
, uint64_t byte_addr
, uint16_t data
)
235 bypass_program(c
, byte_addr
, data
);
238 static void chip_erase(const FlashConfig
*c
)
241 flash_cmd(c
, UNLOCK0_ADDR
, SECOND_UNLOCK_CMD
);
243 flash_cmd(c
, UNLOCK0_ADDR
, CHIP_ERASE_CMD
);
246 static void erase_suspend(const FlashConfig
*c
)
248 flash_cmd(c
, FLASH_ADDR(0), ERASE_SUSPEND_CMD
);
251 static void erase_resume(const FlashConfig
*c
)
253 flash_cmd(c
, FLASH_ADDR(0), ERASE_RESUME_CMD
);
257 * Test flash commands with a variety of device geometry.
259 static void test_geometry(const void *opaque
)
261 const FlashConfig
*config
= opaque
;
263 qtest
= qtest_initf("-M musicpal,accel=qtest"
264 " -drive if=pflash,file=%s,format=raw,copy-on-read"
265 /* Device geometry properties. */
266 " -global driver=cfi.pflash02,"
267 "property=num-blocks0,value=%d"
268 " -global driver=cfi.pflash02,"
269 "property=sector-length0,value=%d"
270 " -global driver=cfi.pflash02,"
271 "property=num-blocks1,value=%d"
272 " -global driver=cfi.pflash02,"
273 "property=sector-length1,value=%d"
274 " -global driver=cfi.pflash02,"
275 "property=num-blocks2,value=%d"
276 " -global driver=cfi.pflash02,"
277 "property=sector-length2,value=%d"
278 " -global driver=cfi.pflash02,"
279 "property=num-blocks3,value=%d"
280 " -global driver=cfi.pflash02,"
281 "property=sector-length3,value=%d",
284 config
->sector_len
[0],
286 config
->sector_len
[1],
288 config
->sector_len
[2],
290 config
->sector_len
[3]);
291 FlashConfig explicit_config
= expand_config_defaults(config
);
292 explicit_config
.qtest
= qtest
;
293 const FlashConfig
*c
= &explicit_config
;
297 flash_cmd(c
, UNLOCK0_ADDR
, AUTOSELECT_CMD
);
298 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
299 if (c
->bank_width
>= 2) {
301 * XXX: The ID returned by the musicpal flash chip is 16 bits which
302 * wouldn't happen with an 8-bit device. It would probably be best to
303 * prohibit addresses larger than the device width in pflash_cfi02.c,
304 * but then we couldn't test smaller device widths at all.
306 g_assert_cmphex(flash_query(c
, FLASH_ADDR(1)), ==,
307 replicate(c
, 0x236D));
311 /* Check the erase blocks. */
312 flash_cmd(c
, CFI_ADDR
, CFI_CMD
);
313 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x10)), ==, replicate(c
, 'Q'));
314 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x11)), ==, replicate(c
, 'R'));
315 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x12)), ==, replicate(c
, 'Y'));
317 /* Num erase regions. */
318 int nb_erase_regions
= flash_query_1(c
, FLASH_ADDR(0x2C));
319 g_assert_cmphex(nb_erase_regions
, ==,
320 !!c
->nb_blocs
[0] + !!c
->nb_blocs
[1] + !!c
->nb_blocs
[2] +
323 /* Check device length. */
324 uint32_t device_len
= 1 << flash_query_1(c
, FLASH_ADDR(0x27));
325 g_assert_cmphex(device_len
, ==, UNIFORM_FLASH_SIZE
);
327 /* Check that erase suspend to read/write is supported. */
328 uint16_t pri
= flash_query_1(c
, FLASH_ADDR(0x15)) +
329 (flash_query_1(c
, FLASH_ADDR(0x16)) << 8);
330 g_assert_cmpint(pri
, >=, 0x2D + 4 * nb_erase_regions
);
331 g_assert_cmpint(flash_query(c
, FLASH_ADDR(pri
+ 0)), ==, replicate(c
, 'P'));
332 g_assert_cmpint(flash_query(c
, FLASH_ADDR(pri
+ 1)), ==, replicate(c
, 'R'));
333 g_assert_cmpint(flash_query(c
, FLASH_ADDR(pri
+ 2)), ==, replicate(c
, 'I'));
334 g_assert_cmpint(flash_query_1(c
, FLASH_ADDR(pri
+ 6)), ==, 2); /* R/W */
337 const uint64_t dq7
= replicate(c
, 0x80);
338 const uint64_t dq6
= replicate(c
, 0x40);
339 const uint64_t dq3
= replicate(c
, 0x08);
340 const uint64_t dq2
= replicate(c
, 0x04);
342 uint64_t byte_addr
= 0;
343 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
344 uint64_t base
= 0x2D + 4 * region
;
345 flash_cmd(c
, CFI_ADDR
, CFI_CMD
);
346 uint32_t nb_sectors
= flash_query_1(c
, FLASH_ADDR(base
+ 0)) +
347 (flash_query_1(c
, FLASH_ADDR(base
+ 1)) << 8) + 1;
348 uint32_t sector_len
= (flash_query_1(c
, FLASH_ADDR(base
+ 2)) << 8) +
349 (flash_query_1(c
, FLASH_ADDR(base
+ 3)) << 16);
350 g_assert_cmphex(nb_sectors
, ==, c
->nb_blocs
[region
]);
351 g_assert_cmphex(sector_len
, ==, c
->sector_len
[region
]);
354 /* Erase and program sector. */
355 for (uint32_t i
= 0; i
< nb_sectors
; ++i
) {
356 sector_erase(c
, byte_addr
);
358 /* Check that DQ3 is 0. */
359 g_assert_cmphex(flash_read(c
, byte_addr
) & dq3
, ==, 0);
360 qtest_clock_step_next(c
->qtest
); /* Step over the 50 us timeout. */
362 /* Check that DQ3 is 1. */
363 uint64_t status0
= flash_read(c
, byte_addr
);
364 g_assert_cmphex(status0
& dq3
, ==, dq3
);
366 /* DQ7 is 0 during an erase. */
367 g_assert_cmphex(status0
& dq7
, ==, 0);
368 uint64_t status1
= flash_read(c
, byte_addr
);
370 /* DQ6 toggles during an erase. */
371 g_assert_cmphex(status0
& dq6
, ==, ~status1
& dq6
);
373 /* Wait for erase to complete. */
374 wait_for_completion(c
, byte_addr
);
376 /* Ensure DQ6 has stopped toggling. */
377 g_assert_cmphex(flash_read(c
, byte_addr
), ==,
378 flash_read(c
, byte_addr
));
380 /* Now the data should be valid. */
381 g_assert_cmphex(flash_read(c
, byte_addr
), ==, bank_mask(c
));
383 /* Program a bit pattern. */
384 program(c
, byte_addr
, 0x55);
385 g_assert_cmphex(flash_read(c
, byte_addr
) & 0xFF, ==, 0x55);
386 program(c
, byte_addr
, 0xA5);
387 g_assert_cmphex(flash_read(c
, byte_addr
) & 0xFF, ==, 0x05);
388 byte_addr
+= sector_len
;
392 /* Erase the chip. */
395 uint64_t status0
= flash_read(c
, 0);
396 /* DQ7 is 0 during an erase. */
397 g_assert_cmphex(status0
& dq7
, ==, 0);
398 uint64_t status1
= flash_read(c
, 0);
399 /* DQ6 toggles during an erase. */
400 g_assert_cmphex(status0
& dq6
, ==, ~status1
& dq6
);
401 /* Wait for erase to complete. */
402 qtest_clock_step_next(c
->qtest
);
403 /* Ensure DQ6 has stopped toggling. */
404 g_assert_cmphex(flash_read(c
, 0), ==, flash_read(c
, 0));
405 /* Now the data should be valid. */
407 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
408 for (uint32_t i
= 0; i
< c
->nb_blocs
[region
]; ++i
) {
409 uint64_t byte_addr
= i
* c
->sector_len
[region
];
410 g_assert_cmphex(flash_read(c
, byte_addr
), ==, bank_mask(c
));
416 flash_cmd(c
, UNLOCK0_ADDR
, UNLOCK_BYPASS_CMD
);
417 bypass_program(c
, 0 * c
->bank_width
, 0x01);
418 bypass_program(c
, 1 * c
->bank_width
, 0x23);
419 bypass_program(c
, 2 * c
->bank_width
, 0x45);
421 * Test that bypass programming, unlike normal programming can use any
422 * address for the PROGRAM_CMD.
424 flash_cmd(c
, FLASH_ADDR(3 * c
->bank_width
), PROGRAM_CMD
);
425 flash_write(c
, 3 * c
->bank_width
, 0x67);
426 wait_for_completion(c
, 3 * c
->bank_width
);
427 flash_cmd(c
, FLASH_ADDR(0), UNLOCK_BYPASS_RESET_CMD
);
428 bypass_program(c
, 4 * c
->bank_width
, 0x89); /* Should fail. */
429 g_assert_cmphex(flash_read(c
, 0 * c
->bank_width
), ==, 0x01);
430 g_assert_cmphex(flash_read(c
, 1 * c
->bank_width
), ==, 0x23);
431 g_assert_cmphex(flash_read(c
, 2 * c
->bank_width
), ==, 0x45);
432 g_assert_cmphex(flash_read(c
, 3 * c
->bank_width
), ==, 0x67);
433 g_assert_cmphex(flash_read(c
, 4 * c
->bank_width
), ==, bank_mask(c
));
435 /* Test ignored high order bits of address. */
436 flash_cmd(c
, FLASH_ADDR(0x5555), UNLOCK0_CMD
);
437 flash_cmd(c
, FLASH_ADDR(0x2AAA), UNLOCK1_CMD
);
438 flash_cmd(c
, FLASH_ADDR(0x5555), AUTOSELECT_CMD
);
439 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
443 * Program a word on each sector, erase one or two sectors per region, and
444 * verify that all of those, and only those, are erased.
447 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
448 for (int i
= 0; i
< config
->nb_blocs
[region
]; ++i
) {
449 program(c
, byte_addr
, 0);
450 byte_addr
+= config
->sector_len
[region
];
454 flash_cmd(c
, UNLOCK0_ADDR
, SECOND_UNLOCK_CMD
);
457 const uint64_t erase_cmd
= replicate(c
, SECTOR_ERASE_CMD
);
458 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
459 flash_write(c
, byte_addr
, erase_cmd
);
460 if (c
->nb_blocs
[region
] > 1) {
461 flash_write(c
, byte_addr
+ c
->sector_len
[region
], erase_cmd
);
463 byte_addr
+= c
->sector_len
[region
] * c
->nb_blocs
[region
];
466 qtest_clock_step_next(c
->qtest
); /* Step over the 50 us timeout. */
467 wait_for_completion(c
, 0);
469 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
470 for (int i
= 0; i
< config
->nb_blocs
[region
]; ++i
) {
472 g_assert_cmphex(flash_read(c
, byte_addr
), ==, bank_mask(c
));
474 g_assert_cmphex(flash_read(c
, byte_addr
), ==, 0);
476 byte_addr
+= config
->sector_len
[region
];
480 /* Test erase suspend/resume during erase timeout. */
483 * Check that DQ 3 is 0 and DQ6 and DQ2 are toggling in the sector being
484 * erased as well as in a sector not being erased.
486 byte_addr
= c
->sector_len
[0];
487 status0
= flash_read(c
, 0);
488 status1
= flash_read(c
, 0);
489 g_assert_cmpint(status0
& dq3
, ==, 0);
490 g_assert_cmpint(status0
& dq6
, ==, ~status1
& dq6
);
491 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
492 status0
= flash_read(c
, byte_addr
);
493 status1
= flash_read(c
, byte_addr
);
494 g_assert_cmpint(status0
& dq3
, ==, 0);
495 g_assert_cmpint(status0
& dq6
, ==, ~status1
& dq6
);
496 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
499 * Check that after suspending, DQ6 does not toggle but DQ2 does toggle in
500 * an erase suspended sector but that neither toggle (we should be
501 * getting data) in a sector not being erased.
504 status0
= flash_read(c
, 0);
505 status1
= flash_read(c
, 0);
506 g_assert_cmpint(status0
& dq6
, ==, status1
& dq6
);
507 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
508 g_assert_cmpint(flash_read(c
, byte_addr
), ==, flash_read(c
, byte_addr
));
510 /* Check that after resuming, DQ3 is 1 and DQ6 and DQ2 toggle. */
512 status0
= flash_read(c
, 0);
513 status1
= flash_read(c
, 0);
514 g_assert_cmpint(status0
& dq3
, ==, dq3
);
515 g_assert_cmpint(status0
& dq6
, ==, ~status1
& dq6
);
516 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
517 status0
= flash_read(c
, byte_addr
);
518 status1
= flash_read(c
, byte_addr
);
519 g_assert_cmpint(status0
& dq3
, ==, dq3
);
520 g_assert_cmpint(status0
& dq6
, ==, ~status1
& dq6
);
521 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
522 wait_for_completion(c
, 0);
524 /* Repeat this process but this time suspend after the timeout. */
526 qtest_clock_step_next(c
->qtest
);
528 * Check that DQ 3 is 1 and DQ6 and DQ2 are toggling in the sector being
529 * erased as well as in a sector not being erased.
531 byte_addr
= c
->sector_len
[0];
532 status0
= flash_read(c
, 0);
533 status1
= flash_read(c
, 0);
534 g_assert_cmpint(status0
& dq3
, ==, dq3
);
535 g_assert_cmpint(status0
& dq6
, ==, ~status1
& dq6
);
536 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
537 status0
= flash_read(c
, byte_addr
);
538 status1
= flash_read(c
, byte_addr
);
539 g_assert_cmpint(status0
& dq3
, ==, dq3
);
540 g_assert_cmpint(status0
& dq6
, ==, ~status1
& dq6
);
541 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
544 * Check that after suspending, DQ6 does not toggle but DQ2 does toggle in
545 * an erase suspended sector but that neither toggle (we should be
546 * getting data) in a sector not being erased.
549 status0
= flash_read(c
, 0);
550 status1
= flash_read(c
, 0);
551 g_assert_cmpint(status0
& dq6
, ==, status1
& dq6
);
552 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
553 g_assert_cmpint(flash_read(c
, byte_addr
), ==, flash_read(c
, byte_addr
));
555 /* Check that after resuming, DQ3 is 1 and DQ6 and DQ2 toggle. */
557 status0
= flash_read(c
, 0);
558 status1
= flash_read(c
, 0);
559 g_assert_cmpint(status0
& dq3
, ==, dq3
);
560 g_assert_cmpint(status0
& dq6
, ==, ~status1
& dq6
);
561 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
562 status0
= flash_read(c
, byte_addr
);
563 status1
= flash_read(c
, byte_addr
);
564 g_assert_cmpint(status0
& dq3
, ==, dq3
);
565 g_assert_cmpint(status0
& dq6
, ==, ~status1
& dq6
);
566 g_assert_cmpint(status0
& dq2
, ==, ~status1
& dq2
);
567 wait_for_completion(c
, 0);
574 * 1. enter autoselect mode;
575 * 2. enter CFI mode; and then
577 * leaves the flash device in autoselect mode.
579 static void test_cfi_in_autoselect(const void *opaque
)
581 const FlashConfig
*config
= opaque
;
583 qtest
= qtest_initf("-M musicpal,accel=qtest"
584 " -drive if=pflash,file=%s,format=raw,copy-on-read",
586 FlashConfig explicit_config
= expand_config_defaults(config
);
587 explicit_config
.qtest
= qtest
;
588 const FlashConfig
*c
= &explicit_config
;
590 /* 1. Enter autoselect. */
592 flash_cmd(c
, UNLOCK0_ADDR
, AUTOSELECT_CMD
);
593 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
596 flash_cmd(c
, CFI_ADDR
, CFI_CMD
);
597 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x10)), ==, replicate(c
, 'Q'));
598 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x11)), ==, replicate(c
, 'R'));
599 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x12)), ==, replicate(c
, 'Y'));
603 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
608 static void cleanup(void *opaque
)
614 * XXX: Tests are limited to bank_width = 2 for now because that's what
615 * hw/arm/musicpal.c has.
617 static const FlashConfig configuration
[] = {
618 /* One x16 device. */
622 /* Nonuniform sectors (top boot). */
625 .nb_blocs
= { 127, 1, 2, 1 },
626 .sector_len
= { 0x10000, 0x08000, 0x02000, 0x04000 },
628 /* Nonuniform sectors (bottom boot). */
631 .nb_blocs
= { 1, 2, 1, 127 },
632 .sector_len
= { 0x04000, 0x02000, 0x08000, 0x10000 },
636 int main(int argc
, char **argv
)
638 int fd
= mkstemp(image_path
);
640 g_printerr("Failed to create temporary file %s: %s\n", image_path
,
644 if (ftruncate(fd
, UNIFORM_FLASH_SIZE
) < 0) {
645 int error_code
= errno
;
648 g_printerr("Failed to truncate file %s to %u MB: %s\n", image_path
,
649 UNIFORM_FLASH_SIZE
, strerror(error_code
));
654 qtest_add_abrt_handler(cleanup
, NULL
);
655 g_test_init(&argc
, &argv
, NULL
);
657 size_t nb_configurations
= sizeof configuration
/ sizeof configuration
[0];
658 for (size_t i
= 0; i
< nb_configurations
; ++i
) {
659 const FlashConfig
*config
= &configuration
[i
];
660 char *path
= g_strdup_printf("pflash-cfi02"
661 "/geometry/%dx%x-%dx%x-%dx%x-%dx%x"
664 config
->sector_len
[0],
666 config
->sector_len
[1],
668 config
->sector_len
[2],
670 config
->sector_len
[3],
672 qtest_add_data_func(path
, config
, test_geometry
);
676 qtest_add_data_func("pflash-cfi02/cfi-in-autoselect", &configuration
[0],
677 test_cfi_in_autoselect
);
678 int result
= g_test_run();