1433d6423SLionel Sambuc /* This file contains the table with device <-> driver mappings. It also
2433d6423SLionel Sambuc * contains some routines to dynamically add and/ or remove device drivers
3433d6423SLionel Sambuc * or change mappings.
4433d6423SLionel Sambuc */
5433d6423SLionel Sambuc
6433d6423SLionel Sambuc #include "fs.h"
7433d6423SLionel Sambuc #include <assert.h>
8433d6423SLionel Sambuc #include <string.h>
9433d6423SLionel Sambuc #include <stdlib.h>
10433d6423SLionel Sambuc #include <ctype.h>
11433d6423SLionel Sambuc #include <unistd.h>
12433d6423SLionel Sambuc #include <minix/callnr.h>
13433d6423SLionel Sambuc #include <minix/ds.h>
14433d6423SLionel Sambuc
15433d6423SLionel Sambuc /* The order of the entries in the table determines the mapping between major
16433d6423SLionel Sambuc * device numbers and device drivers. Character and block devices
17433d6423SLionel Sambuc * can be intermixed at random. The ordering determines the device numbers in
18433d6423SLionel Sambuc * /dev. Note that the major device numbers used in /dev are NOT the same as
19433d6423SLionel Sambuc * the process numbers of the device drivers. See <minix/dmap.h> for mappings.
20433d6423SLionel Sambuc */
21433d6423SLionel Sambuc
22433d6423SLionel Sambuc struct dmap dmap[NR_DEVICES];
23433d6423SLionel Sambuc
24433d6423SLionel Sambuc /*===========================================================================*
25433d6423SLionel Sambuc * lock_dmap *
26433d6423SLionel Sambuc *===========================================================================*/
lock_dmap(struct dmap * dp)27433d6423SLionel Sambuc void lock_dmap(struct dmap *dp)
28433d6423SLionel Sambuc {
29433d6423SLionel Sambuc /* Lock a driver */
30433d6423SLionel Sambuc struct worker_thread *org_self;
31433d6423SLionel Sambuc int r;
32433d6423SLionel Sambuc
33433d6423SLionel Sambuc assert(dp != NULL);
34433d6423SLionel Sambuc assert(dp->dmap_driver != NONE);
35433d6423SLionel Sambuc
36433d6423SLionel Sambuc org_self = worker_suspend();
37433d6423SLionel Sambuc
38433d6423SLionel Sambuc if ((r = mutex_lock(&dp->dmap_lock)) != 0)
39433d6423SLionel Sambuc panic("unable to get a lock on dmap: %d\n", r);
40433d6423SLionel Sambuc
41433d6423SLionel Sambuc worker_resume(org_self);
42433d6423SLionel Sambuc }
43433d6423SLionel Sambuc
44433d6423SLionel Sambuc /*===========================================================================*
45433d6423SLionel Sambuc * unlock_dmap *
46433d6423SLionel Sambuc *===========================================================================*/
unlock_dmap(struct dmap * dp)47433d6423SLionel Sambuc void unlock_dmap(struct dmap *dp)
48433d6423SLionel Sambuc {
49433d6423SLionel Sambuc /* Unlock a driver */
50433d6423SLionel Sambuc int r;
51433d6423SLionel Sambuc
52433d6423SLionel Sambuc assert(dp != NULL);
53433d6423SLionel Sambuc
54433d6423SLionel Sambuc if ((r = mutex_unlock(&dp->dmap_lock)) != 0)
55433d6423SLionel Sambuc panic("unable to unlock dmap lock: %d\n", r);
56433d6423SLionel Sambuc }
57433d6423SLionel Sambuc
58433d6423SLionel Sambuc /*===========================================================================*
59433d6423SLionel Sambuc * map_driver *
60433d6423SLionel Sambuc *===========================================================================*/
map_driver(const char label[LABEL_MAX],devmajor_t major,endpoint_t proc_nr_e)61433d6423SLionel Sambuc static int map_driver(const char label[LABEL_MAX], devmajor_t major,
62433d6423SLionel Sambuc endpoint_t proc_nr_e)
63433d6423SLionel Sambuc {
64433d6423SLionel Sambuc /* Add a new device driver mapping in the dmap table. If the proc_nr is set to
65433d6423SLionel Sambuc * NONE, we're supposed to unmap it.
66433d6423SLionel Sambuc */
67433d6423SLionel Sambuc size_t len;
68433d6423SLionel Sambuc struct dmap *dp;
69433d6423SLionel Sambuc
70433d6423SLionel Sambuc /* Get pointer to device entry in the dmap table. */
71433d6423SLionel Sambuc if (major < 0 || major >= NR_DEVICES) return(ENODEV);
72433d6423SLionel Sambuc dp = &dmap[major];
73433d6423SLionel Sambuc
74433d6423SLionel Sambuc /* Check if we're supposed to unmap it. */
75433d6423SLionel Sambuc if (proc_nr_e == NONE) {
76433d6423SLionel Sambuc /* Even when a driver is now unmapped and is shortly to be mapped in
77433d6423SLionel Sambuc * due to recovery, invalidate associated filps if they're character
78433d6423SLionel Sambuc * special files. More sophisticated recovery mechanisms which would
79433d6423SLionel Sambuc * reduce the need to invalidate files are possible, but would require
80433d6423SLionel Sambuc * cooperation of the driver and more recovery framework between RS,
81433d6423SLionel Sambuc * VFS, and DS.
82433d6423SLionel Sambuc */
83433d6423SLionel Sambuc invalidate_filp_by_char_major(major);
84433d6423SLionel Sambuc dp->dmap_driver = NONE;
85433d6423SLionel Sambuc return(OK);
86433d6423SLionel Sambuc }
87433d6423SLionel Sambuc
88433d6423SLionel Sambuc if (label != NULL) {
89433d6423SLionel Sambuc len = strlen(label);
90433d6423SLionel Sambuc if (len+1 > sizeof(dp->dmap_label)) {
913c8950ccSBen Gras printf("VFS: map_driver: label too long: %zu\n", len);
92433d6423SLionel Sambuc return(EINVAL);
93433d6423SLionel Sambuc }
94433d6423SLionel Sambuc strlcpy(dp->dmap_label, label, sizeof(dp->dmap_label));
95433d6423SLionel Sambuc }
96433d6423SLionel Sambuc
97433d6423SLionel Sambuc /* Store driver I/O routines based on type of device */
98433d6423SLionel Sambuc dp->dmap_driver = proc_nr_e;
99433d6423SLionel Sambuc
100433d6423SLionel Sambuc return(OK);
101433d6423SLionel Sambuc }
102433d6423SLionel Sambuc
103433d6423SLionel Sambuc /*===========================================================================*
104433d6423SLionel Sambuc * do_mapdriver *
105433d6423SLionel Sambuc *===========================================================================*/
do_mapdriver(void)106433d6423SLionel Sambuc int do_mapdriver(void)
107433d6423SLionel Sambuc {
108433d6423SLionel Sambuc /* Create a device->driver mapping. RS will tell us which major is driven by
109433d6423SLionel Sambuc * this driver, what type of device it is (regular, TTY, asynchronous, clone,
110433d6423SLionel Sambuc * etc), and its label. This label is registered with DS, and allows us to
111433d6423SLionel Sambuc * retrieve the driver's endpoint.
112433d6423SLionel Sambuc */
113*e3b8d4bbSDavid van Moolenbroek const int *domains;
114181fb1b2SDavid van Moolenbroek int r, slot, ndomains;
1153b468884SDavid van Moolenbroek devmajor_t major;
116433d6423SLionel Sambuc endpoint_t endpoint;
117433d6423SLionel Sambuc vir_bytes label_vir;
118433d6423SLionel Sambuc size_t label_len;
119433d6423SLionel Sambuc char label[LABEL_MAX];
120433d6423SLionel Sambuc struct fproc *rfp;
121433d6423SLionel Sambuc
122433d6423SLionel Sambuc /* Only RS can map drivers. */
123433d6423SLionel Sambuc if (who_e != RS_PROC_NR) return(EPERM);
124433d6423SLionel Sambuc
125433d6423SLionel Sambuc label_vir = job_m_in.m_lsys_vfs_mapdriver.label;
126433d6423SLionel Sambuc label_len = job_m_in.m_lsys_vfs_mapdriver.labellen;
127433d6423SLionel Sambuc major = job_m_in.m_lsys_vfs_mapdriver.major;
128181fb1b2SDavid van Moolenbroek ndomains = job_m_in.m_lsys_vfs_mapdriver.ndomains;
129*e3b8d4bbSDavid van Moolenbroek domains = job_m_in.m_lsys_vfs_mapdriver.domains;
130433d6423SLionel Sambuc
131433d6423SLionel Sambuc /* Get the label */
132433d6423SLionel Sambuc if (label_len > sizeof(label)) { /* Can we store this label? */
133433d6423SLionel Sambuc printf("VFS: do_mapdriver: label too long\n");
134433d6423SLionel Sambuc return(EINVAL);
135433d6423SLionel Sambuc }
136433d6423SLionel Sambuc r = sys_vircopy(who_e, label_vir, SELF, (vir_bytes) label, label_len,
137433d6423SLionel Sambuc CP_FLAG_TRY);
138433d6423SLionel Sambuc if (r != OK) {
139433d6423SLionel Sambuc printf("VFS: do_mapdriver: sys_vircopy failed: %d\n", r);
140433d6423SLionel Sambuc return(EINVAL);
141433d6423SLionel Sambuc }
142433d6423SLionel Sambuc if (label[label_len-1] != '\0') {
143433d6423SLionel Sambuc printf("VFS: do_mapdriver: label not null-terminated\n");
144433d6423SLionel Sambuc return(EINVAL);
145433d6423SLionel Sambuc }
146433d6423SLionel Sambuc
147433d6423SLionel Sambuc /* Now we know how the driver is called, fetch its endpoint */
148433d6423SLionel Sambuc r = ds_retrieve_label_endpt(label, &endpoint);
149433d6423SLionel Sambuc if (r != OK) {
150433d6423SLionel Sambuc printf("VFS: do_mapdriver: label '%s' unknown\n", label);
151433d6423SLionel Sambuc return(EINVAL);
152433d6423SLionel Sambuc }
153433d6423SLionel Sambuc
154433d6423SLionel Sambuc /* Process is a service */
155433d6423SLionel Sambuc if (isokendpt(endpoint, &slot) != OK) {
156433d6423SLionel Sambuc printf("VFS: can't map driver to unknown endpoint %d\n", endpoint);
157433d6423SLionel Sambuc return(EINVAL);
158433d6423SLionel Sambuc }
159433d6423SLionel Sambuc rfp = &fproc[slot];
160433d6423SLionel Sambuc rfp->fp_flags |= FP_SRV_PROC;
161433d6423SLionel Sambuc
162433d6423SLionel Sambuc /* Try to update device mapping. */
163181fb1b2SDavid van Moolenbroek if (major != NO_DEV) {
164181fb1b2SDavid van Moolenbroek if ((r = map_driver(label, major, endpoint)) != OK)
165181fb1b2SDavid van Moolenbroek return r;
166181fb1b2SDavid van Moolenbroek }
167181fb1b2SDavid van Moolenbroek if (ndomains != 0) {
168*e3b8d4bbSDavid van Moolenbroek if ((r = smap_map(label, endpoint, domains, ndomains)) != OK) {
169181fb1b2SDavid van Moolenbroek if (major != NO_DEV)
170181fb1b2SDavid van Moolenbroek map_driver(NULL, major, NONE); /* undo */
171181fb1b2SDavid van Moolenbroek return r;
172181fb1b2SDavid van Moolenbroek }
173181fb1b2SDavid van Moolenbroek }
174181fb1b2SDavid van Moolenbroek return OK;
175433d6423SLionel Sambuc }
176433d6423SLionel Sambuc
177433d6423SLionel Sambuc /*===========================================================================*
178433d6423SLionel Sambuc * dmap_unmap_by_endpt *
179433d6423SLionel Sambuc *===========================================================================*/
dmap_unmap_by_endpt(endpoint_t proc_e)180433d6423SLionel Sambuc void dmap_unmap_by_endpt(endpoint_t proc_e)
181433d6423SLionel Sambuc {
182433d6423SLionel Sambuc /* Lookup driver in dmap table by endpoint and unmap it */
1833b468884SDavid van Moolenbroek devmajor_t major;
1843b468884SDavid van Moolenbroek int r;
185433d6423SLionel Sambuc
186433d6423SLionel Sambuc for (major = 0; major < NR_DEVICES; major++) {
187433d6423SLionel Sambuc if (dmap_driver_match(proc_e, major)) {
188433d6423SLionel Sambuc /* Found driver; overwrite it with a NULL entry */
189433d6423SLionel Sambuc if ((r = map_driver(NULL, major, NONE)) != OK) {
190433d6423SLionel Sambuc printf("VFS: unmapping driver %d for major %d failed:"
191433d6423SLionel Sambuc " %d\n", proc_e, major, r);
192433d6423SLionel Sambuc }
193433d6423SLionel Sambuc }
194433d6423SLionel Sambuc }
195433d6423SLionel Sambuc }
196433d6423SLionel Sambuc
197433d6423SLionel Sambuc /*===========================================================================*
198433d6423SLionel Sambuc * map_service *
199433d6423SLionel Sambuc *===========================================================================*/
map_service(struct rprocpub * rpub)200433d6423SLionel Sambuc int map_service(struct rprocpub *rpub)
201433d6423SLionel Sambuc {
202433d6423SLionel Sambuc /* Map a new service by storing its device driver properties. */
203433d6423SLionel Sambuc int r, slot;
204433d6423SLionel Sambuc struct fproc *rfp;
205433d6423SLionel Sambuc
206433d6423SLionel Sambuc if (IS_RPUB_BOOT_USR(rpub)) return(OK);
207433d6423SLionel Sambuc
208433d6423SLionel Sambuc /* Process is a service */
209433d6423SLionel Sambuc if (isokendpt(rpub->endpoint, &slot) != OK) {
210433d6423SLionel Sambuc printf("VFS: can't map service with unknown endpoint %d\n",
211433d6423SLionel Sambuc rpub->endpoint);
212433d6423SLionel Sambuc return(EINVAL);
213433d6423SLionel Sambuc }
214433d6423SLionel Sambuc rfp = &fproc[slot];
215433d6423SLionel Sambuc rfp->fp_flags |= FP_SRV_PROC;
216433d6423SLionel Sambuc
217433d6423SLionel Sambuc /* Not a driver, nothing more to do. */
218433d6423SLionel Sambuc if (rpub->dev_nr == NO_DEV) return(OK);
219433d6423SLionel Sambuc
220433d6423SLionel Sambuc /* Map driver. */
221433d6423SLionel Sambuc r = map_driver(rpub->label, rpub->dev_nr, rpub->endpoint);
222433d6423SLionel Sambuc if(r != OK) return(r);
223433d6423SLionel Sambuc
224433d6423SLionel Sambuc return(OK);
225433d6423SLionel Sambuc }
226433d6423SLionel Sambuc
227433d6423SLionel Sambuc /*===========================================================================*
228433d6423SLionel Sambuc * init_dmap *
229433d6423SLionel Sambuc *===========================================================================*/
init_dmap(void)230433d6423SLionel Sambuc void init_dmap(void)
231433d6423SLionel Sambuc {
232433d6423SLionel Sambuc /* Initialize the device mapping table. */
233433d6423SLionel Sambuc int i;
234433d6423SLionel Sambuc
235433d6423SLionel Sambuc memset(dmap, 0, sizeof(dmap));
236433d6423SLionel Sambuc
237433d6423SLionel Sambuc for (i = 0; i < NR_DEVICES; i++) {
238433d6423SLionel Sambuc dmap[i].dmap_driver = NONE;
239433d6423SLionel Sambuc dmap[i].dmap_servicing = INVALID_THREAD;
240433d6423SLionel Sambuc if (mutex_init(&dmap[i].dmap_lock, NULL) != 0)
241433d6423SLionel Sambuc panic("unable to initialize dmap lock");
242433d6423SLionel Sambuc }
243433d6423SLionel Sambuc
244433d6423SLionel Sambuc /* CTTY_MAJOR is a special case, which is handled by VFS itself. */
245433d6423SLionel Sambuc if (map_driver("vfs", CTTY_MAJOR, CTTY_ENDPT) != OK)
246433d6423SLionel Sambuc panic("map_driver(CTTY_MAJOR) failed");
247433d6423SLionel Sambuc }
248433d6423SLionel Sambuc
249433d6423SLionel Sambuc /*===========================================================================*
250433d6423SLionel Sambuc * dmap_driver_match *
251433d6423SLionel Sambuc *===========================================================================*/
dmap_driver_match(endpoint_t proc,devmajor_t major)2523b468884SDavid van Moolenbroek int dmap_driver_match(endpoint_t proc, devmajor_t major)
253433d6423SLionel Sambuc {
254433d6423SLionel Sambuc if (major < 0 || major >= NR_DEVICES) return(0);
255433d6423SLionel Sambuc if (dmap[major].dmap_driver != NONE && dmap[major].dmap_driver == proc)
256433d6423SLionel Sambuc return(1);
257433d6423SLionel Sambuc
258433d6423SLionel Sambuc return(0);
259433d6423SLionel Sambuc }
260433d6423SLionel Sambuc
261433d6423SLionel Sambuc /*===========================================================================*
262433d6423SLionel Sambuc * dmap_by_major *
263433d6423SLionel Sambuc *===========================================================================*/
264433d6423SLionel Sambuc struct dmap *
get_dmap_by_major(devmajor_t major)2653b468884SDavid van Moolenbroek get_dmap_by_major(devmajor_t major)
266433d6423SLionel Sambuc {
267433d6423SLionel Sambuc if (major < 0 || major >= NR_DEVICES) return(NULL);
268433d6423SLionel Sambuc if (dmap[major].dmap_driver == NONE) return(NULL);
269433d6423SLionel Sambuc return(&dmap[major]);
270433d6423SLionel Sambuc }
271433d6423SLionel Sambuc
272433d6423SLionel Sambuc /*===========================================================================*
273433d6423SLionel Sambuc * dmap_endpt_up *
274433d6423SLionel Sambuc *===========================================================================*/
dmap_endpt_up(endpoint_t proc_e,int is_blk)275433d6423SLionel Sambuc void dmap_endpt_up(endpoint_t proc_e, int is_blk)
276433d6423SLionel Sambuc {
277433d6423SLionel Sambuc /* A device driver with endpoint proc_e has been restarted. Go tell everyone
278433d6423SLionel Sambuc * that might be blocking on it that this device is 'up'.
279433d6423SLionel Sambuc */
2803b468884SDavid van Moolenbroek devmajor_t major;
281433d6423SLionel Sambuc struct dmap *dp;
282433d6423SLionel Sambuc struct worker_thread *worker;
283433d6423SLionel Sambuc
284433d6423SLionel Sambuc if (proc_e == NONE) return;
285433d6423SLionel Sambuc
286433d6423SLionel Sambuc for (major = 0; major < NR_DEVICES; major++) {
287433d6423SLionel Sambuc if ((dp = get_dmap_by_major(major)) == NULL) continue;
288433d6423SLionel Sambuc if (dp->dmap_driver == proc_e) {
289433d6423SLionel Sambuc if (is_blk) {
290433d6423SLionel Sambuc if (dp->dmap_recovering) {
291433d6423SLionel Sambuc printf("VFS: driver recovery failure for"
292433d6423SLionel Sambuc " major %d\n", major);
293433d6423SLionel Sambuc if (dp->dmap_servicing != INVALID_THREAD) {
294433d6423SLionel Sambuc worker = worker_get(dp->dmap_servicing);
295433d6423SLionel Sambuc worker_stop(worker);
296433d6423SLionel Sambuc }
297433d6423SLionel Sambuc dp->dmap_recovering = 0;
298433d6423SLionel Sambuc continue;
299433d6423SLionel Sambuc }
300433d6423SLionel Sambuc dp->dmap_recovering = 1;
301433d6423SLionel Sambuc bdev_up(major);
302433d6423SLionel Sambuc dp->dmap_recovering = 0;
303433d6423SLionel Sambuc } else {
304433d6423SLionel Sambuc if (dp->dmap_servicing != INVALID_THREAD) {
305433d6423SLionel Sambuc worker = worker_get(dp->dmap_servicing);
306433d6423SLionel Sambuc worker_stop(worker);
307433d6423SLionel Sambuc }
308433d6423SLionel Sambuc invalidate_filp_by_char_major(major);
309433d6423SLionel Sambuc }
310433d6423SLionel Sambuc }
311433d6423SLionel Sambuc }
312433d6423SLionel Sambuc }
313433d6423SLionel Sambuc
314433d6423SLionel Sambuc /*===========================================================================*
315433d6423SLionel Sambuc * get_dmap *
316433d6423SLionel Sambuc *===========================================================================*/
get_dmap_by_endpt(endpoint_t proc_e)317*e3b8d4bbSDavid van Moolenbroek struct dmap *get_dmap_by_endpt(endpoint_t proc_e)
318433d6423SLionel Sambuc {
319433d6423SLionel Sambuc /* See if 'proc_e' endpoint belongs to a valid dmap entry. If so, return a
320433d6423SLionel Sambuc * pointer */
3213b468884SDavid van Moolenbroek devmajor_t major;
322433d6423SLionel Sambuc
323433d6423SLionel Sambuc for (major = 0; major < NR_DEVICES; major++)
324433d6423SLionel Sambuc if (dmap_driver_match(proc_e, major))
325433d6423SLionel Sambuc return(&dmap[major]);
326433d6423SLionel Sambuc
327433d6423SLionel Sambuc return(NULL);
328433d6423SLionel Sambuc }
329