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