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: %zu\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 const int *domains; 114 int r, slot, ndomains; 115 devmajor_t major; 116 endpoint_t endpoint; 117 vir_bytes label_vir; 118 size_t label_len; 119 char label[LABEL_MAX]; 120 struct fproc *rfp; 121 122 /* Only RS can map drivers. */ 123 if (who_e != RS_PROC_NR) return(EPERM); 124 125 label_vir = job_m_in.m_lsys_vfs_mapdriver.label; 126 label_len = job_m_in.m_lsys_vfs_mapdriver.labellen; 127 major = job_m_in.m_lsys_vfs_mapdriver.major; 128 ndomains = job_m_in.m_lsys_vfs_mapdriver.ndomains; 129 domains = job_m_in.m_lsys_vfs_mapdriver.domains; 130 131 /* Get the label */ 132 if (label_len > sizeof(label)) { /* Can we store this label? */ 133 printf("VFS: do_mapdriver: label too long\n"); 134 return(EINVAL); 135 } 136 r = sys_vircopy(who_e, label_vir, SELF, (vir_bytes) label, label_len, 137 CP_FLAG_TRY); 138 if (r != OK) { 139 printf("VFS: do_mapdriver: sys_vircopy failed: %d\n", r); 140 return(EINVAL); 141 } 142 if (label[label_len-1] != '\0') { 143 printf("VFS: do_mapdriver: label not null-terminated\n"); 144 return(EINVAL); 145 } 146 147 /* Now we know how the driver is called, fetch its endpoint */ 148 r = ds_retrieve_label_endpt(label, &endpoint); 149 if (r != OK) { 150 printf("VFS: do_mapdriver: label '%s' unknown\n", label); 151 return(EINVAL); 152 } 153 154 /* Process is a service */ 155 if (isokendpt(endpoint, &slot) != OK) { 156 printf("VFS: can't map driver to unknown endpoint %d\n", endpoint); 157 return(EINVAL); 158 } 159 rfp = &fproc[slot]; 160 rfp->fp_flags |= FP_SRV_PROC; 161 162 /* Try to update device mapping. */ 163 if (major != NO_DEV) { 164 if ((r = map_driver(label, major, endpoint)) != OK) 165 return r; 166 } 167 if (ndomains != 0) { 168 if ((r = smap_map(label, endpoint, domains, ndomains)) != OK) { 169 if (major != NO_DEV) 170 map_driver(NULL, major, NONE); /* undo */ 171 return r; 172 } 173 } 174 return OK; 175 } 176 177 /*===========================================================================* 178 * dmap_unmap_by_endpt * 179 *===========================================================================*/ 180 void dmap_unmap_by_endpt(endpoint_t proc_e) 181 { 182 /* Lookup driver in dmap table by endpoint and unmap it */ 183 devmajor_t major; 184 int r; 185 186 for (major = 0; major < NR_DEVICES; major++) { 187 if (dmap_driver_match(proc_e, major)) { 188 /* Found driver; overwrite it with a NULL entry */ 189 if ((r = map_driver(NULL, major, NONE)) != OK) { 190 printf("VFS: unmapping driver %d for major %d failed:" 191 " %d\n", proc_e, major, r); 192 } 193 } 194 } 195 } 196 197 /*===========================================================================* 198 * map_service * 199 *===========================================================================*/ 200 int map_service(struct rprocpub *rpub) 201 { 202 /* Map a new service by storing its device driver properties. */ 203 int r, slot; 204 struct fproc *rfp; 205 206 if (IS_RPUB_BOOT_USR(rpub)) return(OK); 207 208 /* Process is a service */ 209 if (isokendpt(rpub->endpoint, &slot) != OK) { 210 printf("VFS: can't map service with unknown endpoint %d\n", 211 rpub->endpoint); 212 return(EINVAL); 213 } 214 rfp = &fproc[slot]; 215 rfp->fp_flags |= FP_SRV_PROC; 216 217 /* Not a driver, nothing more to do. */ 218 if (rpub->dev_nr == NO_DEV) return(OK); 219 220 /* Map driver. */ 221 r = map_driver(rpub->label, rpub->dev_nr, rpub->endpoint); 222 if(r != OK) return(r); 223 224 return(OK); 225 } 226 227 /*===========================================================================* 228 * init_dmap * 229 *===========================================================================*/ 230 void init_dmap(void) 231 { 232 /* Initialize the device mapping table. */ 233 int i; 234 235 memset(dmap, 0, sizeof(dmap)); 236 237 for (i = 0; i < NR_DEVICES; i++) { 238 dmap[i].dmap_driver = NONE; 239 dmap[i].dmap_servicing = INVALID_THREAD; 240 if (mutex_init(&dmap[i].dmap_lock, NULL) != 0) 241 panic("unable to initialize dmap lock"); 242 } 243 244 /* CTTY_MAJOR is a special case, which is handled by VFS itself. */ 245 if (map_driver("vfs", CTTY_MAJOR, CTTY_ENDPT) != OK) 246 panic("map_driver(CTTY_MAJOR) failed"); 247 } 248 249 /*===========================================================================* 250 * dmap_driver_match * 251 *===========================================================================*/ 252 int dmap_driver_match(endpoint_t proc, devmajor_t major) 253 { 254 if (major < 0 || major >= NR_DEVICES) return(0); 255 if (dmap[major].dmap_driver != NONE && dmap[major].dmap_driver == proc) 256 return(1); 257 258 return(0); 259 } 260 261 /*===========================================================================* 262 * dmap_by_major * 263 *===========================================================================*/ 264 struct dmap * 265 get_dmap_by_major(devmajor_t major) 266 { 267 if (major < 0 || major >= NR_DEVICES) return(NULL); 268 if (dmap[major].dmap_driver == NONE) return(NULL); 269 return(&dmap[major]); 270 } 271 272 /*===========================================================================* 273 * dmap_endpt_up * 274 *===========================================================================*/ 275 void dmap_endpt_up(endpoint_t proc_e, int is_blk) 276 { 277 /* A device driver with endpoint proc_e has been restarted. Go tell everyone 278 * that might be blocking on it that this device is 'up'. 279 */ 280 devmajor_t major; 281 struct dmap *dp; 282 struct worker_thread *worker; 283 284 if (proc_e == NONE) return; 285 286 for (major = 0; major < NR_DEVICES; major++) { 287 if ((dp = get_dmap_by_major(major)) == NULL) continue; 288 if (dp->dmap_driver == proc_e) { 289 if (is_blk) { 290 if (dp->dmap_recovering) { 291 printf("VFS: driver recovery failure for" 292 " major %d\n", major); 293 if (dp->dmap_servicing != INVALID_THREAD) { 294 worker = worker_get(dp->dmap_servicing); 295 worker_stop(worker); 296 } 297 dp->dmap_recovering = 0; 298 continue; 299 } 300 dp->dmap_recovering = 1; 301 bdev_up(major); 302 dp->dmap_recovering = 0; 303 } else { 304 if (dp->dmap_servicing != INVALID_THREAD) { 305 worker = worker_get(dp->dmap_servicing); 306 worker_stop(worker); 307 } 308 invalidate_filp_by_char_major(major); 309 } 310 } 311 } 312 } 313 314 /*===========================================================================* 315 * get_dmap * 316 *===========================================================================*/ 317 struct dmap *get_dmap_by_endpt(endpoint_t proc_e) 318 { 319 /* See if 'proc_e' endpoint belongs to a valid dmap entry. If so, return a 320 * pointer */ 321 devmajor_t major; 322 323 for (major = 0; major < NR_DEVICES; major++) 324 if (dmap_driver_match(proc_e, major)) 325 return(&dmap[major]); 326 327 return(NULL); 328 } 329