aboutsummaryrefslogtreecommitdiffstats
path: root/hw/cfi_flash.c
blob: 3c76c04aa13ac96d18c2f2e966fdf2aff9f2c00f (plain)
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
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/sizes.h>
#include <linux/types.h>

#include "kvm/kvm.h"
#include "kvm/kvm-arch.h"
#include "kvm/devices.h"
#include "kvm/fdt.h"
#include "kvm/mutex.h"
#include "kvm/util.h"

/*
 * The EDK2 driver hardcodes two 16-bit chips on a 32-bit bus.
 * This code supports one or two chips (enforced below).
 */
#define CFI_NR_FLASH_CHIPS			2

/* We always emulate a 32 bit bus width. */
#define CFI_BUS_WIDTH				4

/* The *effective* size of an erase block (over all chips) */
#define FLASH_BLOCK_SIZE			SZ_64K
#define FLASH_BLOCK_SIZE_PER_CHIP					\
	(FLASH_BLOCK_SIZE / CFI_NR_FLASH_CHIPS)

#define PROGRAM_BUFF_SIZE_BITS			7
#define PROGRAM_BUFF_SIZE			(1U << PROGRAM_BUFF_SIZE_BITS)
#define PROGRAM_BUFF_SIZE_BITS_PER_CHIP					\
	(PROGRAM_BUFF_SIZE_BITS + 1 - CFI_NR_FLASH_CHIPS)

/* CFI commands */
#define CFI_CMD_LOCK_BLOCK			0x01
#define CFI_CMD_ALTERNATE_WORD_PROGRAM		0x10
#define CFI_CMD_ERASE_BLOCK_SETUP		0x20
#define CFI_CMD_WORD_PROGRAM			0x40
#define CFI_CMD_CLEAR_STATUS_REG		0x50
#define CFI_CMD_LOCK_BLOCK_SETUP		0x60
#define CFI_CMD_READ_STATUS_REG			0x70
#define CFI_CMD_READ_JEDEC_DEVID		0x90
#define CFI_CMD_READ_CFI_QUERY			0x98
#define CFI_CMD_CONFIRM				0xd0
#define CFI_CMD_BUFFERED_PROGRAM_SETUP		0xe8
#define CFI_CMD_READ_ARRAY			0xff

#define CFI_STATUS_PROTECT_BIT		0x02
#define CFI_STATUS_PROGRAM_LOCK_BIT	0x10
#define CFI_STATUS_ERASE_CLEAR_LOCK_BIT	0x20
#define CFI_STATUS_LOCK_ERROR		CFI_STATUS_PROGRAM_LOCK_BIT |	\
					CFI_STATUS_PROTECT_BIT
#define CFI_STATUS_ERASE_ERROR		CFI_STATUS_ERASE_CLEAR_LOCK_BIT | \
					CFI_STATUS_PROGRAM_LOCK_BIT
#define CFI_STATUS_READY		0x80

/*
 * CFI query table contents, as far as it is constant.
 * The dynamic information (size, etc.) will be generated on the fly.
 */
#define CFI_GEOM_OFFSET				0x27
static const u8 cfi_query_table[] = {
		/* CFI query identification string */
	[0x10] = 'Q', 'R', 'Y',		/* ID string */
	0x01, 0x00,		/* primary command set: Intel/Sharp extended */
	0x31, 0x00,		/* address of primary extended query table */
	0x00, 0x00,		/* alternative command set: unused */
	0x00, 0x00,		/* address of alternative extended query table*/
		/* system interface information */
	[0x1b] = 0x45,			/* minimum Vcc voltage: 4.5V */
	0x55,			/* maximum Vcc voltage: 5.5V */
	0x00,			/* minimum Vpp voltage: 0.0V (unused) */
	0x00,			/* maximum Vpp voltage: 0.0V *(unused) */
	0x01,			/* timeout for single word program: 2 us */
	0x01,			/* timeout for multi-byte program: 2 us */
	0x01,			/* timeout for block erase: 2 ms */
	0x00,			/* timeout for full chip erase: not supported */
	0x00,			/* max timeout for single word program: 1x */
	0x00,			/* max timeout for mulit-byte program: 1x */
	0x00,			/* max timeout for block erase: 1x */
	0x00,			/* max timeout for chip erase: not supported */
		/* flash geometry information */
	[0x27] = 0x00,		/* size in power-of-2 bytes, filled later */
	0x05, 0x00,		/* interface description: 32 and 16 bits */
	PROGRAM_BUFF_SIZE_BITS_PER_CHIP, 0x00,
				/* number of bytes in write buffer */
	0x01,			/* one erase block region */
	0x00, 0x00, 0x00, 0x00, /* number and size of erase blocks, generated */
		/* Intel primary algorithm extended query table */
	[0x31] = 'P', 'R', 'I',
	'1', '0',		/* version 1.0 */
	0xa0, 0x00, 0x00, 0x00, /* optional features: instant lock & pm-read */
	0x00,			/* no functions after suspend */
	0x01, 0x00,		/* only lock bit supported */
	0x50,			/* best Vcc value: 5.0V */
	0x00,			/* best Vpp value: 0.0V (unused) */
	0x01,			/* number of protection register fields */
	0x00, 0x00, 0x00, 0x00,	/* protection field 1 description */
};

