1 /* This file contains the table with device <-> driver mappings. It also 2 * contains some routines to dynamically add and/ or remove device drivers 3 * or change mappings. 4 */ 5 6 #include "fs.h" 7 #include <assert.h> 8 #include <string.h> 9 #include <stdlib.h> 10 #include <ctype.h> 11 #include <unistd.h> 12 #include <minix/callnr.h> 13 #include <minix/ds.h> 14 15 /* The order of the entries in the table determines the mapping between major 16 * device numbers and device drivers. Character and block devices 17 * can be intermixed at random. The ordering determines the device numbers in 18 * /dev. Note that the major device numbers used in /dev are NOT the same as 19 * the process numbers of the device drivers. See <minix/dmap.h> for mappings. 20 */ 21 22 struct dmap dmap[NR_DEVICES]; 23 24 /*===========================================================================* 25 * lock_dmap * 26 *===========================================================================*/ 27 void lock_dmap(struct dmap *dp) 28 { 29 /* Lock a driver */ 30 struct worker_thread *org_self; 31 int r; 32 33 assert(dp != NULL); 34 assert(dp->dmap_driver != NONE); 35 36 org_self = worker_suspend(); 37 38 if ((r = mutex_lock(&dp->dmap_lock)) != 0) 39 panic("unable to get a lock on dmap: %d\n", r); 40 41 worker_resume(org_self); 42 } 43 44 /*===========================================================================* 45 * unlock_dmap * 46 *===========================================================================*/ 47 void unlock_dmap(struct dmap *dp) 48 { 49 /* Unlock a driver */ 50 int r; 51 52 assert(dp != NULL); 53 54 if ((r = mutex_unlock(&dp->dmap_lock)) != 0) 55 panic("unable to unlock dmap lock: %d\n", r); 56 } 57 58 /*===========================================================================* 59 * map_driver * 60 *===========================================================================*/ 61 static int map_driver(const char label[LABEL_MAX], devmajor_t major, 62 endpoint_t proc_nr_e) 63 { 64 /* Add a new device driver mapping in the dmap table. If the proc_nr is set to 65 * NONE, we're supposed to unmap it. 66 */ 67 size_t len; 68 struct dmap *dp; 69 70 /* Get pointer to device entry in the dmap table. */ 71 if (major < 0 || major >= NR_DEVICES) return(ENODEV); 72 dp = &dmap[major]; 73 74 /* Check if we're supposed to unmap it. */ 75 if (proc_nr_e == NONE) { 76 /* Even when a driver is now unmapped and is shortly to be mapped in 77 * due to recovery, invalidate associated filps if they're character 78 * special files. More sophisticated recovery mechanisms which would 79 * reduce the need to invalidate files are possible, but would require 80 * cooperation of the driver and more recovery framework between RS, 81 * VFS, and DS. 82 */ 83 invalidate_filp_by_char_major(major); 84 dp->dmap_driver = NONE; 85 return(OK); 86 } 87 88 if (label != NULL) { 89 len = strlen(label); 90 if (len+1 > sizeof(dp->dmap_label)) { 91 printf("VFS: map_driver: label too long: %d\n", len); 92 return(EINVAL); 93 } 94 strlcpy(dp->dmap_label, label, sizeof(dp->dmap_label)); 95 } 96 97 /* Store driver I/O routines based on type of device */ 98 dp->dmap_driver = proc_nr_e; 99 100 return(OK); 101 } 102 103 /*===========================================================================* 104 * do_mapdriver * 105 *===========================================================================*/ 106 int do_mapdriver(void) 107 { 108 /* Create a device->driver mapping. RS will tell us which major is driven by 109 * this driver, what type of device it is (regular, TTY, asynchronous, clone, 110 * etc), and its label. This label is registered with DS, and allows us to 111 * retrieve the driver's endpoint. 112 */ 113 int r, slot; 114 devmajor_t major; 115 endpoint_t endpoint; 116 vir_bytes label_vir; 117 size_t label_len; 118 char label[LABEL_MAX]; 119 struct fproc *rfp; 120 121 /* Only RS can map drivers. */ 122 if (who_e != RS_PROC_NR) return(EPERM); 123 124 label_vir = job_m_in.m_lsys_vfs_mapdriver.label; 125 label_len = job_m_in.m_lsys_vfs_mapdriver.labellen; 126 major = job_m_in.m_lsys_vfs_mapdriver.major; 127 128 /* Get the label */ 129 if (label_len > sizeof(label)) { /* Can we store this label? */ 130 printf("VFS: do_mapdriver: label too long\n"); 131 return(EINVAL); 132 } 133 r = sys_vircopy(who_e, label_vir, SELF, (vir_bytes) label, label_len, 134 CP_FLAG_TRY); 135 if (r != OK) { 136 printf("VFS: do_mapdriver: sys_vircopy failed: %d\n", r); 137 return(EINVAL); 138 } 139 if (label[label_len-1] != '\0') { 140 printf("VFS: do_mapdriver: label not null-terminated\n"); 141 return(EINVAL); 142 } 143 144 /* Now we know how the driver is called, fetch its endpoint */ 145 r = ds_retrieve_label_endpt(label, &endpoint); 146 if (r != OK) { 147 printf("VFS: do_mapdriver: label '%s' unknown\n", label); 148 return(EINVAL); 149 } 150 151 /* Process is a service */ 152 if (isokendpt(endpoint, &slot) != OK) { 153 printf("VFS: can't map driver to unknown endpoint %d\n", endpoint); 154 return(EINVAL); 155 } 156 rfp = &fproc[slot]; 157 rfp->fp_flags |= FP_SRV_PROC; 158 159 /* Try to update device mapping. */ 160 return map_driver(label, major, endpoint); 161 } 162 163 /*===========================================================================* 164 * dmap_unmap_by_endpt * 165 *===========================================================================*/ 166 void dmap_unmap_by_endpt(endpoint_t proc_e) 167 { 168 /* Lookup driver in dmap table by endpoint and unmap it */ 169 devmajor_t major; 170 int r; 171 172 for (major = 0; major < NR_DEVICES; major++) { 173 if (dmap_driver_match(proc_e, major)) { 174 /* Found driver; overwrite it with a NULL entry */ 175 if ((r = map_driver(NULL, major, NONE)) != OK) { 176 printf("VFS: unmapping driver %d for major %d failed:" 177 " %d\n", proc_e, major, r); 178 } 179 } 180 } 181 } 182 183 /*===========================================================================* 184 * map_service * 185 *===========================================================================*/ 186 int map_service(struct rprocpub *rpub) 187 { 188 /* Map a new service by storing its device driver properties. */ 189 int r, slot; 190 struct fproc *rfp; 191 192 if (IS_RPUB_BOOT_USR(rpub)) return(OK); 193 194 /* Process is a service */ 195 if (isokendpt(rpub->endpoint, &slot) != OK) { 196 printf("VFS: can't map service with unknown endpoint %d\n", 197 rpub->endpoint); 198 return(EINVAL); 199 } 200 rfp = &fproc[slot]; 201 rfp->fp_flags |= FP_SRV_PROC; 202 203 /* Not a driver, nothing more to do. */ 204 if (rpub->dev_nr == NO_DEV) return(OK); 205 206 /* Map driver. */ 207 r = map_driver(rpub->label, rpub->dev_nr, rpub->endpoint); 208 if(r != OK) return(r); 209 210 return(OK); 211 } 212 213 /*===========================================================================* 214 * init_dmap * 215 *===========================================================================*/ 216 void init_dmap(void) 217 { 218 /* Initialize the device mapping table. */ 219 int i; 220 221 memset(dmap, 0, sizeof(dmap)); 222 223 for (i = 0; i < NR_DEVICES; i++) { 224 dmap[i].dmap_driver = NONE; 225 dmap[i].dmap_servicing = INVALID_THREAD; 226 if (mutex_init(&dmap[i].dmap_lock, NULL) != 0) 227 panic("unable to initialize dmap lock"); 228 } 229 230 /* CTTY_MAJOR is a special case, which is handled by VFS itself. */ 231 if (map_driver("vfs", CTTY_MAJOR, CTTY_ENDPT) != OK) 232 panic("map_driver(CTTY_MAJOR) failed"); 233 } 234 235 /*===========================================================================* 236 * dmap_driver_match * 237 *===========================================================================*/ 238 int dmap_driver_match(endpoint_t proc, devmajor_t major) 239 { 240 if (major < 0 || major >= NR_DEVICES) return(0); 241 if (dmap[major].dmap_driver != NONE && dmap[major].dmap_driver == proc) 242 return(1); 243 244 return(0); 245 } 246 247 /*===========================================================================* 248 * dmap_by_major * 249 *===========================================================================*/ 250 struct dmap * 251 get_dmap_by_major(devmajor_t major) 252 { 253 if (major < 0 || major >= NR_DEVICES) return(NULL); 254 if (dmap[major].dmap_driver == NONE) return(NULL); 255 return(&dmap[major]); 256 } 257 258 /*===========================================================================* 259 * dmap_endpt_up * 260 *===========================================================================*/ 261 void dmap_endpt_up(endpoint_t proc_e, int is_blk) 262 { 263 /* A device driver with endpoint proc_e has been restarted. Go tell everyone 264 * that might be blocking on it that this device is 'up'. 265 */ 266 devmajor_t major; 267 struct dmap *dp; 268 struct worker_thread *worker; 269 270 if (proc_e == NONE) return; 271 272 for (major = 0; major < NR_DEVICES; major++) { 273 if ((dp = get_dmap_by_major(major)) == NULL) continue; 274 if (dp->dmap_driver == proc_e) { 275 if (is_blk) { 276 if (dp->dmap_recovering) { 277 printf("VFS: driver recovery failure for" 278 " major %d\n", major); 279 if (dp->dmap_servicing != INVALID_THREAD) { 280 worker = worker_get(dp->dmap_servicing); 281 worker_stop(worker); 282 } 283 dp->dmap_recovering = 0; 284 continue; 285 } 286 dp->dmap_recovering = 1; 287 bdev_up(major); 288 dp->dmap_recovering = 0; 289 } else { 290 if (dp->dmap_servicing != INVALID_THREAD) { 291 worker = worker_get(dp->dmap_servicing); 292 worker_stop(worker); 293 } 294 invalidate_filp_by_char_major(major); 295 } 296 } 297 } 298 } 299 300 /*===========================================================================* 301 * get_dmap * 302 *===========================================================================*/ 303 struct dmap *get_dmap(endpoint_t proc_e) 304 { 305 /* See if 'proc_e' endpoint belongs to a valid dmap entry. If so, return a 306 * pointer */ 307 devmajor_t major; 308 309 for (major = 0; major < NR_DEVICES; major++) 310 if (dmap_driver_match(proc_e, major)) 311 return(&dmap[major]); 312 313 return(NULL); 314 } 315