Search code examples
linux-kernelconsoledriverboot

Booting linux kernel using a simple char driver as console?


I'm trying to boot the linux kernel (v3.16.1) on a simulation model of a Sparc v8 processor for an academic project.

The simulation model consists of a cpu, memory, timer and a simple polling based output device. We've modified the kernel so that a bootloader is not necessary. We directly put the kernel image in memory, set up some necessary variables and jump into kernel code. We have a rudimentary polling based output-device, and we've been able to direct output of printk to this device.

The kernel boots all the way up to the start of "/init". After this point no output is visible. Just before this point, there's a warning displayed : "Warning: unable to open an initial console." My filesystem image seems to be fine, and contains a /dev/console node (I checked this with Qemu).

My understanding is that while printk works fine (using an early console), user processes need a device node with a proper device driver to be set up. Printk works fine, so is there a way to see all writes by user processes to the console via printk ? There's an existing driver called "ttyprintk" which sends all writes to printk. I enabled it and tried using it by passing "console=ttyprintk" kernel argument, but this gives the same warning. The kernel is not able to open "/dev/console" for writing.

My questions are :

  1. Can I write a simple character device driver and use it as my console ? Inside this driver I plan to send all writes to printk. Is this possible ?

  2. How can I ask the kernel to use this as my console ? Would kernel argument "console = /dev/MyDriver" work ?

  3. Is there a simpler way to have /init and other user processes use my rudimentary output device as a console ?

4.Is there some other reason that could be causing the "Warning: unable to open an initial console." message ?

Thanks for any hints. I am new to kernel programming.

-neha


Solution

  • They way the /dev/console driver works is to attach to some other tty device (or devices!), which is/are then used for the console. The sysfs attribute active of the console device (try /sys/class/tty/console/active) will tell you what device the console is attached to at the moment.

    The kernel also tends to log console changes:

    [    0.186989] dw-apb-uart ffc02000.serial0: ttyS0 at MMIO 0xffc02000 (irq = 194, base_baud = 6250000) is a 16550A
    [    0.755529] console [ttyS0] enabled
    

    In the above log, once the serial port device was created the kernel decided to use it as a console. This refers to the binding of the device to the driver in the kernel, not the device node in /dev. The latter does not matter here. Also understand that the attachment of the console device to a tty happens in the kernel. /dev/console is not a symlink to another device node.

    The kernel has chosen ttyS0 because I told it to via the kernel command line, console=ttyS0,115200n8. Without a console argument, the kernel uses the first console that registers with register_console().

    So the question here is how can one get /dev/ttyprintk to be attached to /dev/console. And the answer appears to be you can't.

    A work around might be to create a custom initramfs that changes the /dev/console device node from major 5 minor 1 to use minor 3, thus changing it into /dev/ttyprintk. Or symlink to achieve the same thing. This should get init to use ttyprintk as its stdin/stdout/stderr.

    In your example, writing a tty/console driver for your output device would be the right way. Make it the console and then the kernel sends printk there.