/*
 * Those states represent a subset of the CFI flash state machine.
 */
enum cfi_flash_state {
	READY,
	LOCK_BLOCK_SETUP,
	WORD_PROGRAM,
	BUFFERED_PROGRAM_SETUP,
	BUFFER_WRITE,
	ERASE_BLOCK_SETUP,
};

/*
 * The device can be in several **Read** modes.
 * We don't implement the asynchronous burst mode.
 */
enum cfi_read_mode {
	READ_ARRAY,
	READ_STATUS_REG,
	READ_JEDEC_DEVID,
	READ_CFI_QUERY,
};

struct cfi_flash_device {
	struct device_header	dev_hdr;
	/* Protects the CFI state machine variables in this data structure. */
	struct mutex		mutex;
	u64			base_addr;
	u32			size;

	void			*flash_memory;
	u8			program_buffer[PROGRAM_BUFF_SIZE];
	unsigned long		*lock_bm;
	u64			block_address;
	unsigned int		buff_written;
	unsigned int		buffer_length;

	enum cfi_flash_state	state;
	enum cfi_read_mode	read_mode;
	u8			sr;
};

static int nr_erase_blocks(struct cfi_flash_device *sfdev)
{
	return sfdev->size / FLASH_BLOCK_SIZE;
}

/*
 * CFI queries always deal with one byte of information, possibly mirrored
 * to other bytes on the bus. This is dealt with in the callers.
 * The address provided is the one for 8-bit addressing, and would need to
 * be adjusted for wider accesses.
 */
static u8 read_cfi(struct cfi_flash_device *sfdev, u64 faddr)
{
	if (faddr > sizeof(cfi_query_table)) {
		pr_debug("CFI query read access beyond the end of table");
		return 0;
	}

	/* Fixup dynamic information in the geometry part of the table. */
	switch (faddr) {
	case 0x27:		/* device size in bytes, power of two */
		return pow2_size(sfdev->size / CFI_NR_FLASH_CHIPS);
	case 0x2d + 0:	/* number of erase blocks, minus one */
		return (nr_erase_blocks(sfdev) - 1) & 0xff;
	case 0x2d + 1:
		return ((nr_erase_blocks(sfdev) - 1) >> 8) & 0xff;
	case 0x2d + 2:	/* erase block size, in units of 256 */
		return (FLASH_BLOCK_SIZE_PER_CHIP / 256) & 0xff;
	case 0x2d + 3:
		return ((FLASH_BLOCK_SIZE_PER_CHIP / 256) >> 8) & 0xff;
	}

	return cfi_query_table[faddr];
}

static bool block_is_locked(struct cfi_flash_device *sfdev, u64 faddr)
{
	int block_nr = faddr / FLASH_BLOCK_SIZE;

	return test_bit(block_nr, sfdev->lock_bm);
}

#define DEV_ID_MASK 0x7ff
static u16 read_dev_id(struct cfi_flash_device *sfdev, u64 faddr)
{
	switch ((faddr & DEV_ID_MASK) / CFI_BUS_WIDTH) {
	case 0x0:				/* vendor ID */
		return 0x0000;
	case 0x1:				/* device ID */
		return 0xffff;
	case 0x2:
		return block_is_locked(sfdev, faddr & ~DEV_ID_MASK);
	default:			/* Ignore the other entries. */
		return 0;
	}
}

static void lock_block(struct cfi_flash_device *sfdev, u64 faddr, bool lock)
{
	int block_nr = faddr / FLASH_BLOCK_SIZE;

	if (lock)
		set_bit(block_nr, sfdev->lock_bm);
	else
		clear_bit(block_nr, sfdev->lock_bm);
}

