Friday, March 9, 2018

BSD Kernel Hacking - Part 2 (character pseudo-devices)

Continuing on from the last post, I now tackle character devices. It turns out that OpenBSD is quite a lot different than FreeBSD in the way that devices are registered. To begin, I need to set up a cdevsw entry for the new device. Rather than defining the struct directly in the implementation code, it is implemented via a macro which is called in the cdevsw struct definition in sys/arch/amd64/amd64/conf.c

As opposed to FreeBSD, the cdevsw structs are built out by a macro, this macro will take the number of devices being initialized, and the name of the module passed to it (e.g. "cdev_example_init(1, example)") and fill out struct cdevsw to point to the appropriate functions via name concatenation (e.g. the open function will be registered as exampleopen).

I begin by adding a cdev_example_init macro to sys/sys/conf.h, then call the macro within the cdevsw[] array in sys/arch/amd64/amd64/conf.c. Also, while I'm in here, I add a call to the cdev_decl() macro. This should take care of the cdevsw setup portion of the code.

From there, I add example.c under sys/dev/ and build out basic open, close, read, and write functions for it, and add it to files.conf. Much to my surprise, after fixing a few compile errors for missing headers, the kernel compiles and installs fine. Once the machine had booted into my new kernel, I ran a "doas mknod /dev/example c 98 0" to create a node for the device in /dev. In this case, 98 was the position in the cdevsw[] array where my call to cdev_exampl_init landed. In OpenBSD, the major node number is simply an index into the cdevsw array.

I had to switch from using copystr to uiomove in order to get I/O working properly, but once I did, the basic interface code worked fine.

Finally, I decided to work with the MAKEDEV script to ensure that my device would be present in a release, if I so chose to make one. The script required me to add a new case for "example)" with a call to the M function and a few parameters to set the device node name, Major/Minor numbers, permissions, and ownership. In order for etc/etc.amd64/MAKEDEV to be copied to /dev by sysmerge, I had to do a full build of the system. This is a bit time consuming, and there might be a better way, but it worked for my immediate purposes.

Index: etc/etc.amd64/MAKEDEV
===================================================================
RCS file: /cvs/src/etc/etc.amd64/MAKEDEV,v
retrieving revision 1.118
diff -u -p -u -p -r1.118 MAKEDEV
--- etc/etc.amd64/MAKEDEV 14 Nov 2017 20:22:04 -0000 1.118
+++ etc/etc.amd64/MAKEDEV 5 Mar 2018 19:32:55 -0000
@@ -251,6 +251,10 @@ switch*)
M switch$U c 97 $U 600
;;
+example)
+ M example c 98 0 660
+ ;;
+
pvbus*)
M pvbus$U c 95 $U 640
;;
@@ -575,7 +579,7 @@ all)
R lpa2 lpt0 lpt1 lpt2 tty00 tty01 tty02 tty03 tty04 tty05
R tty06 tty07 tty08 tty09 tty0a tty0b ttyc0 ttyc1 ttyc2 ttyc3
R ttyc4 ttyc5 ttyc6 ttyc7 apm pf pctr wd0 wd1 wd2 wd3 std st0
- R st1 fd
+ R st1 fd example
;;
wd*|sd*)
Index: sys/arch/amd64/amd64/conf.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/conf.c,v
retrieving revision 1.60
diff -u -p -u -p -r1.60 conf.c
--- sys/arch/amd64/amd64/conf.c 4 Sep 2016 10:51:23 -0000 1.60
+++ sys/arch/amd64/amd64/conf.c 5 Mar 2018 19:33:14 -0000
@@ -116,6 +116,7 @@ int nblkdev = nitems(bdevsw);
#define mmread mmrw
#define mmwrite mmrw
cdev_decl(mm);
+cdev_decl(example);
cdev_decl(wd);
#include "bio.h"
#include "pty.h"
@@ -297,6 +298,7 @@ struct cdevsw cdevsw[] =
cdev_pvbus_init(NPVBUS,pvbus), /* 95: pvbus(4) control interface */
cdev_ipmi_init(NIPMI,ipmi), /* 96: ipmi */
cdev_switch_init(NSWITCH,switch), /* 97: switch(4) control interface */
+ cdev_example_init(1,example) /* 98: example character device */
};
int nchrdev = nitems(cdevsw);
Index: sys/conf/files
===================================================================
RCS file: /cvs/src/sys/conf/files,v
retrieving revision 1.660
diff -u -p -u -p -r1.660 files
--- sys/conf/files 14 Feb 2018 23:51:49 -0000 1.660
+++ sys/conf/files 5 Mar 2018 19:33:14 -0000
@@ -430,6 +430,9 @@ file dev/ic/malo.c malo
device bwi: ether, ifnet, ifmedia, firmload, wlan
file dev/ic/bwi.c bwi
+# PHE Example Device
+file dev/example.c
+
# Attributes which machine-independent bus support can be attached to.
# These should be defined here, because some of these busses can have
# devices which provide these attributes, and we'd like to avoid hairy
Index: sys/dev/example.c
===================================================================
RCS file: sys/dev/example.c
diff -N sys/dev/example.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sys/dev/example.c 5 Mar 2018 19:33:14 -0000
@@ -0,0 +1,34 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+#include <sys/systm.h>
+
+static char buf[512 + 1];
+
+int
+exampleopen(dev_t dev, int flag, int mode, struct proc *p)
+{
+ memset(&buf, '\0', 513);
+ return 0;
+}
+
+int
+exampleclose(dev_t dev, int flag, int mode, struct proc *p)
+{
+ return 0;
+}
+
+int
+exampleread(dev_t dev, struct uio *uio, int flags)
+{
+ uiomove(&buf, 513, uio);
+ return 0;
+}
+
+int
+examplewrite(dev_t dev, struct uio *uio, int flags)
+{
+ uiomove(&buf, 513, uio);
+ return 0;
+}
Index: sys/sys/conf.h
===================================================================
RCS file: /cvs/src/sys/sys/conf.h,v
retrieving revision 1.144
diff -u -p -u -p -r1.144 conf.h
--- sys/sys/conf.h 13 Jan 2018 13:03:42 -0000 1.144
+++ sys/sys/conf.h 5 Mar 2018 19:33:15 -0000
@@ -475,6 +475,12 @@ extern struct cdevsw cdevsw[];
(dev_type_stop((*))) enodev, 0, (dev_type_poll((*))) enodev, \
(dev_type_mmap((*))) enodev, 0 }
+/* open, close, read, write */
+#define cdev_example_init(c,n) { \
+ dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \
+ dev_init(c,n,write), (dev_type_ioctl((*))) enodev, \
+ (dev_type_stop((*))) enodev, 0, seltrue, (dev_type_mmap((*))) enodev }
+
#endif
/*
view raw example.diff hosted with ❤ by GitHub
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int kernel_fd;
size_t len;
static char buf[512 + 1];
errno = 0;
kernel_fd = open(argv[1], O_RDWR);
if (kernel_fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
len = strlen(argv[2]);
write(kernel_fd, argv[2], len + 1);
printf("Wrote \"%s\" to %s\n", argv[2], argv[1]);
read(kernel_fd, buf, len + 1);
printf("Read \"%s\" from %s\n", buf, argv[1]);
return 0;
}
view raw interface.c hosted with ❤ by GitHub

No comments:

Post a Comment