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
50 /* Nonuniform block size. */
57 static char image_path
[] = "/tmp/qtest.XXXXXX";
60 * The pflash implementation allows some parameters to be unspecified. We want
61 * to test those configurations but we also need to know the real values in
62 * our testing code. So after we launch qemu, we'll need a new FlashConfig
63 * with the correct values filled in.
65 static FlashConfig
expand_config_defaults(const FlashConfig
*c
)
69 if (ret
.bank_width
== 0) {
72 if (ret
.nb_blocs
[0] == 0 && ret
.sector_len
[0] == 0) {
73 ret
.sector_len
[0] = UNIFORM_FLASH_SECTOR_SIZE
;
74 ret
.nb_blocs
[0] = UNIFORM_FLASH_SIZE
/ UNIFORM_FLASH_SECTOR_SIZE
;
77 /* XXX: Limitations of test harness. */
78 assert(ret
.bank_width
== 2);
83 * Return a bit mask suitable for extracting the least significant
84 * status/query response from an interleaved response.
86 static inline uint64_t device_mask(const FlashConfig
*c
)
92 * Return a bit mask exactly as long as the bank_width.
94 static inline uint64_t bank_mask(const FlashConfig
*c
)
96 if (c
->bank_width
== 8) {
99 return (1ULL << (c
->bank_width
* 8)) - 1ULL;
102 static inline void flash_write(const FlashConfig
*c
, uint64_t byte_addr
,
105 /* Sanity check our tests. */
106 assert((data
& ~bank_mask(c
)) == 0);
107 uint64_t addr
= BASE_ADDR
+ byte_addr
;
108 switch (c
->bank_width
) {
110 qtest_writeb(c
->qtest
, addr
, data
);
113 qtest_writew(c
->qtest
, addr
, data
);
116 qtest_writel(c
->qtest
, addr
, data
);
119 qtest_writeq(c
->qtest
, addr
, data
);
126 static inline uint64_t flash_read(const FlashConfig
*c
, uint64_t byte_addr
)
128 uint64_t addr
= BASE_ADDR
+ byte_addr
;
129 switch (c
->bank_width
) {
131 return qtest_readb(c
->qtest
, addr
);
133 return qtest_readw(c
->qtest
, addr
);
135 return qtest_readl(c
->qtest
, addr
);
137 return qtest_readq(c
->qtest
, addr
);
144 * Convert a flash address expressed in the maximum width of the device as a
147 static inline uint64_t as_byte_addr(const FlashConfig
*c
, faddr flash_addr
)
150 * Command addresses are always given as addresses in the maximum
151 * supported bus size for the flash chip. So an x8/x16 chip in x8 mode
152 * uses addresses 0xAAA and 0x555 to unlock because the least significant
153 * bit is ignored. (0x555 rather than 0x554 is traditional.)
155 * In general we need to multiply by the maximum device width.
157 return flash_addr
.addr
* c
->bank_width
;
161 * Return the command value or expected status replicated across all devices.
163 static inline uint64_t replicate(const FlashConfig
*c
, uint64_t data
)
165 /* Sanity check our tests. */
166 assert((data
& ~device_mask(c
)) == 0);
170 static inline void flash_cmd(const FlashConfig
*c
, faddr cmd_addr
,
173 flash_write(c
, as_byte_addr(c
, cmd_addr
), replicate(c
, cmd
));
176 static inline uint64_t flash_query(const FlashConfig
*c
, faddr query_addr
)
178 return flash_read(c
, as_byte_addr(c
, query_addr
));
181 static inline uint64_t flash_query_1(const FlashConfig
*c
, faddr query_addr
)
183 return flash_query(c
, query_addr
) & device_mask(c
);
186 static void unlock(const FlashConfig
*c
)
188 flash_cmd(c
, UNLOCK0_ADDR
, UNLOCK0_CMD
);
189 flash_cmd(c
, UNLOCK1_ADDR
, UNLOCK1_CMD
);
192 static void reset(const FlashConfig
*c
)
194 flash_cmd(c
, FLASH_ADDR(0), RESET_CMD
);
197 static void sector_erase(const FlashConfig
*c
, uint64_t byte_addr
)
200 flash_cmd(c
, UNLOCK0_ADDR
, SECOND_UNLOCK_CMD
);
202 flash_write(c
, byte_addr
, replicate(c
, SECTOR_ERASE_CMD
));
205 static void wait_for_completion(const FlashConfig
*c
, uint64_t byte_addr
)
207 /* If DQ6 is toggling, step the clock and ensure the toggle stops. */
208 const uint64_t dq6
= replicate(c
, 0x40);
209 if ((flash_read(c
, byte_addr
) & dq6
) ^ (flash_read(c
, byte_addr
) & dq6
)) {
210 /* Wait for erase or program to finish. */
211 qtest_clock_step_next(c
->qtest
);
212 /* Ensure that DQ6 has stopped toggling. */
213 g_assert_cmphex(flash_read(c
, byte_addr
), ==, flash_read(c
, byte_addr
));
217 static void bypass_program(const FlashConfig
*c
, uint64_t byte_addr
,
220 flash_cmd(c
, UNLOCK0_ADDR
, PROGRAM_CMD
);
221 flash_write(c
, byte_addr
, data
);
223 * Data isn't valid until DQ6 stops toggling. We don't model this as
224 * writes are immediate, but if this changes in the future, we can wait
225 * until the program is complete.
227 wait_for_completion(c
, byte_addr
);
230 static void program(const FlashConfig
*c
, uint64_t byte_addr
, uint16_t data
)
233 bypass_program(c
, byte_addr
, data
);
236 static void chip_erase(const FlashConfig
*c
)
239 flash_cmd(c
, UNLOCK0_ADDR
, SECOND_UNLOCK_CMD
);
241 flash_cmd(c
, UNLOCK0_ADDR
, CHIP_ERASE_CMD
);
245 * Test flash commands with a variety of device geometry.
247 static void test_geometry(const void *opaque
)
249 const FlashConfig
*config
= opaque
;
251 qtest
= qtest_initf("-M musicpal,accel=qtest"
252 " -drive if=pflash,file=%s,format=raw,copy-on-read"
253 /* Device geometry properties. */
254 " -global driver=cfi.pflash02,"
255 "property=num-blocks0,value=%d"
256 " -global driver=cfi.pflash02,"
257 "property=sector-length0,value=%d"
258 " -global driver=cfi.pflash02,"
259 "property=num-blocks1,value=%d"
260 " -global driver=cfi.pflash02,"
261 "property=sector-length1,value=%d"
262 " -global driver=cfi.pflash02,"
263 "property=num-blocks2,value=%d"
264 " -global driver=cfi.pflash02,"
265 "property=sector-length2,value=%d"
266 " -global driver=cfi.pflash02,"
267 "property=num-blocks3,value=%d"
268 " -global driver=cfi.pflash02,"
269 "property=sector-length3,value=%d",
272 config
->sector_len
[0],
274 config
->sector_len
[1],
276 config
->sector_len
[2],
278 config
->sector_len
[3]);
279 FlashConfig explicit_config
= expand_config_defaults(config
);
280 explicit_config
.qtest
= qtest
;
281 const FlashConfig
*c
= &explicit_config
;
285 flash_cmd(c
, UNLOCK0_ADDR
, AUTOSELECT_CMD
);
286 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
287 if (c
->bank_width
>= 2) {
289 * XXX: The ID returned by the musicpal flash chip is 16 bits which
290 * wouldn't happen with an 8-bit device. It would probably be best to
291 * prohibit addresses larger than the device width in pflash_cfi02.c,
292 * but then we couldn't test smaller device widths at all.
294 g_assert_cmphex(flash_query(c
, FLASH_ADDR(1)), ==,
295 replicate(c
, 0x236D));
299 /* Check the erase blocks. */
300 flash_cmd(c
, CFI_ADDR
, CFI_CMD
);
301 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x10)), ==, replicate(c
, 'Q'));
302 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x11)), ==, replicate(c
, 'R'));
303 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x12)), ==, replicate(c
, 'Y'));
305 /* Num erase regions. */
306 int nb_erase_regions
= flash_query_1(c
, FLASH_ADDR(0x2C));
307 g_assert_cmphex(nb_erase_regions
, ==,
308 !!c
->nb_blocs
[0] + !!c
->nb_blocs
[1] + !!c
->nb_blocs
[2] +
311 /* Check device length. */
312 uint32_t device_len
= 1 << flash_query_1(c
, FLASH_ADDR(0x27));
313 g_assert_cmphex(device_len
, ==, UNIFORM_FLASH_SIZE
);
317 const uint64_t dq7
= replicate(c
, 0x80);
318 const uint64_t dq6
= replicate(c
, 0x40);
319 const uint64_t dq3
= replicate(c
, 0x08);
321 uint64_t byte_addr
= 0;
322 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
323 uint64_t base
= 0x2D + 4 * region
;
324 flash_cmd(c
, CFI_ADDR
, CFI_CMD
);
325 uint32_t nb_sectors
= flash_query_1(c
, FLASH_ADDR(base
+ 0)) +
326 (flash_query_1(c
, FLASH_ADDR(base
+ 1)) << 8) + 1;
327 uint32_t sector_len
= (flash_query_1(c
, FLASH_ADDR(base
+ 2)) << 8) +
328 (flash_query_1(c
, FLASH_ADDR(base
+ 3)) << 16);
329 g_assert_cmphex(nb_sectors
, ==, c
->nb_blocs
[region
]);
330 g_assert_cmphex(sector_len
, ==, c
->sector_len
[region
]);
333 /* Erase and program sector. */
334 for (uint32_t i
= 0; i
< nb_sectors
; ++i
) {
335 sector_erase(c
, byte_addr
);
337 /* Check that DQ3 is 0. */
338 g_assert_cmphex(flash_read(c
, byte_addr
) & dq3
, ==, 0);
339 qtest_clock_step_next(c
->qtest
); /* Step over the 50 us timeout. */
341 /* Check that DQ3 is 1. */
342 uint64_t status0
= flash_read(c
, byte_addr
);
343 g_assert_cmphex(status0
& dq3
, ==, dq3
);
345 /* DQ7 is 0 during an erase. */
346 g_assert_cmphex(status0
& dq7
, ==, 0);
347 uint64_t status1
= flash_read(c
, byte_addr
);
349 /* DQ6 toggles during an erase. */
350 g_assert_cmphex(status0
& dq6
, ==, ~status1
& dq6
);
352 /* Wait for erase to complete. */
353 wait_for_completion(c
, byte_addr
);
355 /* Ensure DQ6 has stopped toggling. */
356 g_assert_cmphex(flash_read(c
, byte_addr
), ==,
357 flash_read(c
, byte_addr
));
359 /* Now the data should be valid. */
360 g_assert_cmphex(flash_read(c
, byte_addr
), ==, bank_mask(c
));
362 /* Program a bit pattern. */
363 program(c
, byte_addr
, 0x55);
364 g_assert_cmphex(flash_read(c
, byte_addr
) & 0xFF, ==, 0x55);
365 program(c
, byte_addr
, 0xA5);
366 g_assert_cmphex(flash_read(c
, byte_addr
) & 0xFF, ==, 0x05);
367 byte_addr
+= sector_len
;
371 /* Erase the chip. */
374 uint64_t status0
= flash_read(c
, 0);
375 /* DQ7 is 0 during an erase. */
376 g_assert_cmphex(status0
& dq7
, ==, 0);
377 uint64_t status1
= flash_read(c
, 0);
378 /* DQ6 toggles during an erase. */
379 g_assert_cmphex(status0
& dq6
, ==, ~status1
& dq6
);
380 /* Wait for erase to complete. */
381 qtest_clock_step_next(c
->qtest
);
382 /* Ensure DQ6 has stopped toggling. */
383 g_assert_cmphex(flash_read(c
, 0), ==, flash_read(c
, 0));
384 /* Now the data should be valid. */
386 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
387 for (uint32_t i
= 0; i
< c
->nb_blocs
[region
]; ++i
) {
388 uint64_t byte_addr
= i
* c
->sector_len
[region
];
389 g_assert_cmphex(flash_read(c
, byte_addr
), ==, bank_mask(c
));
395 flash_cmd(c
, UNLOCK0_ADDR
, UNLOCK_BYPASS_CMD
);
396 bypass_program(c
, 0 * c
->bank_width
, 0x01);
397 bypass_program(c
, 1 * c
->bank_width
, 0x23);
398 bypass_program(c
, 2 * c
->bank_width
, 0x45);
400 * Test that bypass programming, unlike normal programming can use any
401 * address for the PROGRAM_CMD.
403 flash_cmd(c
, FLASH_ADDR(3 * c
->bank_width
), PROGRAM_CMD
);
404 flash_write(c
, 3 * c
->bank_width
, 0x67);
405 wait_for_completion(c
, 3 * c
->bank_width
);
406 flash_cmd(c
, FLASH_ADDR(0), UNLOCK_BYPASS_RESET_CMD
);
407 bypass_program(c
, 4 * c
->bank_width
, 0x89); /* Should fail. */
408 g_assert_cmphex(flash_read(c
, 0 * c
->bank_width
), ==, 0x01);
409 g_assert_cmphex(flash_read(c
, 1 * c
->bank_width
), ==, 0x23);
410 g_assert_cmphex(flash_read(c
, 2 * c
->bank_width
), ==, 0x45);
411 g_assert_cmphex(flash_read(c
, 3 * c
->bank_width
), ==, 0x67);
412 g_assert_cmphex(flash_read(c
, 4 * c
->bank_width
), ==, bank_mask(c
));
414 /* Test ignored high order bits of address. */
415 flash_cmd(c
, FLASH_ADDR(0x5555), UNLOCK0_CMD
);
416 flash_cmd(c
, FLASH_ADDR(0x2AAA), UNLOCK1_CMD
);
417 flash_cmd(c
, FLASH_ADDR(0x5555), AUTOSELECT_CMD
);
418 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
422 * Program a word on each sector, erase one or two sectors per region, and
423 * verify that all of those, and only those, are erased.
426 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
427 for (int i
= 0; i
< config
->nb_blocs
[region
]; ++i
) {
428 program(c
, byte_addr
, 0);
429 byte_addr
+= config
->sector_len
[region
];
433 flash_cmd(c
, UNLOCK0_ADDR
, SECOND_UNLOCK_CMD
);
436 const uint64_t erase_cmd
= replicate(c
, SECTOR_ERASE_CMD
);
437 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
438 flash_write(c
, byte_addr
, erase_cmd
);
439 if (c
->nb_blocs
[region
] > 1) {
440 flash_write(c
, byte_addr
+ c
->sector_len
[region
], erase_cmd
);
442 byte_addr
+= c
->sector_len
[region
] * c
->nb_blocs
[region
];
445 qtest_clock_step_next(c
->qtest
); /* Step over the 50 us timeout. */
446 wait_for_completion(c
, 0);
448 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
449 for (int i
= 0; i
< config
->nb_blocs
[region
]; ++i
) {
451 g_assert_cmphex(flash_read(c
, byte_addr
), ==, bank_mask(c
));
453 g_assert_cmphex(flash_read(c
, byte_addr
), ==, 0);
455 byte_addr
+= config
->sector_len
[region
];
464 * 1. enter autoselect mode;
465 * 2. enter CFI mode; and then
467 * leaves the flash device in autoselect mode.
469 static void test_cfi_in_autoselect(const void *opaque
)
471 const FlashConfig
*config
= opaque
;
473 qtest
= qtest_initf("-M musicpal,accel=qtest"
474 " -drive if=pflash,file=%s,format=raw,copy-on-read",
476 FlashConfig explicit_config
= expand_config_defaults(config
);
477 explicit_config
.qtest
= qtest
;
478 const FlashConfig
*c
= &explicit_config
;
480 /* 1. Enter autoselect. */
482 flash_cmd(c
, UNLOCK0_ADDR
, AUTOSELECT_CMD
);
483 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
486 flash_cmd(c
, CFI_ADDR
, CFI_CMD
);
487 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x10)), ==, replicate(c
, 'Q'));
488 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x11)), ==, replicate(c
, 'R'));
489 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x12)), ==, replicate(c
, 'Y'));
493 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
498 static void cleanup(void *opaque
)
504 * XXX: Tests are limited to bank_width = 2 for now because that's what
505 * hw/arm/musicpal.c has.
507 static const FlashConfig configuration
[] = {
508 /* One x16 device. */
512 /* Nonuniform sectors (top boot). */
515 .nb_blocs
= { 127, 1, 2, 1 },
516 .sector_len
= { 0x10000, 0x08000, 0x02000, 0x04000 },
518 /* Nonuniform sectors (bottom boot). */
521 .nb_blocs
= { 1, 2, 1, 127 },
522 .sector_len
= { 0x04000, 0x02000, 0x08000, 0x10000 },
526 int main(int argc
, char **argv
)
528 int fd
= mkstemp(image_path
);
530 g_printerr("Failed to create temporary file %s: %s\n", image_path
,
534 if (ftruncate(fd
, UNIFORM_FLASH_SIZE
) < 0) {
535 int error_code
= errno
;
538 g_printerr("Failed to truncate file %s to %u MB: %s\n", image_path
,
539 UNIFORM_FLASH_SIZE
, strerror(error_code
));
544 qtest_add_abrt_handler(cleanup
, NULL
);
545 g_test_init(&argc
, &argv
, NULL
);
547 size_t nb_configurations
= sizeof configuration
/ sizeof configuration
[0];
548 for (size_t i
= 0; i
< nb_configurations
; ++i
) {
549 const FlashConfig
*config
= &configuration
[i
];
550 char *path
= g_strdup_printf("pflash-cfi02"
551 "/geometry/%dx%x-%dx%x-%dx%x-%dx%x"
554 config
->sector_len
[0],
556 config
->sector_len
[1],
558 config
->sector_len
[2],
560 config
->sector_len
[3],
562 qtest_add_data_func(path
, config
, test_geometry
);
566 qtest_add_data_func("pflash-cfi02/cfi-in-autoselect", &configuration
[0],
567 test_cfi_in_autoselect
);
568 int result
= g_test_run();