Safer/better equivalent to ioctl?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Safer/better equivalent to ioctl?

Post by Ethin »

So, I know that Linux (or Unix in general) has the ioctl(2) system call for generic device driver communication to avoid implementing millions of system calls for every kind of device driver in existence. I've never been a fan of ioctl myself; I feel like there should be a better way of approaching this. My idea might be to have an ahcictl, nvmectl, etc., for each kind of disk interface; ethernetctl/ethctl, wifictl for networking; and so on, and then maybe provide a C++ interface that uses function overloading to call the underlying interfaces. But I'm not really sure, so thought I might see what you guys think about just a generally better design other than ioctl().
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: Safer/better equivalent to ioctl?

Post by nullplan »

Alternatively, pseudo file systems. ioctl()s are about sending data to the kernel or retrieving data from the kernel. Both of these can be done with a pseudo-file. That gets around the issue of allocating tons of system call numbers for tons of niche interfaces. It also gets around some ridiculous ioctl()s, like the ones for setting IP addresses. You allocate some socket and use ioctl() on it. But there is never a need for any socket, you just need it as basis for the ioctl(). So maybe "echo 192.168.0.1/24>/net/if/eth0/ipv4/addr" is simpler than using that weirdness, or using "netlink", which Linux came up with. Don't get me wrong, netlink is great at giving you info you could not get otherwise, but for ad-hoc initialization like above IP address setting it is a bit overkill.
Carpe diem!
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Safer/better equivalent to ioctl?

Post by Ethin »

That's... Actually a really good idea. So for NVMe controllers for example (following the FHS, you might have something like: /dev/nvme/nvme0/regs/cap/cmbs, which would access the CMBS bit of the CAP register. (Yeah, I know, devfs isn't commonly used like that -- that's more for /sys -- but it would make sense because /dev is for devices and device information.) Or, for the RTL 8139 you might have /dev/net/rtl0/mac for the MAC address.
Korona
Member
Member
Posts: 1000
Joined: Thu May 17, 2007 1:27 pm
Contact:

Re: Safer/better equivalent to ioctl?

Post by Korona »

I am not sure if pseudo file systems really solve all use cases for ioctls. Pseudo FSes are great for non-performance critical stuff, but ioctls are also used for stuff like zero-copy I/O, and multiplexing that over write() is probably not a good idea. In particular, interpreting part of the written message as a pointer and dereferencing that in the kernel is probably a very bad idea, both for security reasons (e.g., you could trick a program to write to such a file to inject shellcode or extract data) and also because it restricts the implementation (e.g., it will break when used through a pipe to a different program).

