1 /* This file contains the device independent block driver interface. 2 * 3 * Block drivers support the following requests. Message format m10 is used. 4 * Field names are prefixed with BDEV_. Separate field names are used for the 5 * "access", "request", and "user" fields. 6 * 7 * m_type MINOR COUNT GRANT FLAGS ID REQUEST POS 8 * +--------------+--------+----------+-------+-------+------+---------+------+ 9 * | BDEV_OPEN | minor | access | | | id | | | 10 * |--------------+--------+----------+-------+-------+------+---------+------| 11 * | BDEV_CLOSE | minor | | | | id | | | 12 * |--------------+--------+----------+-------+-------+------+---------+------| 13 * | BDEV_READ | minor | bytes | grant | flags | id | | pos. | 14 * |--------------+--------+----------+-------+-------+------+---------+------| 15 * | BDEV_WRITE | minor | bytes | grant | flags | id | | pos. | 16 * |--------------+--------+----------+-------+-------+------+---------+------| 17 * | BDEV_GATHER | minor | elements | grant | flags | id | | pos. | 18 * |--------------+--------+----------+-------+-------+------+---------+------| 19 * | BDEV_SCATTER | minor | elements | grant | flags | id | | pos. | 20 * |--------------+--------+----------+-------+-------+------+---------+------| 21 * | BDEV_IOCTL | minor | | grant | user | id | request | | 22 * ---------------------------------------------------------------------------- 23 * 24 * The following reply message is used for all requests. 25 * 26 * m_type STATUS ID 27 * +--------------+--------+----------+-------+-------+------+---------+------+ 28 * | BDEV_REPLY | status | | | | id | | | 29 * ---------------------------------------------------------------------------- 30 * 31 * Changes: 32 * Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek) 33 * Aug 27, 2011 move common functions into driver.c (A. Welzel) 34 * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder) 35 * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder) 36 * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder) 37 * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot) 38 */ 39 40 #include <minix/drivers.h> 41 #include <minix/blockdriver.h> 42 #include <minix/ds.h> 43 #include <sys/ioc_block.h> 44 #include <sys/ioc_disk.h> 45 46 #include "driver.h" 47 #include "mq.h" 48 #include "trace.h" 49 50 /* Management data for opened devices. */ 51 static int open_devs[MAX_NR_OPEN_DEVICES]; 52 static int next_open_devs_slot = 0; 53 54 /*===========================================================================* 55 * clear_open_devs * 56 *===========================================================================*/ 57 static void clear_open_devs(void) 58 { 59 /* Reset the set of previously opened minor devices. */ 60 next_open_devs_slot = 0; 61 } 62 63 /*===========================================================================* 64 * is_open_dev * 65 *===========================================================================*/ 66 static int is_open_dev(int device) 67 { 68 /* Check whether the given minor device has previously been opened. */ 69 int i; 70 71 for (i = 0; i < next_open_devs_slot; i++) 72 if (open_devs[i] == device) 73 return TRUE; 74 75 return FALSE; 76 } 77 78 /*===========================================================================* 79 * set_open_dev * 80 *===========================================================================*/ 81 static void set_open_dev(int device) 82 { 83 /* Mark the given minor device as having been opened. */ 84 85 if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES) 86 panic("out of slots for open devices"); 87 88 open_devs[next_open_devs_slot] = device; 89 next_open_devs_slot++; 90 } 91 92 /*===========================================================================* 93 * blockdriver_announce * 94 *===========================================================================*/ 95 void blockdriver_announce(int type) 96 { 97 /* Announce we are up after a fresh start or a restart. */ 98 int r; 99 char key[DS_MAX_KEYLEN]; 100 char label[DS_MAX_KEYLEN]; 101 char *driver_prefix = "drv.blk."; 102 103 /* Callers are allowed to use ipc_sendrec to communicate with drivers. 104 * For this reason, there may blocked callers when a driver restarts. 105 * Ask the kernel to unblock them (if any). Note that most block drivers 106 * will not restart statefully, and thus will skip this code. 107 */ 108 if (type == SEF_INIT_RESTART) { 109 if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS)) != OK) 110 panic("blockdriver_init: sys_statectl failed: %d", r); 111 } 112 113 /* Publish a driver up event. */ 114 if ((r = ds_retrieve_label_name(label, sef_self())) != OK) 115 panic("blockdriver_init: unable to get own label: %d", r); 116 117 snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label); 118 if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK) 119 panic("blockdriver_init: unable to publish driver up event: %d", r); 120 121 /* Expect an open for any device before serving regular driver requests. */ 122 clear_open_devs(); 123 124 /* Initialize or reset the message queue. */ 125 mq_init(); 126 } 127 128 /*===========================================================================* 129 * send_reply * 130 *===========================================================================*/ 131 static void send_reply(endpoint_t endpt, message *m_ptr, int ipc_status) 132 { 133 /* Send a reply message to a request. */ 134 int r; 135 136 /* If we would block sending the message, send it asynchronously. The NOREPLY 137 * flag is set because the caller may also issue a SENDREC (mixing sync and 138 * async comm), and the asynchronous reply could otherwise end up satisfying 139 * the SENDREC's receive part, after which our next SENDNB call would fail. 140 */ 141 if (IPC_STATUS_CALL(ipc_status) == SENDREC) 142 r = ipc_sendnb(endpt, m_ptr); 143 else 144 r = asynsend3(endpt, m_ptr, AMF_NOREPLY); 145 146 if (r != OK) 147 printf("blockdriver: unable to send reply to %d: %d\n", endpt, r); 148 } 149 150 /*===========================================================================* 151 * blockdriver_reply * 152 *===========================================================================*/ 153 void blockdriver_reply(message *m_ptr, int ipc_status, int reply) 154 { 155 /* Reply to a block request sent to the driver. */ 156 message m_reply; 157 158 if (reply == EDONTREPLY) 159 return; 160 161 memset(&m_reply, 0, sizeof(m_reply)); 162 163 m_reply.m_type = BDEV_REPLY; 164 m_reply.m_lblockdriver_lbdev_reply.status = reply; 165 m_reply.m_lblockdriver_lbdev_reply.id = m_ptr->m_lbdev_lblockdriver_msg.id; 166 167 send_reply(m_ptr->m_source, &m_reply, ipc_status); 168 } 169 170 /*===========================================================================* 171 * do_open * 172 *===========================================================================*/ 173 static int do_open(struct blockdriver *bdp, message *mp) 174 { 175 /* Open a minor device. */ 176 177 return (*bdp->bdr_open)(mp->m_lbdev_lblockdriver_msg.minor, mp->m_lbdev_lblockdriver_msg.access); 178 } 179 180 /*===========================================================================* 181 * do_close * 182 *===========================================================================*/ 183 static int do_close(struct blockdriver *bdp, message *mp) 184 { 185 /* Close a minor device. */ 186 187 return (*bdp->bdr_close)(mp->m_lbdev_lblockdriver_msg.minor); 188 } 189 190 /*===========================================================================* 191 * do_rdwt * 192 *===========================================================================*/ 193 static int do_rdwt(struct blockdriver *bdp, message *mp) 194 { 195 /* Carry out a single read or write request. */ 196 iovec_t iovec1; 197 u64_t position; 198 int do_write; 199 ssize_t r; 200 201 /* Disk address? Address and length of the user buffer? */ 202 if (mp->m_lbdev_lblockdriver_msg.count < 0) return EINVAL; 203 204 /* Create a one element scatter/gather vector for the buffer. */ 205 iovec1.iov_addr = mp->m_lbdev_lblockdriver_msg.grant; 206 iovec1.iov_size = mp->m_lbdev_lblockdriver_msg.count; 207 208 /* Transfer bytes from/to the device. */ 209 do_write = (mp->m_type == BDEV_WRITE); 210 position = mp->m_lbdev_lblockdriver_msg.pos; 211 212 r = (*bdp->bdr_transfer)(mp->m_lbdev_lblockdriver_msg.minor, do_write, position, mp->m_source, 213 &iovec1, 1, mp->m_lbdev_lblockdriver_msg.flags); 214 215 /* Return the number of bytes transferred or an error code. */ 216 return r; 217 } 218 219 /*===========================================================================* 220 * do_vrdwt * 221 *===========================================================================*/ 222 static int do_vrdwt(struct blockdriver *bdp, message *mp, thread_id_t id) 223 { 224 /* Carry out an device read or write to/from a vector of buffers. */ 225 iovec_t iovec[NR_IOREQS]; 226 unsigned int nr_req; 227 u64_t position; 228 int i, do_write; 229 ssize_t r, size; 230 231 /* Copy the vector from the caller to kernel space. */ 232 nr_req = mp->m_lbdev_lblockdriver_msg.count; /* Length of I/O vector */ 233 if (nr_req > NR_IOREQS) nr_req = NR_IOREQS; 234 235 if (OK != sys_safecopyfrom(mp->m_source, (vir_bytes) mp->m_lbdev_lblockdriver_msg.grant, 236 0, (vir_bytes) iovec, nr_req * sizeof(iovec[0]))) { 237 printf("blockdriver: bad I/O vector by: %d\n", mp->m_source); 238 return EINVAL; 239 } 240 241 /* Check for overflow condition, and update the size for block tracing. */ 242 for (i = size = 0; i < nr_req; i++) { 243 if ((ssize_t) (size + iovec[i].iov_size) < size) return EINVAL; 244 size += iovec[i].iov_size; 245 } 246 247 trace_setsize(id, size); 248 249 /* Transfer bytes from/to the device. */ 250 do_write = (mp->m_type == BDEV_SCATTER); 251 position = mp->m_lbdev_lblockdriver_msg.pos; 252 253 r = (*bdp->bdr_transfer)(mp->m_lbdev_lblockdriver_msg.minor, do_write, position, mp->m_source, 254 iovec, nr_req, mp->m_lbdev_lblockdriver_msg.flags); 255 256 /* Return the number of bytes transferred or an error code. */ 257 return r; 258 } 259 260 /*===========================================================================* 261 * do_dioctl * 262 *===========================================================================*/ 263 static int do_dioctl(struct blockdriver *bdp, dev_t minor, 264 unsigned long request, endpoint_t endpt, cp_grant_id_t grant) 265 { 266 /* Carry out a disk-specific I/O control request. */ 267 struct device *dv; 268 struct part_geom entry; 269 int r = EINVAL; 270 271 switch (request) { 272 case DIOCSETP: 273 /* Copy just this one partition table entry. */ 274 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &entry, 275 sizeof(entry)); 276 if (r != OK) 277 return r; 278 279 if ((dv = (*bdp->bdr_part)(minor)) == NULL) 280 return ENXIO; 281 dv->dv_base = entry.base; 282 dv->dv_size = entry.size; 283 284 break; 285 286 case DIOCGETP: 287 /* Return a partition table entry and the geometry of the drive. */ 288 if ((dv = (*bdp->bdr_part)(minor)) == NULL) 289 return ENXIO; 290 entry.base = dv->dv_base; 291 entry.size = dv->dv_size; 292 if (bdp->bdr_geometry) { 293 (*bdp->bdr_geometry)(minor, &entry); 294 } else { 295 /* The driver doesn't care -- make up fake geometry. */ 296 entry.cylinders = (unsigned long)(entry.size / SECTOR_SIZE) / 297 (64 * 32); 298 entry.heads = 64; 299 entry.sectors = 32; 300 } 301 302 r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &entry, sizeof(entry)); 303 304 break; 305 } 306 307 return r; 308 } 309 310 /*===========================================================================* 311 * do_ioctl * 312 *===========================================================================*/ 313 static int do_ioctl(struct blockdriver *bdp, message *mp) 314 { 315 /* Carry out an I/O control request. We forward block trace control requests 316 * to the tracing module, and handle setting/getting partitions when the driver 317 * has specified that it is a disk driver. 318 */ 319 dev_t minor; 320 unsigned long request; 321 cp_grant_id_t grant; 322 endpoint_t user_endpt; 323 int r; 324 325 minor = mp->m_lbdev_lblockdriver_msg.minor; 326 request = mp->m_lbdev_lblockdriver_msg.request; 327 grant = mp->m_lbdev_lblockdriver_msg.grant; 328 user_endpt = mp->m_lbdev_lblockdriver_msg.user; 329 330 switch (request) { 331 case BIOCTRACEBUF: 332 case BIOCTRACECTL: 333 case BIOCTRACEGET: 334 /* Block trace control. */ 335 r = trace_ctl(minor, request, mp->m_source, grant); 336 337 break; 338 339 case DIOCSETP: 340 case DIOCGETP: 341 /* Handle disk-specific IOCTLs only for disk-type drivers. */ 342 if (bdp->bdr_type == BLOCKDRIVER_TYPE_DISK) { 343 /* Disk partition control. */ 344 r = do_dioctl(bdp, minor, request, mp->m_source, grant); 345 346 break; 347 } 348 349 /* fall-through */ 350 default: 351 if (bdp->bdr_ioctl) 352 r = (*bdp->bdr_ioctl)(minor, request, mp->m_source, grant, 353 user_endpt); 354 else 355 r = ENOTTY; 356 } 357 358 return r; 359 } 360 361 /*===========================================================================* 362 * do_char_open * 363 *===========================================================================*/ 364 static void do_char_open(message *m_ptr, int ipc_status) 365 { 366 /* Reply to a character driver open request stating there is no such device. */ 367 message m_reply; 368 369 memset(&m_reply, 0, sizeof(m_reply)); 370 371 m_reply.m_type = CDEV_REPLY; 372 m_reply.m_lchardriver_vfs_reply.status = ENXIO; 373 m_reply.m_lchardriver_vfs_reply.id = m_ptr->m_vfs_lchardriver_openclose.id; 374 375 send_reply(m_ptr->m_source, &m_reply, ipc_status); 376 } 377 378 /*===========================================================================* 379 * blockdriver_process_on_thread * 380 *===========================================================================*/ 381 void blockdriver_process_on_thread(struct blockdriver *bdp, message *m_ptr, 382 int ipc_status, thread_id_t id) 383 { 384 /* Call the appropiate driver function, based on the type of request. Send 385 * a result code to the caller. The call is processed in the context of the 386 * given thread ID, which may be SINGLE_THREAD for single-threaded callers. 387 */ 388 int r; 389 390 /* Check for notifications first. We never reply to notifications. */ 391 if (is_ipc_notify(ipc_status)) { 392 switch (_ENDPOINT_P(m_ptr->m_source)) { 393 case HARDWARE: 394 if (bdp->bdr_intr) 395 (*bdp->bdr_intr)(m_ptr->m_notify.interrupts); 396 break; 397 398 case CLOCK: 399 if (bdp->bdr_alarm) 400 (*bdp->bdr_alarm)(m_ptr->m_notify.timestamp); 401 break; 402 403 default: 404 if (bdp->bdr_other) 405 (*bdp->bdr_other)(m_ptr, ipc_status); 406 } 407 408 return; /* do not send a reply */ 409 } 410 411 /* Reply to character driver open requests with an error code. Otherwise, if 412 * someone creates a character device node for a block driver, opening that 413 * device node will cause the corresponding VFS thread to block forever. 414 */ 415 if (m_ptr->m_type == CDEV_OPEN) { 416 do_char_open(m_ptr, ipc_status); 417 418 return; 419 } 420 421 /* We might get spurious requests if the driver has been restarted. Deny any 422 * requests on devices that have not previously been opened, signaling the 423 * caller that something went wrong. 424 */ 425 if (IS_BDEV_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->m_lbdev_lblockdriver_msg.minor)) { 426 /* Reply ERESTART to spurious requests for unopened devices. */ 427 if (m_ptr->m_type != BDEV_OPEN) { 428 blockdriver_reply(m_ptr, ipc_status, ERESTART); 429 430 return; 431 } 432 433 /* Mark the device as opened otherwise. */ 434 set_open_dev(m_ptr->m_lbdev_lblockdriver_msg.minor); 435 } 436 437 trace_start(id, m_ptr); 438 439 /* Call the appropriate function(s) for this request. */ 440 switch (m_ptr->m_type) { 441 case BDEV_OPEN: r = do_open(bdp, m_ptr); break; 442 case BDEV_CLOSE: r = do_close(bdp, m_ptr); break; 443 case BDEV_READ: 444 case BDEV_WRITE: r = do_rdwt(bdp, m_ptr); break; 445 case BDEV_GATHER: 446 case BDEV_SCATTER: r = do_vrdwt(bdp, m_ptr, id); break; 447 case BDEV_IOCTL: r = do_ioctl(bdp, m_ptr); break; 448 default: 449 if (bdp->bdr_other != NULL) 450 (*bdp->bdr_other)(m_ptr, ipc_status); 451 452 return; /* do not send a reply */ 453 } 454 455 /* Let the driver perform any cleanup. */ 456 if (bdp->bdr_cleanup != NULL) 457 (*bdp->bdr_cleanup)(); 458 459 trace_finish(id, r); 460 461 blockdriver_reply(m_ptr, ipc_status, r); 462 } 463