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 AUTOSELECT_CMD 0x90
39 #define RESET_CMD 0xF0
40 #define PROGRAM_CMD 0xA0
41 #define SECTOR_ERASE_CMD 0x30
42 #define CHIP_ERASE_CMD 0x10
43 #define UNLOCK_BYPASS_CMD 0x20
44 #define UNLOCK_BYPASS_RESET_CMD 0x00
49 /* Nonuniform block size. */
56 static char image_path
[] = "/tmp/qtest.XXXXXX";
59 * The pflash implementation allows some parameters to be unspecified. We want
60 * to test those configurations but we also need to know the real values in
61 * our testing code. So after we launch qemu, we'll need a new FlashConfig
62 * with the correct values filled in.
64 static FlashConfig
expand_config_defaults(const FlashConfig
*c
)
68 if (ret
.bank_width
== 0) {
71 if (ret
.nb_blocs
[0] == 0 && ret
.sector_len
[0] == 0) {
72 ret
.sector_len
[0] = UNIFORM_FLASH_SECTOR_SIZE
;
73 ret
.nb_blocs
[0] = UNIFORM_FLASH_SIZE
/ UNIFORM_FLASH_SECTOR_SIZE
;
76 /* XXX: Limitations of test harness. */
77 assert(ret
.bank_width
== 2);
82 * Return a bit mask suitable for extracting the least significant
83 * status/query response from an interleaved response.
85 static inline uint64_t device_mask(const FlashConfig
*c
)
91 * Return a bit mask exactly as long as the bank_width.
93 static inline uint64_t bank_mask(const FlashConfig
*c
)
95 if (c
->bank_width
== 8) {
98 return (1ULL << (c
->bank_width
* 8)) - 1ULL;
101 static inline void flash_write(const FlashConfig
*c
, uint64_t byte_addr
,
104 /* Sanity check our tests. */
105 assert((data
& ~bank_mask(c
)) == 0);
106 uint64_t addr
= BASE_ADDR
+ byte_addr
;
107 switch (c
->bank_width
) {
109 qtest_writeb(c
->qtest
, addr
, data
);
112 qtest_writew(c
->qtest
, addr
, data
);
115 qtest_writel(c
->qtest
, addr
, data
);
118 qtest_writeq(c
->qtest
, addr
, data
);
125 static inline uint64_t flash_read(const FlashConfig
*c
, uint64_t byte_addr
)
127 uint64_t addr
= BASE_ADDR
+ byte_addr
;
128 switch (c
->bank_width
) {
130 return qtest_readb(c
->qtest
, addr
);
132 return qtest_readw(c
->qtest
, addr
);
134 return qtest_readl(c
->qtest
, addr
);
136 return qtest_readq(c
->qtest
, addr
);
143 * Convert a flash address expressed in the maximum width of the device as a
146 static inline uint64_t as_byte_addr(const FlashConfig
*c
, faddr flash_addr
)
149 * Command addresses are always given as addresses in the maximum
150 * supported bus size for the flash chip. So an x8/x16 chip in x8 mode
151 * uses addresses 0xAAA and 0x555 to unlock because the least significant
152 * bit is ignored. (0x555 rather than 0x554 is traditional.)
154 * In general we need to multiply by the maximum device width.
156 return flash_addr
.addr
* c
->bank_width
;
160 * Return the command value or expected status replicated across all devices.
162 static inline uint64_t replicate(const FlashConfig
*c
, uint64_t data
)
164 /* Sanity check our tests. */
165 assert((data
& ~device_mask(c
)) == 0);
169 static inline void flash_cmd(const FlashConfig
*c
, faddr cmd_addr
,
172 flash_write(c
, as_byte_addr(c
, cmd_addr
), replicate(c
, cmd
));
175 static inline uint64_t flash_query(const FlashConfig
*c
, faddr query_addr
)
177 return flash_read(c
, as_byte_addr(c
, query_addr
));
180 static inline uint64_t flash_query_1(const FlashConfig
*c
, faddr query_addr
)
182 return flash_query(c
, query_addr
) & device_mask(c
);
185 static void unlock(const FlashConfig
*c
)
187 flash_cmd(c
, UNLOCK0_ADDR
, UNLOCK0_CMD
);
188 flash_cmd(c
, UNLOCK1_ADDR
, UNLOCK1_CMD
);
191 static void reset(const FlashConfig
*c
)
193 flash_cmd(c
, FLASH_ADDR(0), RESET_CMD
);
196 static void sector_erase(const FlashConfig
*c
, uint64_t byte_addr
)
199 flash_cmd(c
, UNLOCK0_ADDR
, 0x80);
201 flash_write(c
, byte_addr
, replicate(c
, SECTOR_ERASE_CMD
));
204 static void wait_for_completion(const FlashConfig
*c
, uint64_t byte_addr
)
206 /* If DQ6 is toggling, step the clock and ensure the toggle stops. */
207 const uint64_t dq6
= replicate(c
, 0x40);
208 if ((flash_read(c
, byte_addr
) & dq6
) ^ (flash_read(c
, byte_addr
) & dq6
)) {
209 /* Wait for erase or program to finish. */
210 qtest_clock_step_next(c
->qtest
);
211 /* Ensure that DQ6 has stopped toggling. */
212 g_assert_cmphex(flash_read(c
, byte_addr
), ==, flash_read(c
, byte_addr
));
216 static void bypass_program(const FlashConfig
*c
, uint64_t byte_addr
,
219 flash_cmd(c
, UNLOCK0_ADDR
, PROGRAM_CMD
);
220 flash_write(c
, byte_addr
, data
);
222 * Data isn't valid until DQ6 stops toggling. We don't model this as
223 * writes are immediate, but if this changes in the future, we can wait
224 * until the program is complete.
226 wait_for_completion(c
, byte_addr
);
229 static void program(const FlashConfig
*c
, uint64_t byte_addr
, uint16_t data
)
232 bypass_program(c
, byte_addr
, data
);
235 static void chip_erase(const FlashConfig
*c
)
238 flash_cmd(c
, UNLOCK0_ADDR
, 0x80);
240 flash_cmd(c
, UNLOCK0_ADDR
, CHIP_ERASE_CMD
);
244 * Test flash commands with a variety of device geometry.
246 static void test_geometry(const void *opaque
)
248 const FlashConfig
*config
= opaque
;
250 qtest
= qtest_initf("-M musicpal,accel=qtest"
251 " -drive if=pflash,file=%s,format=raw,copy-on-read"
252 /* Device geometry properties. */
253 " -global driver=cfi.pflash02,"
254 "property=num-blocks0,value=%d"
255 " -global driver=cfi.pflash02,"
256 "property=sector-length0,value=%d"
257 " -global driver=cfi.pflash02,"
258 "property=num-blocks1,value=%d"
259 " -global driver=cfi.pflash02,"
260 "property=sector-length1,value=%d"
261 " -global driver=cfi.pflash02,"
262 "property=num-blocks2,value=%d"
263 " -global driver=cfi.pflash02,"
264 "property=sector-length2,value=%d"
265 " -global driver=cfi.pflash02,"
266 "property=num-blocks3,value=%d"
267 " -global driver=cfi.pflash02,"
268 "property=sector-length3,value=%d",
271 config
->sector_len
[0],
273 config
->sector_len
[1],
275 config
->sector_len
[2],
277 config
->sector_len
[3]);
278 FlashConfig explicit_config
= expand_config_defaults(config
);
279 explicit_config
.qtest
= qtest
;
280 const FlashConfig
*c
= &explicit_config
;
284 flash_cmd(c
, UNLOCK0_ADDR
, AUTOSELECT_CMD
);
285 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
286 if (c
->bank_width
>= 2) {
288 * XXX: The ID returned by the musicpal flash chip is 16 bits which
289 * wouldn't happen with an 8-bit device. It would probably be best to
290 * prohibit addresses larger than the device width in pflash_cfi02.c,
291 * but then we couldn't test smaller device widths at all.
293 g_assert_cmphex(flash_query(c
, FLASH_ADDR(1)), ==,
294 replicate(c
, 0x236D));
298 /* Check the erase blocks. */
299 flash_cmd(c
, CFI_ADDR
, CFI_CMD
);
300 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x10)), ==, replicate(c
, 'Q'));
301 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x11)), ==, replicate(c
, 'R'));
302 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0x12)), ==, replicate(c
, 'Y'));
304 /* Num erase regions. */
305 int nb_erase_regions
= flash_query_1(c
, FLASH_ADDR(0x2C));
306 g_assert_cmphex(nb_erase_regions
, ==,
307 !!c
->nb_blocs
[0] + !!c
->nb_blocs
[1] + !!c
->nb_blocs
[2] +
310 /* Check device length. */
311 uint32_t device_len
= 1 << flash_query_1(c
, FLASH_ADDR(0x27));
312 g_assert_cmphex(device_len
, ==, UNIFORM_FLASH_SIZE
);
316 const uint64_t dq7
= replicate(c
, 0x80);
317 const uint64_t dq6
= replicate(c
, 0x40);
318 uint64_t byte_addr
= 0;
319 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
320 uint64_t base
= 0x2D + 4 * region
;
321 flash_cmd(c
, CFI_ADDR
, CFI_CMD
);
322 uint32_t nb_sectors
= flash_query_1(c
, FLASH_ADDR(base
+ 0)) +
323 (flash_query_1(c
, FLASH_ADDR(base
+ 1)) << 8) + 1;
324 uint32_t sector_len
= (flash_query_1(c
, FLASH_ADDR(base
+ 2)) << 8) +
325 (flash_query_1(c
, FLASH_ADDR(base
+ 3)) << 16);
326 g_assert_cmphex(nb_sectors
, ==, c
->nb_blocs
[region
]);
327 g_assert_cmphex(sector_len
, ==, c
->sector_len
[region
]);
330 /* Erase and program sector. */
331 for (uint32_t i
= 0; i
< nb_sectors
; ++i
) {
332 sector_erase(c
, byte_addr
);
334 uint64_t status0
= flash_read(c
, byte_addr
);
335 /* DQ7 is 0 during an erase. */
336 g_assert_cmphex(status0
& dq7
, ==, 0);
337 uint64_t status1
= flash_read(c
, byte_addr
);
338 /* DQ6 toggles during an erase. */
339 g_assert_cmphex(status0
& dq6
, ==, ~status1
& dq6
);
340 /* Wait for erase to complete. */
341 qtest_clock_step_next(c
->qtest
);
342 /* Ensure DQ6 has stopped toggling. */
343 g_assert_cmphex(flash_read(c
, byte_addr
), ==,
344 flash_read(c
, byte_addr
));
345 /* Now the data should be valid. */
346 g_assert_cmphex(flash_read(c
, byte_addr
), ==, bank_mask(c
));
348 /* Program a bit pattern. */
349 program(c
, byte_addr
, 0x55);
350 g_assert_cmphex(flash_read(c
, byte_addr
) & 0xFF, ==, 0x55);
351 program(c
, byte_addr
, 0xA5);
352 g_assert_cmphex(flash_read(c
, byte_addr
) & 0xFF, ==, 0x05);
353 byte_addr
+= sector_len
;
357 /* Erase the chip. */
360 uint64_t status0
= flash_read(c
, 0);
361 /* DQ7 is 0 during an erase. */
362 g_assert_cmphex(status0
& dq7
, ==, 0);
363 uint64_t status1
= flash_read(c
, 0);
364 /* DQ6 toggles during an erase. */
365 g_assert_cmphex(status0
& dq6
, ==, ~status1
& dq6
);
366 /* Wait for erase to complete. */
367 qtest_clock_step_next(c
->qtest
);
368 /* Ensure DQ6 has stopped toggling. */
369 g_assert_cmphex(flash_read(c
, 0), ==, flash_read(c
, 0));
370 /* Now the data should be valid. */
372 for (int region
= 0; region
< nb_erase_regions
; ++region
) {
373 for (uint32_t i
= 0; i
< c
->nb_blocs
[region
]; ++i
) {
374 uint64_t byte_addr
= i
* c
->sector_len
[region
];
375 g_assert_cmphex(flash_read(c
, byte_addr
), ==, bank_mask(c
));
381 flash_cmd(c
, UNLOCK0_ADDR
, UNLOCK_BYPASS_CMD
);
382 bypass_program(c
, 0 * c
->bank_width
, 0x01);
383 bypass_program(c
, 1 * c
->bank_width
, 0x23);
384 bypass_program(c
, 2 * c
->bank_width
, 0x45);
386 * Test that bypass programming, unlike normal programming can use any
387 * address for the PROGRAM_CMD.
389 flash_cmd(c
, FLASH_ADDR(3 * c
->bank_width
), PROGRAM_CMD
);
390 flash_write(c
, 3 * c
->bank_width
, 0x67);
391 wait_for_completion(c
, 3 * c
->bank_width
);
392 flash_cmd(c
, FLASH_ADDR(0), UNLOCK_BYPASS_RESET_CMD
);
393 bypass_program(c
, 4 * c
->bank_width
, 0x89); /* Should fail. */
394 g_assert_cmphex(flash_read(c
, 0 * c
->bank_width
), ==, 0x01);
395 g_assert_cmphex(flash_read(c
, 1 * c
->bank_width
), ==, 0x23);
396 g_assert_cmphex(flash_read(c
, 2 * c
->bank_width
), ==, 0x45);
397 g_assert_cmphex(flash_read(c
, 3 * c
->bank_width
), ==, 0x67);
398 g_assert_cmphex(flash_read(c
, 4 * c
->bank_width
), ==, bank_mask(c
));
400 /* Test ignored high order bits of address. */
401 flash_cmd(c
, FLASH_ADDR(0x5555), UNLOCK0_CMD
);
402 flash_cmd(c
, FLASH_ADDR(0x2AAA), UNLOCK1_CMD
);
403 flash_cmd(c
, FLASH_ADDR(0x5555), AUTOSELECT_CMD
);
404 g_assert_cmphex(flash_query(c
, FLASH_ADDR(0)), ==, replicate(c
, 0xBF));
410 static void cleanup(void *opaque
)
416 * XXX: Tests are limited to bank_width = 2 for now because that's what
417 * hw/arm/musicpal.c has.
419 static const FlashConfig configuration
[] = {
420 /* One x16 device. */
424 /* Nonuniform sectors (top boot). */
427 .nb_blocs
= { 127, 1, 2, 1 },
428 .sector_len
= { 0x10000, 0x08000, 0x02000, 0x04000 },
430 /* Nonuniform sectors (bottom boot). */
433 .nb_blocs
= { 1, 2, 1, 127 },
434 .sector_len
= { 0x04000, 0x02000, 0x08000, 0x10000 },
438 int main(int argc
, char **argv
)
440 int fd
= mkstemp(image_path
);
442 g_printerr("Failed to create temporary file %s: %s\n", image_path
,
446 if (ftruncate(fd
, UNIFORM_FLASH_SIZE
) < 0) {
447 int error_code
= errno
;
450 g_printerr("Failed to truncate file %s to %u MB: %s\n", image_path
,
451 UNIFORM_FLASH_SIZE
, strerror(error_code
));
456 qtest_add_abrt_handler(cleanup
, NULL
);
457 g_test_init(&argc
, &argv
, NULL
);
459 size_t nb_configurations
= sizeof configuration
/ sizeof configuration
[0];
460 for (size_t i
= 0; i
< nb_configurations
; ++i
) {
461 const FlashConfig
*config
= &configuration
[i
];
462 char *path
= g_strdup_printf("pflash-cfi02"
463 "/geometry/%dx%x-%dx%x-%dx%x-%dx%x"
466 config
->sector_len
[0],
468 config
->sector_len
[1],
470 config
->sector_len
[2],
472 config
->sector_len
[3],
474 qtest_add_data_func(path
, config
, test_geometry
);
477 int result
= g_test_run();