You could always replace ioctls by native syscalls but then you need an efficient mechanism to dispatch these calls (and a simple lookup table won't work anymore). You could take an approach inspired by object-oriented programming. For example, you could group ioctls into "interfaces", have a syscall to request an interface handle for a pair of (fd, interface name), and then dispatch the ioctls as "syscalls" through the interface handle (i.e., on the kernel side, each interface would have its own syscall table).
managarm: Microkernel-based OS capable of running a Wayland desktop (Discord: https://discord.gg/7WB6Ur3). My OS-dev projects: [mlibc: Portable C library for managarm, qword, Linux, Sigma, ...] [LAI: AML interpreter] [xbstrap: Build system for OS distributions].
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Safer/better equivalent to ioctl?

Post by Ethin »

Korona wrote:I am not sure if pseudo file systems really solve all use cases for ioctls. Pseudo FSes are great for non-performance critical stuff, but ioctls are also used for stuff like zero-copy I/O, and multiplexing that over write() is probably not a good idea. In particular, interpreting part of the written message as a pointer and dereferencing that in the kernel is probably a very bad idea, both for security reasons (e.g., you could trick a program to write to such a file to inject shellcode or extract data) and also because it restricts the implementation (e.g., it will break when used through a pipe to a different program).

You could always replace ioctls by native syscalls but then you need an efficient mechanism to dispatch these calls (and a simple lookup table won't work anymore). You could take an approach inspired by object-oriented programming. For example, you could group ioctls into "interfaces", have a syscall to request an interface handle for a pair of (fd, interface name), and then dispatch the ioctls as "syscalls" through the interface handle (i.e., on the kernel side, each interface would have its own syscall table).
I'm confused. Are you saying that the syscall handler for instance would require something like an interface ID and then the syscall number, followed by arguments? Something like what UEFI does with its protocol handles?
Korona
Member
Member
Posts: 1000
Joined: Thu May 17, 2007 1:27 pm
Contact:

Re: Safer/better equivalent to ioctl?

Post by Korona »

Yes (that would be one possibility).
managarm: Microkernel-based OS capable of running a Wayland desktop (Discord: https://discord.gg/7WB6Ur3). My OS-dev projects: [mlibc: Portable C library for managarm, qword, Linux, Sigma, ...] [LAI: AML interpreter] [xbstrap: Build system for OS distributions].
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: Safer/better equivalent to ioctl?

Post by nullplan »

Korona wrote:You could always replace ioctls by native syscalls but then you need an efficient mechanism to dispatch these calls (and a simple lookup table won't work anymore). You could take an approach inspired by object-oriented programming. For example, you could group ioctls into "interfaces", have a syscall to request an interface handle for a pair of (fd, interface name), and then dispatch the ioctls as "syscalls" through the interface handle (i.e., on the kernel side, each interface would have its own syscall table).
You know, I hadn't though of that. That sounds like a good idea as well, particularly for those ioctl()s that actually do use the FD, e.g. TCSETATTR. I've been searching for some way to make terminal attributes into a pseudo-file that applications can still find reliably. The problem is that in general, finding the file name for a file descriptor can be pretty hard. Even if you have /proc/self/fd in your OS, readlink() is a messy interface. The code I came up with to read an arbitrary link requires malloc() in a loop. And the kernel definitely knows the terminal attached to the file descriptor directly. So this all seemed a bit pointless. Also, suddenly your applications all need to know about the different types of terminal device in your OS. And you have more dependencies on the VFS being configured in a particular way.

So having these interfaces and request them directly on the FD seems like a good idea for those types of ioctl()s. Also sound data. The old OSS would work by letting you open /dev/dsp and writing your sound samples to the device. Problem 1: What if you have multiple sound cards? OK, make /dev/dsp0, /dev/dsp1, ... and /dev/dsp is a symlink to your default device. Problem 2: How to configure the card? ioctl(). *sigh*. And then ALSA came along and said "ioctl() makes you sigh? Well, here's loads more of them!" And now there are ioctl()s for everything. You don't even write your samples to the device, you transfer them by ioctl(). So that seems like a thing I could quarantine away with those interfaces.
Carpe diem!
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Safer/better equivalent to ioctl?

Post by Ethin »

For sound especially, I was thinking of something like /dev/dspx/samples, which you'd write to. Every time you did the driver would update any metadata it needs to pass to the sound controller about the samples being processed -- things like position at any rate. Then you'd have somethign like /dev/dspx/sampleinfo, which would include general info about the samples being written, e.g.: sample rate, number of channels, interleaving status and such. Then you'd have /dev/dspx/cmd, which would allow you to tell the driver when to actually begin processing the samples. That would make it easier to write streams in bulk.
User avatar
AndrewAPrice
Member
Member
Posts: 2300
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: Safer/better equivalent to ioctl?

Post by AndrewAPrice »

Do you want to keep the Unix philosophy of making everything a file?
My OS is Perception.
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Safer/better equivalent to ioctl?

Post by Ethin »

Not really. I think that descriptors for disks, files, etc., should be separate and there shouldn't be a universal "handle" for everything (that makes type checking kinda difficult). I also feel like devices/sockets/etc. shouldn't be treated as a part of the file system because they really, really are independent entities with pretty much nothing in common with each other other than being able to read from and write to them.
User avatar
AndrewAPrice
Member
Member
Posts: 2300
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: Safer/better equivalent to ioctl?

Post by AndrewAPrice »

I went a different route in my OS and made a system where programs could implement and discover named services. The names are fully qualified, e.g. "perception.devices.StorageDevice".

Anyone who wants to build a storage device driver can implement and register the StorageDevice interface, and my VFS will be monitoring for when a new StorageDevice service is registered.

My graphics driver implements "perception.devices.GraphicsDevice", my window manager implements "perception.WindowManager", etc.

I came up with my own interface descriptor language, that lists methods and the request/response types, and it generates a C++ stub for calling and implementing the methods.

See: https://wiki.osdev.org/Remote_Procedure_Call
Inspiration: https://grpc.io/ https://thrift.apache.org/
What my IDL looks like: https://github.com/AndrewAPrice/Percept ... n/permebuf
My OS is Perception.
andrew_w
Posts: 19
Joined: Wed May 07, 2008 5:06 am

Re: Safer/better equivalent to ioctl?

Post by andrew_w »

Ethin wrote:Not really. I think that descriptors for disks, files, etc., should be separate and there shouldn't be a universal "handle" for everything (that makes type checking kinda difficult). I also feel like devices/sockets/etc. shouldn't be treated as a part of the file system because they really, really are independent entities with pretty much nothing in common with each other other than being able to read from and write to them.
If you do that, you lose the device-independent I/O that is relatively common in Unix. For instance, it's very common to have programs that can do I/O to either a terminal or a disk file without having to be aware of any differences between the two, and if you make those completely separate, then every program has to be specifically aware of every possible device type, which I would say is a big step backwards. It also makes security a lot more complex if you have separate primitives for each device because each has to have its own separate security layer (which already makes security way more complicated than it needs to be on conventional Unices with their multiple non-file-based primitives), whereas in an OS with a purely file-oriented architecture you basically only have to have one relatively simple security layer for the entire system. In addition, file-oriented architecture with per-process namespaces makes containerization and multiple instances of system services a lot easier.

UX/RT, the OS I'm working on, will basically do the opposite of what you're proposing and take "everything is a file" as far as it can be taken (anonymous memory will be replaced with files in a tmpfs, process/thread creation will be done through procfs, and even most filesystem APIs themselves other than reads/writes will go through an RPC connection over a file descriptor). Any service that an OS implements can be built on top of receive/send or read/write primitives (in a typical microkernel OS everything actually is), so why overcomplicate things by having more primitives than you need? It's always possible to provide interface libraries on top of the file-based interfaces for services where it isn't convenient to use the file-based interface directly (UX/RT will do this for most of its APIs).
Developer of UX/RT, a QNX/Plan 9-like OS
User avatar
AndrewAPrice
Member
Member
Posts: 2300
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: Safer/better equivalent to ioctl?

Post by AndrewAPrice »

andrew_w wrote:If you do that, you lose the device-independent I/O that is relatively common in Unix. For instance, it's very common to have programs that can do I/O to either a terminal or a disk file without having to be aware of any differences between the two, and if you make those completely separate, then every program has to be specifically aware of every possible device type, which I would say is a big step backwards. It also makes security a lot more complex if you have separate primitives for each device because each has to have its own separate security layer (which already makes security way more complicated than it needs to be on conventional Unices with their multiple non-file-based primitives), whereas in an OS with a purely file-oriented architecture you basically only have to have one relatively simple security layer for the entire system. In addition, file-oriented architecture with per-process namespaces makes containerization and multiple instances of system services a lot easier.
You get piping (piping one program's stdout to another's stdin or a file, which should be the job of the shell), but I can't really think of a concrete example where two totally different classes of devices would pipe ioctls with the other understanding it?
andrew_w wrote:then every program has to be specifically aware of every possible device type
Does every program need to touch every device, other than your device manager that can scan and load drivers?

If you use an IDL to define an interface per device class, and you had a legitimate need to pretend your joystick is an Ethernet card, you can create a wrapper program that implements a virtual Ethernet card and transforms the command to joystick commands.

So as a concrete example, let's say you had a program that distorted your voice and you wanted to pipe your microphone to it, then pipe the outputs to your speaker, you could do:

Code: Select all

voice_transformer < /device/microphone > /devices/speaker
Whereas, I'm proposing it's not too much of an ask to build a util file called "print_microphone" and "play_stdin", put it on the package manager/app store, and now you can do:

Code: Select all

print_microphone --depth=24 --rate=96 | voice_transformer | play_stdin --depth=24 --rate=96
I'm not proposing it's better than "everything is a file", but the fun thing about OS Dev is if you're not interested in making a Unix clone you're free to explore whatever system you'd like.
Last edited by AndrewAPrice on Mon Mar 22, 2021 10:56 am, edited 1 time in total.
My OS is Perception.
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: Safer/better equivalent to ioctl?

Post by nexos »

In my OS, I am going to make an object manager. Drivers will get registered in the object manager. They can register functions for different tasks. For example, a driver would be registered. It would then make I/O control handlers in the object dispatch table. An app would grab the table, read the message numbers that correspond to a specific control (as my OS will be microkernel based) and then send a message with the proper parameters. I don't plan on making a strict Unix clone, rather something kind of Unix like.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Safer/better equivalent to ioctl?

Post by rdos »

My view is that ioctl is a completely useless interface, and by not implementing it you can stop device driver manufacturers and kernel gurus from modifying their devices or the kernel with undocumented APIs. If a function is important to support, you provide API functions for it, and if it simply is something fancy a hw manufacturer wants to push, then they can either configure it with environmental variables or do some automatic configuration. Of course, unless the OS team thinks it is something useful that more than one device supports, and then provide a new API for it.
Post Reply