static void word_program(struct cfi_flash_device *sfdev,
			 u64 faddr, void *data, int len)
{
	if (block_is_locked(sfdev, faddr)) {
		sfdev->sr |= CFI_STATUS_LOCK_ERROR;
		return;
	}

	memcpy(sfdev->flash_memory + faddr, data, len);
}

/* Reset the program buffer state to prepare for follow-up writes. */
static void buffer_setup(struct cfi_flash_device *sfdev)
{
	memset(sfdev->program_buffer, 0, sizeof(sfdev->program_buffer));
	sfdev->block_address = ~0ULL;
	sfdev->buff_written = 0;
}

static bool buffer_write(struct cfi_flash_device *sfdev,
			 u64 faddr, void *buffer, int len)
{
	unsigned int buff_addr;

	if (sfdev->buff_written >= sfdev->buffer_length)
		return false;

	/*
	 * The first word written into the buffer after the setup command
	 * happens to be the base address for the buffer.
	 * All subsequent writes need to be within this address and this
	 * address plus the buffer size, so keep this value around.
	 */
	if (sfdev->block_address == ~0ULL)
		sfdev->block_address = faddr;

	if (faddr < sfdev->block_address)
		return false;
	buff_addr = faddr - sfdev->block_address;
	if (buff_addr >= PROGRAM_BUFF_SIZE)
		return false;

	memcpy(sfdev->program_buffer + buff_addr, buffer, len);
	sfdev->buff_written += len;

	return true;
}

static void buffer_confirm(struct cfi_flash_device *sfdev)
{
	if (block_is_locked(sfdev, sfdev->block_address)) {
		sfdev->sr |= CFI_STATUS_LOCK_ERROR;
		return;
	}
	memcpy(sfdev->flash_memory + sfdev->block_address,
	       sfdev->program_buffer, sfdev->buff_written);
}

static void block_erase_confirm(struct cfi_flash_device *sfdev, u64 faddr)
{
	if (block_is_locked(sfdev, faddr)) {
		sfdev->sr |= CFI_STATUS_LOCK_ERROR;
		return;
	}

	memset(sfdev->flash_memory + faddr, 0xff, FLASH_BLOCK_SIZE);
}

static void cfi_flash_read(struct cfi_flash_device *sfdev,
			   u64 faddr, u8 *data, u32 len)
{
	u16 cfi_value = 0;

	switch (sfdev->read_mode) {
	case READ_ARRAY:
		/* just copy the requested bytes from the array */
		memcpy(data, sfdev->flash_memory + faddr, len);
		return;
	case READ_STATUS_REG:
		cfi_value = sfdev->sr;
		break;
	case READ_JEDEC_DEVID:
		cfi_value = read_dev_id(sfdev, faddr);
		break;
	case READ_CFI_QUERY:
		cfi_value = read_cfi(sfdev, faddr / CFI_BUS_WIDTH);
		break;
	}
	switch (len) {
	case 1:
		*data = cfi_value;
		break;
	case 8: memset(data + 4, 0, 4);
		/* fall-through */
	case 4:
		if (CFI_NR_FLASH_CHIPS == 2)
			memcpy(data + 2, &cfi_value, 2);
		else
			memset(data + 2, 0, 2);
		/* fall-through */
	case 2:
		memcpy(data, &cfi_value, 2);
		break;
	default:
		pr_debug("CFI flash: illegal access length %d for read mode %d",
			 len, sfdev->read_mode);
		break;
	}
}

/*
 * Any writes happening in "READY" state don't actually write to the memory,
 * but are really treated as commands to advance the state machine and select
 * the next action.
 * Change the state and modes according to the value written. The address
 * that value is written to does not matter and is ignored.
 */
