// SPDX-License-Identifier: GPL-2.0-or-later // // See include/dev/kdev.h. #include "core/types.h" #include "core/defs.h" #include "core/param.h" #include "core/spinlock.h" #include "core/sleeplock.h" #include "fs/fs.h" #include "fs/blkdev.h" #include "dev/kdev.h" #include "dev/pty.h" #include "net/socket.h" static const char *const subsys[] = { "input", "mem", "tty", "graphics", "block", }; #define NSUBSYS ((int)(sizeof subsys / sizeof subsys[0])) #define NO_PARENT (-1) // Majors and minors are the ones /dev reports, devsw indices and all -- the // devnodes table in kernel/fs/devfs.c. udev compares what it reads here with // what stat() says about the node, so these have to be the same numbers. static const struct kdev table[] = { { "input", "input0", "", 0, 0, 0, NO_PARENT, KD_KEYBOARD }, { "input", "event0", "input/event0", 7, 0, 0, 0, KD_NONE }, { "input", "input1", "", 0, 0, 0, NO_PARENT, KD_MOUSE }, { "input", "event1", "input/event1", 7, 1, 0, 2, KD_NONE }, { "mem", "null", "null", 1, 2, 0, NO_PARENT, KD_NONE }, { "mem", "zero", "zero", 1, 5, 0, NO_PARENT, KD_NONE }, { "mem", "full", "full", 1, 7, 0, NO_PARENT, KD_NONE }, { "mem", "random", "random", 1, 8, 0, NO_PARENT, KD_NONE }, { "mem", "urandom", "urandom", 1, 9, 0, NO_PARENT, KD_NONE }, { "tty", "console", "console", 1, 1, 0, NO_PARENT, KD_NONE }, { "tty", "tty", "tty", 1, 10, 0, NO_PARENT, KD_NONE }, { "tty", "tty0", "tty0", 1, 0, 0, NO_PARENT, KD_NONE }, { "tty", "tty1", "tty1", 1, 11, 0, NO_PARENT, KD_NONE }, { "tty", "tty2", "tty2", 1, 12, 0, NO_PARENT, KD_NONE }, { "tty", "tty3", "tty3", 1, 13, 0, NO_PARENT, KD_NONE }, { "tty", "tty4", "tty4", 1, 14, 0, NO_PARENT, KD_NONE }, { "tty", "tty5", "tty5", 1, 15, 0, NO_PARENT, KD_NONE }, { "tty", "tty6", "tty6", 1, 16, 0, NO_PARENT, KD_NONE }, { "tty", "tty7", "tty7", 1, 17, 0, NO_PARENT, KD_NONE }, { "tty", "ptmx", "ptmx", PTMX_MAJOR, 0, 0, NO_PARENT, KD_NONE }, { "graphics", "fb0", "fb0", 2, 0, 0, NO_PARENT, KD_NONE }, }; #define NTABLE ((int)(sizeof table / sizeof table[0])) int kdev_nsubsys(void) { return NSUBSYS; } const char *kdev_subsys(int s) { return s >= 0 && s < NSUBSYS ? subsys[s] : 0; } int kdev_nth(int n) { if (n < NTABLE) return n < 0 ? -1 : n; n -= NTABLE; for (int k = 0, b; (b = blkdev_nth(k)) >= 0; k++) if (n-- == 0) return KDEV_BLK0 + b; return -1; } static void copy(char *d, const char *s, int cap) { int i = 0; while (s[i] && i < cap - 1) { d[i] = s[i]; i++; } d[i] = 0; } int kdev_get(int i, struct kdev *k) { if (i >= 0 && i < NTABLE) { *k = table[i]; return 0; } if (i >= KDEV_BLK0 && blkdev_present((uint)(i - KDEV_BLK0))) { memset(k, 0, sizeof(*k)); copy(k->subsys, "block", sizeof k->subsys); copy(k->name, blkdev_name((uint)(i - KDEV_BLK0)), sizeof k->name); copy(k->devname, k->name, sizeof k->devname); k->major = 8; k->minor = (short)(i - KDEV_BLK0); k->block = 1; k->parent = NO_PARENT; return 0; } return -1; } void kdev_numbers(const struct kdev *k, int *maj, int *min) { if (k->block) { *maj = k->major; *min = k->minor; return; } dev_stat_numbers(k->major, k->minor, maj, min); } int kdev_path(int i, char *buf, int cap) { struct kdev k; char tmp[160]; int n = 0; if (kdev_get(i, &k) < 0) return -1; // The top ancestor names the subsystem directory; below it, one directory // per level. int chain[4], depth = 0; for (int c = i; c >= 0 && depth < 4; ) { struct kdev ck; if (kdev_get(c, &ck) < 0) break; chain[depth++] = c; c = ck.parent; } copy(tmp, "devices/virtual/", sizeof tmp); n = (int)strlen(tmp); { struct kdev top; kdev_get(chain[depth - 1], &top); copy(tmp + n, top.subsys, (int)sizeof tmp - n); n += (int)strlen(tmp + n); } for (int d = depth - 1; d >= 0 && n < (int)sizeof tmp - 2; d--) { struct kdev ck; kdev_get(chain[d], &ck); tmp[n++] = '/'; copy(tmp + n, ck.name, (int)sizeof tmp - n); n += (int)strlen(tmp + n); } copy(buf, tmp, cap); return (int)strlen(buf); } static uint kseq; // One counter for every uevent the kernel sends, whoever builds it. uint kdev_seqnum(void) { return ++kseq; } static int put(char *b, int cap, int n, const char *s) { while (*s && n < cap - 1) b[n++] = *s++; return n; } static int putnum(char *b, int cap, int n, uint v) { char t[12]; int k = 0; do { t[k++] = '0' + v % 10; v /= 10; } while (v); while (k && n < cap - 1) b[n++] = t[--k]; return n; } // KEY=value pairs, NUL separated, as the kernel sends them; `nl` swaps the // NULs for newlines, which is what the uevent file shows. static int vars(int i, char *buf, int cap, const char *action, int nl) { struct kdev k; char path[160]; int n = 0; char sep = nl ? '\n' : 0; if (kdev_get(i, &k) < 0 || kdev_path(i, path, sizeof path) < 0) return -1; if (action) { n = put(buf, cap, n, "ACTION="); n = put(buf, cap, n, action); buf[n++] = sep; } n = put(buf, cap, n, "DEVPATH=/"); n = put(buf, cap, n, path); buf[n++] = sep; n = put(buf, cap, n, "SUBSYSTEM="); n = put(buf, cap, n, k.subsys); buf[n++] = sep; if (action) { n = put(buf, cap, n, "SEQNUM="); n = putnum(buf, cap, n, kdev_seqnum()); buf[n++] = sep; } if (k.devname[0]) { int maj, min; kdev_numbers(&k, &maj, &min); n = put(buf, cap, n, "MAJOR="); n = putnum(buf, cap, n, (uint)maj); buf[n++] = sep; n = put(buf, cap, n, "MINOR="); n = putnum(buf, cap, n, (uint)min); buf[n++] = sep; n = put(buf, cap, n, "DEVNAME="); n = put(buf, cap, n, k.devname); buf[n++] = sep; } if (k.block) { n = put(buf, cap, n, "DEVTYPE=disk"); buf[n++] = sep; } return n; } int kdev_uevent_text(int i, char *buf, int cap) { return vars(i, buf, cap, 0, 1); } void kdev_uevent(int i, const char *action) { char msg[512]; char path[160]; int n = 0; if (kdev_path(i, path, sizeof path) < 0) return; n = put(msg, sizeof msg, n, action); msg[n++] = '@'; n = put(msg, sizeof msg, n, "/"); n = put(msg, sizeof msg, n, path); msg[n++] = 0; int v = vars(i, msg + n, (int)sizeof msg - n, action, 0); if (v < 0) return; uevent_send(UEVENT_GROUP_KERNEL, msg, n + v, 0); }