vm: fix a null dereference on out-of-memory
[minix.git] / drivers / mmc / mmcblk.c
blob4951b33b7019b52dba26feccdbf9753295b8be51
1 /*
2 * Block driver for Multi Media Cards (MMC).
3 */
4 /* kernel headers */
5 #include <minix/syslib.h>
6 #include <minix/driver.h>
7 #include <minix/blockdriver.h>
8 #include <minix/drvlib.h>
10 /* system headers */
11 #include <sys/ioc_disk.h> /* disk IOCTL's */
13 /* usr headers */
14 #include <assert.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <signal.h>
20 /* local headers */
21 #include "mmchost.h"
22 #include "mmclog.h"
24 /* used for logging */
25 static struct mmclog log = {
26 .name = "mmc_block",
27 .log_level = LEVEL_DEBUG,
28 .log_func = default_log
31 /* holding the current host controller */
32 static struct mmc_host host;
34 /*@TODO REMOVE THIS */
35 void
36 read_tsc_64(u64_t * t)
40 u32_t
41 tsc_64_to_micros(u64_t tsc)
43 return 0;
46 #define SUB_PER_DRIVE (NR_PARTITIONS * NR_PARTITIONS)
47 #define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE)
49 /* When passing data over a grant one needs to pass
50 * a buffer to sys_safecopy copybuff is used for that*/
51 #define COPYBUFF_SIZE 0x1000 /* 4k buff */
52 static unsigned char copybuff[COPYBUFF_SIZE];
54 static struct sd_slot *get_slot(dev_t minor);
56 /* Prototypes for the block device */
57 static int block_open(dev_t minor, int access);
58 static int block_close(dev_t minor);
59 static int block_transfer(dev_t minor,
60 int do_write,
61 u64_t position,
62 endpoint_t endpt, iovec_t * iov, unsigned int nr_req, int flags);
64 static int block_ioctl(dev_t minor,
65 unsigned int request, endpoint_t endpt, cp_grant_id_t grant);
66 static struct device *block_part(dev_t minor);
68 /* System even handling */
69 static void sef_local_startup();
70 static int block_system_event_cb(int type, sef_init_info_t * info);
71 static void block_signal_handler_cb(int signo);
73 static int apply_env();
75 #if 0
76 /* set the global logging level */
77 static void set_log_level(int level);
78 #endif
80 /* Entry points for the BLOCK driver. */
81 static struct blockdriver mmc_driver = {
82 BLOCKDRIVER_TYPE_DISK, /* handle partition requests */
83 block_open, /* open or mount */
84 block_close, /* on a close */
85 block_transfer, /* does the I/O */
86 block_ioctl, /* ioclt's */
87 NULL, /* no need to clean up (yet) */
88 block_part, /* return partition information */
89 NULL, /* no geometry */
90 NULL, /* no interrupt processing */
91 NULL, /* no alarm processing */
92 NULL, /* no processing of other messages */
93 NULL /* no threading support */
96 static int
97 apply_env()
99 /* apply the env setting passed to this driver parameters accepted
100 * log_level=[0-4] (NONE,WARNING,INFO,DEBUG,TRACE) instance=[0-3]
101 * instance/bus number to use for this driver Passing these arguments
102 * is done when starting the driver using the service command in the
103 * following way service up /sbin/mmc -args "log_level=2 instance=1
104 * driver=dummy" -dev /dev/c2d0 */
105 char driver[16];
106 memset(driver, '\0', 16);
107 (void) env_get_param("driver", driver, 16);
108 if (strlen(driver) == 0
109 || strncmp(driver, "mmchs", strlen("mmchs") + 1) == 0) {
110 /* early init of host mmc host controller. This code should
111 * depend on knowing the hardware that is running bellow. */
112 host_initialize_host_structure_mmchs(&host);
113 } else if (strncmp(driver, "dummy", strlen("dummy") + 1) == 0) {
114 host_initialize_host_structure_dummy(&host);
115 } else {
116 mmc_log_warn(&log, "Unknown driver %s\n", driver);
118 #if 0
119 long v;
120 /* The following code(env_parse) uses strtol.c and needs __aeabi_idiv */
121 /* @TODO: re-enable this function when __aeabi_idiv will be present */
122 /* Initialize the verbosity level. */
123 v = 0;
124 if (env_parse("log_level", "d", 0, &v, LEVEL_NONE,
125 LEVEL_TRACE) == EP_SET) {
126 set_log_level(v);
129 /* Find out which driver instance we are. */
130 v = 0;
131 env_parse("instance", "d", 0, &v, 0, 3);
132 if (host.host_set_instance(&host, v)) {
133 mmc_log_warn(&log, "Failed to set mmc instance to %d\n", v);
134 return -1; /* NOT OK */
136 #endif
137 return OK;
142 /*===========================================================================*
143 * block_open *
144 *===========================================================================*/
145 static int
146 block_open(dev_t minor, int access)
148 struct sd_slot *slot;
149 slot = get_slot(minor);
150 int i, j;
151 int part_count, sub_part_count;
153 i = j = part_count = sub_part_count = 0;
155 if (!slot) {
156 mmc_log_debug(&log,
157 "Not handling open on non existing slot\n");
158 return EIO;
161 assert(slot->host != NULL);
163 if (!slot->host->card_detect(slot)) {
164 mmc_log_debug(&log, "No card inserted in the SD slot\n");
165 return EIO;
168 /* If we are already open just increase the open count and return */
169 if (slot->card.state == SD_MODE_DATA_TRANSFER_MODE) {
170 assert(slot->card.open_ct >= 0);
171 slot->card.open_ct++;
172 mmc_log_trace(&log, "increased open count to %d\n",
173 slot->card.open_ct);
174 return OK;
177 /* We did not have an sd-card inserted so we are going to probe for it
179 mmc_log_debug(&log, "First open on (%d)\n", minor);
180 if (!host.card_initialize(slot)) {
181 // * TODO: set card state to INVALID until removed? */
182 return EIO;
185 partition(&mmc_driver, 0 /* first card on bus */ , P_PRIMARY,
186 0 /* atapi device?? */ );
188 mmc_log_debug(&log, "descr \toffset(bytes) size(bytes)\n", minor);
190 mmc_log_debug(&log, "disk %d\t0x%016llx 0x%016llx\n", i,
191 slot->card.part[0].dv_base, slot->card.part[0].dv_size);
192 for (i = 1; i < 5; i++) {
193 if (slot->card.part[i].dv_size == 0)
194 continue;
195 part_count++;
196 mmc_log_debug(&log, "part %d\t0x%016llx 0x%016llx\n", i,
197 slot->card.part[i].dv_base, slot->card.part[i].dv_size);
198 for (j = 0; j < 4; j++) {
199 if (slot->card.subpart[(i - 1) * 4 + j].dv_size == 0)
200 continue;
201 sub_part_count++;
202 mmc_log_debug(&log,
203 " sub %d/%d\t0x%016llx 0x%016llx\n", i, j,
204 slot->card.subpart[(i - 1) * 4 + j].dv_base,
205 slot->card.subpart[(i - 1) * 4 + j].dv_size);
208 mmc_log_info(&log, "Found %d partitions and %d sub partitions\n",
209 part_count, sub_part_count);
210 slot->card.open_ct++;
211 assert(slot->card.open_ct == 1);
212 return OK;
215 /*===========================================================================*
216 * block_close *
217 *===========================================================================*/
218 static int
219 block_close(dev_t minor)
221 struct sd_slot *slot;
223 slot = get_slot(minor);
224 if (!slot) {
225 mmc_log_debug(&log,
226 "Not handling open on non existing slot\n");
227 return EIO;
230 /* if we arrived here we expect a card to be present, we will need do
231 * deal with removal later */
232 assert(slot->host != NULL);
233 assert(slot->card.open_ct >= 1);
235 /* If this is not the last open count simply decrease the counter and
236 * return */
237 if (slot->card.open_ct > 1) {
238 slot->card.open_ct--;
239 mmc_log_trace(&log, "decreased open count to %d\n",
240 slot->card.open_ct);
241 return OK;
244 assert(slot->card.open_ct == 1);
245 mmc_log_debug(&log,
246 "freeing the block device as it is no longer used\n");
248 /* release the card as check the open_ct should be 0 */
249 slot->host->card_release(&slot->card);
250 assert(slot->card.open_ct == 0);
251 return OK;
254 static int
255 copyto(endpoint_t dst_e,
256 cp_grant_id_t gr_id, vir_bytes offset, vir_bytes address, size_t bytes)
258 /* Helper function that used memcpy to copy data when the endpoint ==
259 * SELF */
260 if (dst_e == SELF) {
261 memcpy((char *) gr_id + offset, (char *) address, bytes);
262 return OK;
263 } else {
264 /* Read io_size bytes from our data at the correct * offset
265 * and write it to the output buffer at 0 */
266 return sys_safecopyto(dst_e, gr_id, offset, address, bytes);
270 static int
271 copyfrom(endpoint_t src_e,
272 cp_grant_id_t gr_id, vir_bytes offset, vir_bytes address, size_t bytes)
274 /* Helper function that used memcpy to copy data when the endpoint ==
275 * SELF */
276 if (src_e == SELF) {
277 memcpy((char *) address, (char *) gr_id + offset, bytes);
278 return OK;
279 } else {
280 return sys_safecopyfrom(src_e, gr_id, offset, address, bytes);
284 /*===========================================================================*
285 * block_transfer *
286 *===========================================================================*/
287 static int
288 block_transfer(dev_t minor, /* minor device number */
289 int do_write, /* read or write? */
290 u64_t position, /* offset on device to read or write */
291 endpoint_t endpt, /* process doing the request */
292 iovec_t * iov, /* pointer to read or write request vector */
293 unsigned int nr_req, /* length of request vector */
294 int flags /* transfer flags */
297 unsigned long counter;
298 iovec_t *ciov; /* Current IO Vector */
299 struct device *dev; /* The device used */
300 struct sd_slot *slot; /* The sd slot the requests is pointed to */
301 vir_bytes io_size; /* Size to read/write to/from the iov */
302 vir_bytes io_offset; /* Size to read/write to/from the iov */
303 vir_bytes bytes_written;
305 int r, blk_size, i;
307 /* Get the current "device" geometry */
308 dev = block_part(minor);
309 if (dev == NULL) {
310 mmc_log_warn(&log,
311 "Transfer requested on unknown device minor(%d)\n", minor);
312 /* Unknown device */
313 return ENXIO;
315 mmc_log_trace(&log, "I/O %d %s 0x%llx\n", minor,
316 (do_write) ? "Write" : "Read", position);
318 slot = get_slot(minor);
319 assert(slot);
321 if (slot->card.blk_size == 0) {
322 mmc_log_warn(&log, "Request on a card with block size of 0\n");
323 return EINVAL;
325 if (slot->card.blk_size > COPYBUFF_SIZE) {
326 mmc_log_warn(&log,
327 "Card block size (%d) exceeds internal buffer size %d\n",
328 slot->card.blk_size, COPYBUFF_SIZE);
329 return EINVAL;
332 /* It is fully up to the driver to decide on restrictions for the
333 * parameters of transfers, in those cases we return EINVAL */
334 if (position % slot->card.blk_size != 0) {
335 /* Starting at a block boundary */
336 mmc_log_warn(&log,
337 "Requests must start at a block boundary"
338 "(start,block size)=(%016llx,%08x)\n", position,
339 slot->card.blk_size);
340 return EINVAL;
343 blk_size = slot->card.blk_size;
345 bytes_written = 0;
347 /* Are we trying to start reading past the end */
348 if (position >= dev->dv_size) {
349 mmc_log_warn(&log, "start reading past drive size\n");
350 return 0;
353 ciov = iov;
354 /* do some more validation */
355 for (counter = 0; counter < nr_req; counter++) {
356 assert(ciov != NULL);
357 if (ciov->iov_size % blk_size != 0) {
358 /* transfer a multiple of blk_size */
359 mmc_log_warn(&log,
360 "Requests must start at a block boundary "
361 "(start,block size)=(%016llx,%08x)\n", position,
362 slot->card.blk_size);
363 return EINVAL;
366 if (ciov->iov_size <= 0) {
367 mmc_log_warn(&log,
368 "Invalid iov size for iov %d of %d size\n",
369 counter, nr_req, ciov->iov_size);
370 return EINVAL;
372 ciov++;
375 ciov = iov;
376 for (counter = 0; counter < nr_req; counter++) {
377 /* Assume we are to transfer the amount of data given in the
378 * input/output vector but ensure we are not doing i/o past
379 * our own boundaries */
380 io_size = ciov->iov_size;
381 io_offset = position + bytes_written;
383 /* Check we are not reading/writing past the end */
384 if (position + bytes_written + io_size > dev->dv_size) {
385 io_size = dev->dv_size - (position + bytes_written);
388 mmc_log_trace(&log,
389 "I/O %s request(%d/%d) iov(grant,size,iosize,"
390 "offset)=(%d,%d,%d,%d)\n",
391 (do_write) ? "write" : "read", counter + 1, nr_req,
392 ciov->iov_addr, ciov->iov_size, io_size, io_offset);
393 /* transfer max one block at the time */
394 for (i = 0; i < io_size / blk_size; i++) {
395 if (do_write) {
396 /* Read io_size bytes from i/o vector starting
397 * at 0 and write it to out buffer at the
398 * correct offset */
399 r = copyfrom(endpt, ciov->iov_addr,
400 i * blk_size, (vir_bytes) copybuff,
401 blk_size);
402 if (r != OK) {
403 mmc_log_warn(&log,
404 "I/O write error: %s iov(base,size)=(%d,%d)"
405 " at offset=%d\n",
406 strerror(_SIGN r), ciov->iov_addr,
407 ciov->iov_size, io_offset);
408 return EINVAL;
411 /* write a single block */
412 slot->host->write(&slot->card,
413 (dev->dv_base / blk_size) +
414 (io_offset / blk_size) + i, 1, copybuff);
415 bytes_written += blk_size;
416 } else {
417 /* read a single block info copybuff */
418 slot->host->read(&slot->card,
419 (dev->dv_base / blk_size) +
420 (io_offset / blk_size) + i, 1, copybuff);
421 /* Read io_size bytes from our data at the
422 * correct offset and write it to the output
423 * buffer at 0 */
424 r = copyto(endpt, ciov->iov_addr, i * blk_size,
425 (vir_bytes) copybuff, blk_size);
426 if (r != OK) {
427 mmc_log_warn(&log,
428 "I/O read error: %s iov(base,size)=(%d,%d)"
429 " at offset=%d\n",
430 strerror(_SIGN r), ciov->iov_addr,
431 ciov->iov_size, io_offset);
432 return EINVAL;
434 bytes_written += blk_size;
437 ciov++;
439 return bytes_written;
442 /*===========================================================================*
443 * block_ioctl *
444 *===========================================================================*/
445 static int
446 block_ioctl(dev_t minor,
447 unsigned int request, endpoint_t endpt, cp_grant_id_t grant)
449 /* IOCTL handling */
450 struct sd_slot *slot;
451 mmc_log_trace(&log,
452 "enter (minor,request,endpoint,grant)=(%d,%lu,%d)\n", minor,
453 request, endpt, grant);
455 slot = get_slot(minor);
456 if (!slot) {
457 mmc_log_warn(&log,
458 "Doing ioctl on non existing block device(%d)\n", minor);
459 return EINVAL;
462 switch (request) {
463 case DIOCOPENCT:
464 // TODO: add a check for card validity */
465 mmc_log_trace(&log, "returning open count %d\n",
466 slot->card.open_ct);
467 /* return the current open count */
468 return sys_safecopyto(endpt, grant, 0,
469 (vir_bytes) & slot->card.open_ct,
470 sizeof(slot->card.open_ct));
471 case DIOCFLUSH:
472 /* No need to flush but some devices like movinands require
473 * 500 ms inactivity */
474 return OK;
477 return EINVAL;
480 /*===========================================================================*
481 * block_part *
482 *===========================================================================*/
483 static struct device *
484 block_part(dev_t minor)
487 * Reuse the existing MINIX major/minor partitioning scheme.
488 * - 8 drives
489 * - 5 devices per drive allowing direct access to the disk and up to 4
490 * partitions (IBM style partitioning without extended partitions)
491 * - 4 Minix style sub partitions per partitions
493 struct device *dev;
494 struct sd_slot *slot;
496 dev = NULL;
497 slot = get_slot(minor);
498 if (!slot) {
499 mmc_log_warn(&log,
500 "Device information requested for non existing partition "
501 "minor(%d)\n", minor);
502 return NULL;
505 if (!slot->host->card_detect(slot)) {
506 mmc_log_warn(&log,
507 "Device information requested from empty slot(%d)\n",
508 minor);
509 return NULL;
512 if (minor < 5) {
513 /* we are talking about the first disk */
514 dev = &slot->card.part[minor];
515 mmc_log_trace(&log,
516 "returning partition(%d) (base,size)=(0x%016llx,0x%016llx)\n",
517 minor, dev->dv_base, dev->dv_size);
518 } else if (minor >= 128 && minor < 128 + 16) {
519 /* sub partitions of the first disk we don't care about the
520 * rest */
521 dev = &slot->card.subpart[minor - 128];
522 mmc_log_trace(&log,
523 "returning sub partition(%d) (base,size)=(0x%016llx,0x%016llx)\n",
524 minor - 128, dev->dv_base, dev->dv_size);
526 } else {
527 mmc_log_warn(&log,
528 "Device information requested for non existing "
529 "partition minor(%d)\n", minor);
531 return dev;
534 /*===========================================================================*
535 * sef_local_startup *
536 *===========================================================================*/
537 static void
538 sef_local_startup()
540 mmc_log_info(&log, "Initializing the MMC block device\n");
541 if (apply_env()) {
542 mmc_log_warn(&log,
543 "Failed while applying environment settings\n");
544 exit(EXIT_FAILURE);
547 if (host.host_init(&host)) {
548 mmc_log_warn(&log,
549 "Failed to initialize the host controller\n");
550 exit(EXIT_FAILURE);
553 * Register callbacks for fresh start, live update and restart.
554 * Use the same function for all event types
556 sef_setcb_init_fresh(block_system_event_cb);
557 sef_setcb_init_lu(block_system_event_cb);
559 /* Register a signal handler */
560 sef_setcb_signal_handler(block_signal_handler_cb);
562 /* SEF startup */
563 sef_startup();
566 /*===========================================================================*
567 * block_system_event_cb *
568 *===========================================================================*/
569 static int
570 block_system_event_cb(int type, sef_init_info_t * info)
573 * Callbacks for the System event framework as registered in
574 * sef_local_startup */
575 switch (type) {
576 case SEF_INIT_FRESH:
577 mmc_log_info(&log, "System event framework fresh start\n");
578 break;
580 case SEF_INIT_LU:
581 /* Restore the state. post update */
582 mmc_log_info(&log, "System event framework live update\n");
583 break;
585 case SEF_INIT_RESTART:
586 mmc_log_info(&log, "System event framework post restart\n");
587 break;
589 blockdriver_announce(type);
590 return OK;
593 /*===========================================================================*
594 * block_signal_handler_cb *
595 *===========================================================================*/
596 static void
597 block_signal_handler_cb(int signo)
599 mmc_log_debug(&log, "System event framework signal handler sig(%d)\n",
600 signo);
601 /* Only check for termination signal, ignore anything else. */
602 if (signo != SIGTERM)
603 return;
604 // FIXME shutdown
605 exit(0);
608 #define IS_MINIX_SUB_PARTITION_MINOR(minor) (minor >= MINOR_d0p0s0 )
610 static struct sd_slot *
611 get_slot(dev_t minor)
614 * Get an sd_slot based on the minor number.
616 * This driver only supports a single card at at time. Also as
617 * we are following the major/minor scheme of other driver we
618 * must return a slot for all minors on disk 0 these are 0-5
619 * for the disk and 4 main partitions and
620 * number 128 till 144 for sub partitions.
622 /* If this is a minor for the first disk (e.g. minor 0 till 5) */
623 if (minor / DEV_PER_DRIVE == 0) {
624 /* we are talking about the first disk and that is all we
625 * support */
626 return &host.slot[0];
627 } else if (IS_MINIX_SUB_PARTITION_MINOR(minor)
628 && (((minor - MINOR_d0p0s0) / SUB_PER_DRIVE) == 0)) {
629 /* a minor from the first disk */
630 return &host.slot[0];
631 } else {
632 mmc_log_trace(&log,
633 "Device information requested for non existing partition "
634 "minor(%d)\n", minor);
635 return NULL;
639 #if 0
640 static void
641 set_log_level(int level)
643 if (level < 0 || level >= 4) {
644 return;
646 mmc_log_debug(&log, "Setting verbosity level to %d\n", level);
647 log.log_level = level;
648 if (host.set_log_level) {
649 host.set_log_level(level);
652 #endif
655 main(int argc, char **argv)
658 /* Set and apply the environment */
659 env_setargs(argc, argv);
661 sef_local_startup();
662 blockdriver_task(&mmc_driver);
663 return EXIT_SUCCESS;