static void cfi_flash_write_ready(struct cfi_flash_device *sfdev, u8 command)
{
	switch (command) {
	case CFI_CMD_READ_JEDEC_DEVID:
		sfdev->read_mode = READ_JEDEC_DEVID;
		break;
	case CFI_CMD_READ_STATUS_REG:
		sfdev->read_mode = READ_STATUS_REG;
		break;
	case CFI_CMD_READ_CFI_QUERY:
		sfdev->read_mode = READ_CFI_QUERY;
		break;
	case CFI_CMD_CLEAR_STATUS_REG:
		sfdev->sr = CFI_STATUS_READY;
		break;
	case CFI_CMD_WORD_PROGRAM:
	case CFI_CMD_ALTERNATE_WORD_PROGRAM:
		sfdev->state = WORD_PROGRAM;
		sfdev->read_mode = READ_STATUS_REG;
		break;
	case CFI_CMD_LOCK_BLOCK_SETUP:
		sfdev->state = LOCK_BLOCK_SETUP;
		break;
	case CFI_CMD_ERASE_BLOCK_SETUP:
		sfdev->state = ERASE_BLOCK_SETUP;
		sfdev->read_mode = READ_STATUS_REG;
		break;
	case CFI_CMD_BUFFERED_PROGRAM_SETUP:
		buffer_setup(sfdev);
		sfdev->state = BUFFERED_PROGRAM_SETUP;
		sfdev->read_mode = READ_STATUS_REG;
		break;
	case CFI_CMD_CONFIRM:
		pr_debug("CFI flash: unexpected confirm command 0xd0");
		break;
	default:
		pr_debug("CFI flash: unknown command 0x%x", command);
		/* fall-through */
	case CFI_CMD_READ_ARRAY:
		sfdev->read_mode = READ_ARRAY;
		break;
	}
}

static void cfi_flash_write(struct cfi_flash_device *sfdev, u16 command,
			    u64 faddr, u8 *data, u32 len)
{
	switch (sfdev->state) {
	case READY:
		cfi_flash_write_ready(sfdev, command & 0xff);
		return;
	case LOCK_BLOCK_SETUP:
		switch (command & 0xff) {
		case CFI_CMD_LOCK_BLOCK:
			lock_block(sfdev, faddr, true);
			sfdev->read_mode = READ_STATUS_REG;
			break;
		case CFI_CMD_CONFIRM:
			lock_block(sfdev, faddr, false);
			sfdev->read_mode = READ_STATUS_REG;
			break;
		default:
			sfdev->sr |= CFI_STATUS_ERASE_ERROR;
			break;
		}
		sfdev->state = READY;
		break;

	case WORD_PROGRAM:
		word_program(sfdev, faddr, data, len);
		sfdev->read_mode = READ_STATUS_REG;
		sfdev->state = READY;
		break;

	case BUFFER_WRITE:
		if (buffer_write(sfdev, faddr, data, len))
			break;

		if ((command & 0xff) == CFI_CMD_CONFIRM) {
			buffer_confirm(sfdev);
			sfdev->read_mode = READ_STATUS_REG;
		} else {
			pr_debug("CFI flash: BUFFER_WRITE: expected CONFIRM(0xd0), got 0x%x @ 0x%llx",
				 command, faddr);
			sfdev->sr |= CFI_STATUS_PROGRAM_LOCK_BIT;
		}
		sfdev->state = READY;
		break;

	case BUFFERED_PROGRAM_SETUP:
		sfdev->buffer_length = (command + 1) * CFI_BUS_WIDTH;
		if (sfdev->buffer_length > PROGRAM_BUFF_SIZE)
			sfdev->buffer_length = PROGRAM_BUFF_SIZE;
		sfdev->state = BUFFER_WRITE;
		sfdev->read_mode = READ_STATUS_REG;
		break;

	case ERASE_BLOCK_SETUP:
		if ((command & 0xff) == CFI_CMD_CONFIRM)
			block_erase_confirm(sfdev, faddr);
		else
			sfdev->sr |= CFI_STATUS_ERASE_ERROR;

		sfdev->state = READY;
		sfdev->read_mode = READ_STATUS_REG;
		break;
	default:
		pr_debug("CFI flash: unexpected/unknown command 0x%x", command);
		break;
	}
}

static void cfi_flash_mmio(struct kvm_cpu *vcpu,
			   u64 addr, u8 *data, u32 len, u8 is_write,
			   void *context)
{
	struct cfi_flash_device *sfdev = context;
	u64 faddr = addr - sfdev->base_addr;
	u32 value;

	if (!is_write) {
		mutex_lock(&sfdev->mutex);

		cfi_flash_read(sfdev, faddr, data, len);

		mutex_unlock(&sfdev->mutex);

		return;
	}

	if (len > 4) {
		pr_info("CFI flash: MMIO %d-bit write access not supported",
			 len * 8);
		return;
	}

	memcpy(&value, data, len);

	mutex_lock(&sfdev->mutex);

	cfi_flash_write(sfdev, value & 0xffff, faddr, data, len);

	mutex_unlock(&sfdev->mutex);
}

