1/*-
2 * Copyright (C) 2009-2012 Semihalf
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/* Simulated NAND controller driver */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/proc.h>
35#include <sys/bus.h>
36#include <sys/conf.h>
37#include <sys/kernel.h>
38#include <sys/module.h>
39#include <sys/malloc.h>
40
41#include <dev/nand/nand.h>
42#include <dev/nand/nandsim.h>
43#include <dev/nand/nandsim_chip.h>
44#include <dev/nand/nandsim_log.h>
45#include <dev/nand/nandsim_swap.h>
46
47struct sim_param sim;
48struct sim_ctrl_conf ctrls[MAX_SIM_DEV];
49
50static struct cdev *nandsim_dev;
51static d_ioctl_t nandsim_ioctl;
52
53static void nandsim_init_sim_param(struct sim_param *);
54static int nandsim_create_ctrl(struct sim_ctrl *);
55static int nandsim_destroy_ctrl(int);
56static int nandsim_ctrl_status(struct sim_ctrl *);
57static int nandsim_create_chip(struct sim_chip *);
58static int nandsim_destroy_chip(struct sim_ctrl_chip *);
59static int nandsim_chip_status(struct sim_chip *);
60static int nandsim_start_ctrl(int);
61static int nandsim_stop_ctrl(int);
62static int nandsim_inject_error(struct sim_error *);
63static int nandsim_get_block_state(struct sim_block_state *);
64static int nandsim_set_block_state(struct sim_block_state *);
65static int nandsim_modify(struct sim_mod *);
66static int nandsim_dump(struct sim_dump *);
67static int nandsim_restore(struct sim_dump *);
68static int nandsim_freeze(struct sim_ctrl_chip *);
69static void nandsim_print_log(struct sim_log *);
70static struct nandsim_chip *get_nandsim_chip(uint8_t, uint8_t);
71
72static struct cdevsw nandsim_cdevsw = {
73	.d_version =    D_VERSION,
74	.d_ioctl =      nandsim_ioctl,
75	.d_name =       "nandsim",
76};
77
78int
79nandsim_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
80    int flags, struct thread *td)
81{
82	int ret = 0;
83
84	switch (cmd) {
85	case NANDSIM_SIM_PARAM:
86		nandsim_init_sim_param((struct sim_param *)data);
87		break;
88	case NANDSIM_CREATE_CTRL:
89		ret = nandsim_create_ctrl((struct sim_ctrl *)data);
90		break;
91	case NANDSIM_DESTROY_CTRL:
92		ret = nandsim_destroy_ctrl(*(int *)data);
93		break;
94	case NANDSIM_STATUS_CTRL:
95		ret = nandsim_ctrl_status((struct sim_ctrl *)data);
96		break;
97	case NANDSIM_CREATE_CHIP:
98		ret = nandsim_create_chip((struct sim_chip *)data);
99		break;
100	case NANDSIM_DESTROY_CHIP:
101		ret = nandsim_destroy_chip((struct sim_ctrl_chip *)data);
102		break;
103	case NANDSIM_STATUS_CHIP:
104		ret = nandsim_chip_status((struct sim_chip *)data);
105		break;
106	case NANDSIM_MODIFY:
107		ret = nandsim_modify((struct sim_mod *)data);
108		break;
109	case NANDSIM_START_CTRL:
110		ret = nandsim_start_ctrl(*(int *)data);
111		break;
112	case NANDSIM_STOP_CTRL:
113		ret = nandsim_stop_ctrl(*(int *)data);
114		break;
115	case NANDSIM_INJECT_ERROR:
116		ret = nandsim_inject_error((struct sim_error *)data);
117		break;
118	case NANDSIM_SET_BLOCK_STATE:
119		ret = nandsim_set_block_state((struct sim_block_state *)data);
120		break;
121	case NANDSIM_GET_BLOCK_STATE:
122		ret = nandsim_get_block_state((struct sim_block_state *)data);
123		break;
124	case NANDSIM_PRINT_LOG:
125		nandsim_print_log((struct sim_log *)data);
126		break;
127	case NANDSIM_DUMP:
128		ret = nandsim_dump((struct sim_dump *)data);
129		break;
130	case NANDSIM_RESTORE:
131		ret = nandsim_restore((struct sim_dump *)data);
132		break;
133	case NANDSIM_FREEZE:
134		ret = nandsim_freeze((struct sim_ctrl_chip *)data);
135		break;
136	default:
137		ret = EINVAL;
138		break;
139	}
140
141	return (ret);
142}
143
144static void
145nandsim_init_sim_param(struct sim_param *param)
146{
147
148	if (!param)
149		return;
150
151	nand_debug(NDBG_SIM,"log level:%d output %d", param->log_level,
152	    param->log_output);
153	nandsim_log_level = param->log_level;
154	nandsim_log_output = param->log_output;
155}
156
157static int
158nandsim_create_ctrl(struct sim_ctrl *ctrl)
159{
160	struct sim_ctrl_conf *sim_ctrl;
161
162	nand_debug(NDBG_SIM,"create controller num:%d cs:%d",ctrl->num,
163	    ctrl->num_cs);
164
165	if (ctrl->num >= MAX_SIM_DEV) {
166		return (EINVAL);
167	}
168
169	sim_ctrl = &ctrls[ctrl->num];
170	if(sim_ctrl->created)
171		return (EEXIST);
172
173	sim_ctrl->num = ctrl->num;
174	sim_ctrl->num_cs = ctrl->num_cs;
175	sim_ctrl->ecc = ctrl->ecc;
176	memcpy(sim_ctrl->ecc_layout, ctrl->ecc_layout,
177	    MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
178	strlcpy(sim_ctrl->filename, ctrl->filename,
179	    FILENAME_SIZE);
180	sim_ctrl->created = 1;
181
182	return (0);
183}
184
185static int
186nandsim_destroy_ctrl(int ctrl_num)
187{
188
189	nand_debug(NDBG_SIM,"destroy controller num:%d", ctrl_num);
190
191	if (ctrl_num >= MAX_SIM_DEV) {
192		return (EINVAL);
193	}
194
195	if (!ctrls[ctrl_num].created) {
196		return (ENODEV);
197	}
198
199	if (ctrls[ctrl_num].running) {
200		return (EBUSY);
201	}
202
203	memset(&ctrls[ctrl_num], 0, sizeof(ctrls[ctrl_num]));
204
205	return (0);
206}
207
208static int
209nandsim_ctrl_status(struct sim_ctrl *ctrl)
210{
211
212	nand_debug(NDBG_SIM,"status controller num:%d cs:%d",ctrl->num,
213	    ctrl->num_cs);
214
215	if (ctrl->num >= MAX_SIM_DEV) {
216		return (EINVAL);
217	}
218
219	ctrl->num_cs = ctrls[ctrl->num].num_cs;
220	ctrl->ecc = ctrls[ctrl->num].ecc;
221	memcpy(ctrl->ecc_layout, ctrls[ctrl->num].ecc_layout,
222	    MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
223	strlcpy(ctrl->filename, ctrls[ctrl->num].filename,
224	    FILENAME_SIZE);
225	ctrl->running = ctrls[ctrl->num].running;
226	ctrl->created = ctrls[ctrl->num].created;
227
228	return (0);
229}
230
231static int
232nandsim_create_chip(struct sim_chip *chip)
233{
234	struct sim_chip *sim_chip;
235
236	nand_debug(NDBG_SIM,"create chip num:%d at ctrl:%d", chip->num,
237	    chip->ctrl_num);
238
239	if (chip->ctrl_num >= MAX_SIM_DEV ||
240	    chip->num >= MAX_CTRL_CS) {
241		return (EINVAL);
242	}
243
244	if (ctrls[chip->ctrl_num].chips[chip->num]) {
245		return (EEXIST);
246	}
247
248	sim_chip = malloc(sizeof(*sim_chip), M_NANDSIM,
249	    M_WAITOK);
250	if (sim_chip == NULL) {
251		return (ENOMEM);
252	}
253
254	memcpy(sim_chip, chip, sizeof(*sim_chip));
255	ctrls[chip->ctrl_num].chips[chip->num] = sim_chip;
256	sim_chip->created = 1;
257
258	return (0);
259}
260
261static int
262nandsim_destroy_chip(struct sim_ctrl_chip *chip)
263{
264	struct sim_ctrl_conf *ctrl_conf;
265
266	nand_debug(NDBG_SIM,"destroy chip num:%d at ctrl:%d", chip->chip_num,
267	    chip->ctrl_num);
268
269	if (chip->ctrl_num >= MAX_SIM_DEV ||
270	    chip->chip_num >= MAX_CTRL_CS)
271		return (EINVAL);
272
273	ctrl_conf = &ctrls[chip->ctrl_num];
274
275	if (!ctrl_conf->created || !ctrl_conf->chips[chip->chip_num])
276		return (ENODEV);
277
278	if (ctrl_conf->running)
279		return (EBUSY);
280
281	free(ctrl_conf->chips[chip->chip_num], M_NANDSIM);
282	ctrl_conf->chips[chip->chip_num] = NULL;
283
284	return (0);
285}
286
287static int
288nandsim_chip_status(struct sim_chip *chip)
289{
290	struct sim_ctrl_conf *ctrl_conf;
291
292	nand_debug(NDBG_SIM,"status for chip num:%d at ctrl:%d", chip->num,
293	    chip->ctrl_num);
294
295	if (chip->ctrl_num >= MAX_SIM_DEV &&
296	    chip->num >= MAX_CTRL_CS)
297		return (EINVAL);
298
299	ctrl_conf = &ctrls[chip->ctrl_num];
300	if (!ctrl_conf->chips[chip->num])
301		chip->created = 0;
302	else
303		memcpy(chip, ctrl_conf->chips[chip->num], sizeof(*chip));
304
305	return (0);
306}
307
308static int
309nandsim_start_ctrl(int num)
310{
311	device_t nexus, ndev;
312	devclass_t nexus_devclass;
313	int ret = 0;
314
315	nand_debug(NDBG_SIM,"start ctlr num:%d", num);
316
317	if (num >= MAX_SIM_DEV)
318		return (EINVAL);
319
320	if (!ctrls[num].created)
321		return (ENODEV);
322
323	if (ctrls[num].running)
324		return (EBUSY);
325
326	/* We will add our device as a child of the nexus0 device */
327	if (!(nexus_devclass = devclass_find("nexus")) ||
328	    !(nexus = devclass_get_device(nexus_devclass, 0)))
329		return (EFAULT);
330
331	/*
332	 * Create a newbus device representing this frontend instance
333	 *
334	 * XXX powerpc nexus doesn't implement bus_add_child, so child
335	 * must be added by device_add_child().
336	 */
337#if defined(__powerpc__)
338	ndev = device_add_child(nexus, "nandsim", num);
339#else
340	ndev = BUS_ADD_CHILD(nexus, 0, "nandsim", num);
341#endif
342	if (!ndev)
343		return (EFAULT);
344
345	mtx_lock(&Giant);
346	ret = device_probe_and_attach(ndev);
347	mtx_unlock(&Giant);
348
349	if (ret == 0) {
350		ctrls[num].sim_ctrl_dev = ndev;
351		ctrls[num].running = 1;
352	}
353
354	return (ret);
355}
356
357static int
358nandsim_stop_ctrl(int num)
359{
360	device_t nexus;
361	devclass_t nexus_devclass;
362	int ret = 0;
363
364	nand_debug(NDBG_SIM,"stop controller num:%d", num);
365
366	if (num >= MAX_SIM_DEV) {
367		return (EINVAL);
368	}
369
370	if (!ctrls[num].created || !ctrls[num].running) {
371		return (ENODEV);
372	}
373
374	/* We will add our device as a child of the nexus0 device */
375	if (!(nexus_devclass = devclass_find("nexus")) ||
376	    !(nexus = devclass_get_device(nexus_devclass, 0))) {
377		return (ENODEV);
378	}
379
380	mtx_lock(&Giant);
381	if (ctrls[num].sim_ctrl_dev) {
382		ret = device_delete_child(nexus, ctrls[num].sim_ctrl_dev);
383		ctrls[num].sim_ctrl_dev = NULL;
384	}
385	mtx_unlock(&Giant);
386
387	ctrls[num].running = 0;
388
389	return (ret);
390}
391
392static struct nandsim_chip *
393get_nandsim_chip(uint8_t ctrl_num, uint8_t chip_num)
394{
395	struct nandsim_softc *sc;
396
397	if (!ctrls[ctrl_num].sim_ctrl_dev)
398		return (NULL);
399
400	sc = device_get_softc(ctrls[ctrl_num].sim_ctrl_dev);
401	return (sc->chips[chip_num]);
402}
403
404static void
405nandsim_print_log(struct sim_log *sim_log)
406{
407	struct nandsim_softc *sc;
408	int len1, len2;
409
410	if (!ctrls[sim_log->ctrl_num].sim_ctrl_dev)
411		return;
412
413	sc = device_get_softc(ctrls[sim_log->ctrl_num].sim_ctrl_dev);
414	if (sc->log_buff) {
415		len1 = strlen(&sc->log_buff[sc->log_idx + 1]);
416		if (len1 >= sim_log->len)
417			len1 = sim_log->len;
418		copyout(&sc->log_buff[sc->log_idx + 1], sim_log->log, len1);
419		len2 = strlen(sc->log_buff);
420		if (len2 >= (sim_log->len - len1))
421			len2 = (sim_log->len - len1);
422		copyout(sc->log_buff, &sim_log->log[len1], len2);
423		sim_log->len = len1 + len2;
424	}
425}
426
427static int
428nandsim_inject_error(struct sim_error *error)
429{
430	struct nandsim_chip *chip;
431	struct block_space *bs;
432	struct onfi_params *param;
433	int page, page_size, block, offset;
434
435	nand_debug(NDBG_SIM,"inject error for chip %d at ctrl %d\n",
436	    error->chip_num, error->ctrl_num);
437
438	if (error->ctrl_num >= MAX_SIM_DEV ||
439	    error->chip_num >= MAX_CTRL_CS)
440		return (EINVAL);
441
442	if (!ctrls[error->ctrl_num].created || !ctrls[error->ctrl_num].running)
443		return (ENODEV);
444
445	chip = get_nandsim_chip(error->ctrl_num, error->chip_num);
446	param = &chip->params;
447	page_size = param->bytes_per_page + param->spare_bytes_per_page;
448	block = error->page_num / param->pages_per_block;
449	page = error->page_num % param->pages_per_block;
450
451	bs = get_bs(chip->swap, block, 1);
452	if (!bs)
453		return (EINVAL);
454
455	offset = (page * page_size) + error->column;
456	memset(&bs->blk_ptr[offset], error->pattern, error->len);
457
458	return (0);
459}
460
461static int
462nandsim_set_block_state(struct sim_block_state *bs)
463{
464	struct onfi_params *params;
465	struct nandsim_chip *chip;
466	int blocks;
467
468	nand_debug(NDBG_SIM,"set block state for %d:%d block %d\n",
469	    bs->chip_num, bs->ctrl_num, bs->block_num);
470
471	if (bs->ctrl_num >= MAX_SIM_DEV ||
472	    bs->chip_num >= MAX_CTRL_CS)
473		return (EINVAL);
474
475	chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
476	params = &chip->params;
477	blocks = params->luns * params->blocks_per_lun;
478
479	if (bs->block_num > blocks)
480		return (EINVAL);
481
482	chip->blk_state[bs->block_num].is_bad = bs->state;
483
484	if (bs->wearout >= 0)
485		chip->blk_state[bs->block_num].wear_lev = bs->wearout;
486
487	return (0);
488}
489
490static int
491nandsim_get_block_state(struct sim_block_state *bs)
492{
493	struct onfi_params *params;
494	struct nandsim_chip *chip;
495	int blocks;
496
497	if (bs->ctrl_num >= MAX_SIM_DEV ||
498	    bs->chip_num >= MAX_CTRL_CS)
499		return (EINVAL);
500
501	nand_debug(NDBG_SIM,"get block state for %d:%d block %d\n",
502	    bs->chip_num, bs->ctrl_num, bs->block_num);
503
504	chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
505	params = &chip->params;
506	blocks = params->luns * params->blocks_per_lun;
507
508	if (bs->block_num > blocks)
509		return (EINVAL);
510
511	bs->state = chip->blk_state[bs->block_num].is_bad;
512	bs->wearout = chip->blk_state[bs->block_num].wear_lev;
513
514	return (0);
515}
516
517static int
518nandsim_dump(struct sim_dump *dump)
519{
520	struct nandsim_chip *chip;
521	struct block_space *bs;
522	int blk_size;
523
524	nand_debug(NDBG_SIM,"dump chip %d %d\n", dump->ctrl_num, dump->chip_num);
525
526	if (dump->ctrl_num >= MAX_SIM_DEV ||
527	    dump->chip_num >= MAX_CTRL_CS)
528		return (EINVAL);
529
530	chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
531	blk_size = chip->cg.block_size +
532	    (chip->cg.oob_size * chip->cg.pgs_per_blk);
533
534	bs = get_bs(chip->swap, dump->block_num, 0);
535	if (!bs)
536		return (EINVAL);
537
538	if (dump->len > blk_size)
539		dump->len = blk_size;
540
541	copyout(bs->blk_ptr, dump->data, dump->len);
542
543	return (0);
544}
545
546static int
547nandsim_restore(struct sim_dump *dump)
548{
549	struct nandsim_chip *chip;
550	struct block_space *bs;
551	int blk_size;
552
553	nand_debug(NDBG_SIM,"restore chip %d %d\n", dump->ctrl_num,
554	    dump->chip_num);
555
556	if (dump->ctrl_num >= MAX_SIM_DEV ||
557	    dump->chip_num >= MAX_CTRL_CS)
558		return (EINVAL);
559
560	chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
561	blk_size = chip->cg.block_size +
562	    (chip->cg.oob_size * chip->cg.pgs_per_blk);
563
564	bs = get_bs(chip->swap, dump->block_num, 1);
565	if (!bs)
566		return (EINVAL);
567
568	if (dump->len > blk_size)
569		dump->len = blk_size;
570
571
572	copyin(dump->data, bs->blk_ptr, dump->len);
573
574	return (0);
575}
576
577static int
578nandsim_freeze(struct sim_ctrl_chip *ctrl_chip)
579{
580	struct nandsim_chip *chip;
581
582	if (ctrl_chip->ctrl_num >= MAX_SIM_DEV ||
583	    ctrl_chip->chip_num >= MAX_CTRL_CS)
584		return (EINVAL);
585
586	chip = get_nandsim_chip(ctrl_chip->ctrl_num, ctrl_chip->chip_num);
587	nandsim_chip_freeze(chip);
588
589	return (0);
590}
591
592static int
593nandsim_modify(struct sim_mod *mod)
594{
595	struct sim_chip *sim_conf = NULL;
596	struct nandsim_chip *sim_chip = NULL;
597
598	nand_debug(NDBG_SIM,"modify ctlr %d chip %d", mod->ctrl_num,
599	    mod->chip_num);
600
601	if (mod->field != SIM_MOD_LOG_LEVEL) {
602		if (mod->ctrl_num >= MAX_SIM_DEV ||
603		    mod->chip_num >= MAX_CTRL_CS)
604			return (EINVAL);
605
606		sim_conf = ctrls[mod->ctrl_num].chips[mod->chip_num];
607		sim_chip = get_nandsim_chip(mod->ctrl_num, mod->chip_num);
608	}
609
610	switch (mod->field) {
611	case SIM_MOD_LOG_LEVEL:
612		nandsim_log_level = mod->new_value;
613		break;
614	case SIM_MOD_ERASE_TIME:
615		sim_conf->erase_time = sim_chip->erase_delay = mod->new_value;
616		break;
617	case SIM_MOD_PROG_TIME:
618		sim_conf->prog_time = sim_chip->prog_delay = mod->new_value;
619		break;
620	case SIM_MOD_READ_TIME:
621		sim_conf->read_time = sim_chip->read_delay = mod->new_value;
622		break;
623	case SIM_MOD_ERROR_RATIO:
624		sim_conf->error_ratio = mod->new_value;
625		sim_chip->error_ratio = mod->new_value;
626		break;
627	default:
628		break;
629	}
630
631	return (0);
632}
633static int
634nandsim_modevent(module_t mod __unused, int type, void *data __unused)
635{
636	struct sim_ctrl_chip chip_ctrl;
637	int i, j;
638
639	switch (type) {
640	case MOD_LOAD:
641		nandsim_dev = make_dev(&nandsim_cdevsw, 0,
642		    UID_ROOT, GID_WHEEL, 0666, "nandsim.ioctl");
643		break;
644	case MOD_UNLOAD:
645		for (i = 0; i < MAX_SIM_DEV; i++) {
646			nandsim_stop_ctrl(i);
647			chip_ctrl.ctrl_num = i;
648			for (j = 0; j < MAX_CTRL_CS; j++) {
649				chip_ctrl.chip_num = j;
650				nandsim_destroy_chip(&chip_ctrl);
651			}
652			nandsim_destroy_ctrl(i);
653		}
654		destroy_dev(nandsim_dev);
655		break;
656	case MOD_SHUTDOWN:
657		break;
658	default:
659		return (EOPNOTSUPP);
660	}
661	return (0);
662}
663
664DEV_MODULE(nandsim, nandsim_modevent, NULL);
665MODULE_VERSION(nandsim, 1);
666MODULE_DEPEND(nandsim, nand, 1, 1, 1);
667MODULE_DEPEND(nandsim, alq, 1, 1, 1);
668