Dynamic major number allocation - how to get it in ioctl application? - linux-device-driver

I want to follow best-practices and use dynamic major number allocation for my driver when registering kernel module.
I can output this number with printk and see it with dmesg. But is it possible to retrieve such number using ioctl application, that is used to talk with driver?
Or perhaphs it is possible to write some shell script, that will perform insmod, mknod & also retrieve given major number?

Usually by reading /proc/devices we can get dynamically allocated Major number.
awk '$2=="misc" {print $1}' /proc/devices
Here "misc" is the driver name.
There is no harm in writing a extra function which returns major number.

I'm assuming you have a static /dev directory, otherwise you could just let the device files be created dynamically with the correct major and minor device numbers and be done with it.
If you have sysfs mounted on /sys, you could add a read-only module parameter to indicate the major device number registered to your devices. (If you load your driver as a module, you could also use the same module parameter to set a specific major device number when loading the module, defaulting to a dynamically allocated major device number. To add such a module parameter to your driver, you can do something like this:
#define MYDRIVER_MAJOR_DEF 0 /* Default major is dynamic */
unsigned int major = MYDRIVER_MAJOR_DEF;
module_param(major, uint, 0444);
MODULE_PARM_DESC(major,
"Major device number; set to 0 to assign dynamically;"
" (default="__MODULE_STRING(MYDRIVER_MAJOR_DEF)")");
Then use the value of major when registering your devices with register_chrdev or whatever. After registering your devices, if major was 0, set it to the actual, dynamically allocated major device number so that you can read it back via the sysfs module parameter file.
From user-space, the module parameter file will be something like /sys/module/mydriver/parameters/major and will contain the major device number as a string of decimal characters. You can read that in a script and use it with the mknod program to create the device files.
It's easier to use a dynamic /dev though!

You need to use the function alloc_chrdev_region.
See https://www.kernel.org/doc/htmldocs/kernel-api/API-alloc-chrdev-region.html

Related

How do I add a missing peripheral register to a STM32 MCU model in Renode?

I am trying out this MCU / SoC emulator, Renode.
I loaded their existing model template under platforms/cpus/stm32l072.repl, which just includes the repl file for stm32l071 and adds one little thing.
When I then load & run a program binary built with STM32CubeIDE and ST's LL library, and the code hits the initial function of SystemClock_Config(), where the Flash:ACR register is being probed in a loop, to observe an expected change in value, it gets stuck there, as the Renode Monitor window is outputting:
[WARNING] sysbus: Read from an unimplemented register Flash:ACR (0x40022000), returning a value from SVD: 0x0
This seems to be expected, not all existing templates model nearly everything out of the box. I also found that the stm32L071 model is missing some of the USARTs and NVIC channels. I saw how, probably, the latter might be added, but there seems to be not a single among the default models defining that Flash:ACR register that I could use as example.
How would one add such a missing register for this particular MCU model?
Note1: For this test, I'm using a STM32 firmware binary which works as intended on actual hardware, e.g. a devboard for this MCU.
Note2:
The stated advantage of Renode over QEMU, which does apparently not emulate peripherals, is also allowing to stick together a more complex system, out of mocked external e.g. I2C and other devices (apparently C# modules, not yet looked into it).
They say "use the same binary as on the real system".
Which is my reason for trying this out - sounds like a lot of potential for implementing systems where the hardware is not yet fully available, and also automatted testing.
So the obvious thing, commenting out a lot of parts in init code, to only test some hardware-independent code while sidestepping such issues, would defeat the purpose here.
If you want to just provide the ACR register for the flash to pass your init, use a tag.
You can either provide it via REPL (recommended, like here https://github.com/renode/renode/blob/master/platforms/cpus/stm32l071.repl#L175) or via RESC.
Assuming that your software would like to read value 0xDEADBEEF. In the repl you'd use:
sysbus:
init:
Tag <0x40022000, 0x40022003> "ACR" 0xDEADBEEF
In the resc or in the Monitor it would be just:
sysbus Tag <0x40022000, 0x40022003> "ACR" 0xDEADBEEF
If you want more complex logic, you can use a Python peripheral, as described in the docs (https://renode.readthedocs.io/en/latest/basic/using-python.html#python-peripherals-in-a-platform-description):
flash: Python.PythonPeripheral # sysbus 0x40022000
size: 0x1000
initable: false
filename: "script_with_complex_python_logic.py"
```
If you really need advanced implementation, then you need to create a complete C# model.
As you correctly mentioned, we do not want you to modify your binary. But we're ok with mocking some parts we're not interested in for a particular use case if the software passes with these mocks.
Disclaimer: I'm one of the Renode developers.

ebpf-tc: how to keep unique information inside a ebpf instance when same program is attached to multiple interface

When we pin a MAP, we can able to share information from userspace to ebpf but it is system wide.
But if i want to share different value to different instance from tc ingress/egress (array map with size of 1)
Is there any way to pass argument ?
Map (unpinned unique per instance) - update from userspace
Any other way to communicate from userspace to kernel (while attaching or after)
Really appreciate your help.
Pinning a map doesn't make it system wide. Every map is always accessible system wide, pinning just adds a reference to the file system to make it easier to find and to make sure a map isn't removed even when not in use by any program.
Is there any way to pass argument ?
No, once a program is loaded, only the kernel can pass arguments(contexts) to a program, userspace can only use maps to communicate with eBPF programs.
Map (unpinned unique per instance) - update from userspace
Any userspace program with the right permissions can update any map as long as you can obtain a file descriptor to the map. Map FDs can be obtained:
By creating a new map
By opening a map pin
By using its unique ID (directly or by looping over all loaded maps)
By IPC from another process that already has the FD
Any other way to communicate from userspace to kernel (while attaching or after)
Maps are it. You can rewrite the program before loading it into the kernel by setting constants at specific locations, but not at attach time. One way which might be interesting is that on newer kernels, global data is supported. Which allow you to change the value of variables defined in the global scope from userspace. In this case the global data is packed in a array map with a single key/value.

Passing kernel params from U-Boot to ARM Linux when device tree is used

I've been investigating a change to some embedded software inasmuch as we want U-Boot to be able to pass specific command-line parameters to the kernel, ones that aren't necessarily known in advance.
This is so that the kernel can tell which U-Boot partition it was started by (we have two copies, one in /dev/mmc3boot0 and /dev/mmc3boot1, and they both share a single (redundant) environment space, so we can't use that alone to uniquely identify the instance).
One thought was to simply have each U-Boot write its ID to the shared environment when it boots but that has the downside that there are variants out there that do not currently do this. So, if we boot from one that does, it will write its ID and, if we then boot from one that doesn't, it will not change the ID back to blank, leading to incorrect information if we rely on that.
This is why we thought to use a kernel parameter - since older style U-Boot instances never supply an ID, we know it's running in boot0. The newer style would provide their actual ID so we could then search the two boot partitions to see which one it was in.
To that end, I've modified U-Boot so that it sets up ATAGs to pass through the extra parameter needed. Specifically:
I've define CONFIG_SYS_BOOT_GET_CMDLINE in arch\arm\include\asm\config.h so that boot_get_cmdline() is called.
I've modified that boot_get_cmdline() function so that it sets up a specific parameter before appending the normal parameters. In other words, rather than just plugh=xyzzy, we now get uboot_instance=42 plugh=xyzzy.
This all compiles fine and U-Boot successfully starts the kernel but the extra information is not being reflected in the Linux kernel, which has its kernel parameters set to the regular plugh=xyzzy.
On further research, it appears that we are falling afoul of the two possible ways to invoke the kernel. One of these is with ATAGs and one is with a flattened device tree (FDT), and they appear to be mutually exclusive (the kernel startup code selects one or the other based on signatures passed in with the pointer referencing either the ATAGs or FDT structure).
So my question is this. Given that the device tree is meant to be a fixed structure for the device you're describing, how do you pass arbitrary kernel command line parameters (calculated at runtime) when the bootloader is invoking the kernel?
You can use a dummy environment variable for your platform in include/configs/<board>.h.
For example, assume you have the following (simplified) UBoot environment variables for booting:
bootcmd=run mmcargs
run loadimage loadfdt
bootz ${loadaddr} - ${fdt_addr}
mmcargs=setenv bootargs blah=blah
This uses mmcargs to set up the kernel command line to use. What we need to do is to insert that dummy environment variable in a way that current UBoot instances supply nothing and new ones supply the actual ID. This is done simply with the following change:
mmcargs=setenv bootargs ${uboot_id_stanza} blah=blah
Then, during the initialization of the board, you can set this variable using env_set API, specifically by writing your own custom board_late_init of the board init code in board/<vendor>/<init_code>.c.
The following line should be placed at the end of the board_late_init function:
setenv("uboot_id_stanza", "uboot_id=<uniqueId>");
That way, the uboot_id variable setting is added to the kernel command line but, since you didn't do a saveenv, it doesn't become persistent. Every UBoot instance will set the correct ID (including old ones that don't set an ID).

Device Drivers vs the /dev + glibc Interface

I am looking to have the processor read from I2C and store the data in DDR in an embedded system. As I have been looking at solutions, I have been introduced to Linux device drivers as well as the GNU C Library. It seems like for many operations you can perform with the basic Linux drivers you can also perform with basic glibc system calls. I am somewhat confused when one should be used over the other. Both interfaces can be accessed from the user space.
When should I use a kernel driver to access a device like I2C or USB and when should I use the GNU C Library system functions?
The GNU C Library forwards function calls such as read, write, ioctl directly to the kernel. These functions are just very thin wrappers around the system calls. You could call the kernel all by yourself using inline assembly, but that is rarely helpful. So in this sense, all interactions with the kernel driver will go through these glibc functions.
If you have questions about specific interfaces and their trade-offs, you need to name them explicitly.
In ARM:
Privilege states are built into the processor and are changed via assembly commands. A memory protection unit, a part of the chip, is configured to disallow access to arbitrary ranges of memory depending on the privilege status.
In the case of the Linux kernel, ALL physical memory is privileged- memory addresses in userspace are virtual (fake) addresses, translated to real addresses once in privileged mode.
So, to access a privileged memory range, the mechanics are like a function call- you set the parameters indicating what you want, and then make a ('SVC')- an interrupt function which removes control of the program from userspace, gives it to the kernel. The kernel looks at your parameters and does what you need.
The standard library basically makes that whole process easier.
Drivers create interfaces to physical memory addresses and provide an API through the SVC call and whatever 'arguments' it's passed.
If physical memory is not reserved by a driver, the kernel generally won't allow anyone to access it.
Accessing physical memory you're not privileged to will cause a "bus error".
BTW: You can use a driver like UIO to put physical memory into userspace.

Why are there ioctl calls in socket.c?

Trying to understand why there are ioctl calls in socket.c ? I can see a modified kernel that I am using, it has some ioctl calls which load in the required modules when the calls are made.
I was wondering why these calls ended up in socket.c ? Isn't socket kind of not-a-device and ioctls are primarily used for device.
Talking about 2.6.32.0 heavily modified kernel here.
ioctl suffers from its historic name. While originally developed to perform i/o controls on devices, it has a generic enough construct that it may be used for arbitrary service requests to the kernel in context of a file descriptor. A file descriptor is an opaque value (just an int) provided by the kernel that can be associated with anything.
Now if you treat a file descriptor and think of things as files, which most *nix constructs do, open/read/write/close isn't enough. What if you want to label a file (rename)? what if you want to wait for a file to become available (ioctl)? what if you want to terminate everything if a file closes (termios)? all the "meta" operations that don't make sense in the core read/write context are lumped under ioctls; fctls; etc. unless they are so frequently used that they deserve their own system call (e.g. flock(2) functionality in BSD4.2)