#ifdef CONFIG_HAS_LIBFDT
static void generate_cfi_flash_fdt_node(void *fdt,
					struct device_header *dev_hdr,
					void (*generate_irq_prop)(void *fdt,
								  u8 irq,
								enum irq_type))
{
	struct cfi_flash_device *sfdev;
	u64 reg_prop[2];

	sfdev = container_of(dev_hdr, struct cfi_flash_device, dev_hdr);
	reg_prop[0] = cpu_to_fdt64(sfdev->base_addr);
	reg_prop[1] = cpu_to_fdt64(sfdev->size);

	_FDT(fdt_begin_node(fdt, "flash"));
	_FDT(fdt_property_cell(fdt, "bank-width", CFI_BUS_WIDTH));
	_FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
	_FDT(fdt_property_cell(fdt, "#size-cells", 0x1));
	_FDT(fdt_property_string(fdt, "compatible", "cfi-flash"));
	_FDT(fdt_property_string(fdt, "label", "System-firmware"));
	_FDT(fdt_property(fdt, "reg", &reg_prop, sizeof(reg_prop)));
	_FDT(fdt_end_node(fdt));
}
#else
#define generate_cfi_flash_fdt_node NULL
#endif

static struct cfi_flash_device *create_flash_device_file(struct kvm *kvm,
							 const char *filename)
{
	struct cfi_flash_device *sfdev;
	struct stat statbuf;
	unsigned int value;
	int ret;
	int fd;

	fd = open(filename, O_RDWR);
	if (fd < 0)
		return ERR_PTR(-errno);

	if (fstat(fd, &statbuf) < 0) {
		ret = -errno;
		goto out_close;
	}

	sfdev = malloc(sizeof(struct cfi_flash_device));
	if (!sfdev) {
		ret = -ENOMEM;
		goto out_close;
	}

	sfdev->size = statbuf.st_size;
	/* Round down to nearest power-of-2 size value. */
	sfdev->size = 1U << (pow2_size(sfdev->size + 1) - 1);
	if (sfdev->size > KVM_FLASH_MAX_SIZE)
		sfdev->size = KVM_FLASH_MAX_SIZE;
	if (sfdev->size < statbuf.st_size) {
		pr_info("flash file size (%llu bytes) is not a power of two",
			(unsigned long long)statbuf.st_size);
		pr_info("only using first %u bytes", sfdev->size);
	}
	sfdev->flash_memory = mmap(NULL, sfdev->size,
				   PROT_READ | PROT_WRITE, MAP_SHARED,
				   fd, 0);
	if (sfdev->flash_memory == MAP_FAILED) {
		ret = -errno;
		goto out_free;
	}
	sfdev->base_addr = KVM_FLASH_MMIO_BASE;
	sfdev->state = READY;
	sfdev->read_mode = READ_ARRAY;
	sfdev->sr = CFI_STATUS_READY;

	value = roundup(nr_erase_blocks(sfdev), BITS_PER_LONG) / 8;
	sfdev->lock_bm = malloc(value);
	memset(sfdev->lock_bm, 0, value);

	sfdev->dev_hdr.bus_type = DEVICE_BUS_MMIO;
	sfdev->dev_hdr.data = generate_cfi_flash_fdt_node;
	mutex_init(&sfdev->mutex);
	ret = device__register(&sfdev->dev_hdr);
	if (ret)
		goto out_unmap;

	ret = kvm__register_mmio(kvm,
				 sfdev->base_addr, sfdev->size,
				 false, cfi_flash_mmio, sfdev);
	if (ret) {
		device__unregister(&sfdev->dev_hdr);
		goto out_unmap;
	}

	return sfdev;

out_unmap:
	munmap(sfdev->flash_memory, sfdev->size);
out_free:
	free(sfdev);
out_close:
	close(fd);

	return ERR_PTR(ret);
}

static int cfi_flash__init(struct kvm *kvm)
{
	struct cfi_flash_device *sfdev;

	BUILD_BUG_ON(CFI_NR_FLASH_CHIPS != 1 && CFI_NR_FLASH_CHIPS != 2);

	if (!kvm->cfg.flash_filename)
		return 0;

	sfdev = create_flash_device_file(kvm, kvm->cfg.flash_filename);
	if (IS_ERR(sfdev))
		return PTR_ERR(sfdev);

	return 0;
}
dev_init(cfi_flash__init);