Discover millions of ebooks, audiobooks, and so much more with a free trial

From $11.99/month after trial. Cancel anytime.

Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization: Create user-kernel interfaces, work with peripheral I/O, and handle hardware interrupts
Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization: Create user-kernel interfaces, work with peripheral I/O, and handle hardware interrupts
Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization: Create user-kernel interfaces, work with peripheral I/O, and handle hardware interrupts
Ebook787 pages6 hours

Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization: Create user-kernel interfaces, work with peripheral I/O, and handle hardware interrupts

Rating: 0 out of 5 stars

()

Read preview

About this ebook

Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization is an ideal companion guide to the Linux Kernel Programming book. This book provides a comprehensive introduction for those new to Linux device driver development and will have you up and running with writing misc class character device driver code (on the 5.4 LTS Linux kernel) in next to no time.
You'll begin by learning how to write a simple and complete misc class character driver before interfacing your driver with user-mode processes via procfs, sysfs, debugfs, netlink sockets, and ioctl. You'll then find out how to work with hardware I/O memory. The book covers working with hardware interrupts in depth and helps you understand interrupt request (IRQ) allocation, threaded IRQ handlers, tasklets, and softirqs. You'll also explore the practical usage of useful kernel mechanisms, setting up delays, timers, kernel threads, and workqueues. Finally, you'll discover how to deal with the complexity of kernel synchronization with locking technologies (mutexes, spinlocks, and atomic/refcount operators), including more advanced topics such as cache effects, a primer on lock-free techniques, deadlock avoidance (with lockdep), and kernel lock debugging techniques.
By the end of this Linux kernel book, you'll have learned the fundamentals of writing Linux character device driver code for real-world projects and products.

LanguageEnglish
Release dateMar 19, 2021
ISBN9781801070829
Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization: Create user-kernel interfaces, work with peripheral I/O, and handle hardware interrupts

Read more from Kaiwan N. Billimoria

Related to Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization

Related ebooks

System Administration For You

View More

Reviews for Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization

Rating: 0 out of 5 stars
0 ratings

0 ratings0 reviews

What did you think?

Tap to rate

Review must be at least 10 words

    Book preview

    Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization - Kaiwan N. Billimoria

    Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization

    Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization

    Create user-kernel interfaces, work with peripheral I/O, and handle hardware interrupts

    Kaiwan N Billimoria

    BIRMINGHAM - MUMBAI

    Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization

    Copyright © 2021 Packt Publishing

    All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

    Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

    Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

    Group Product Manager: Wilson D'souza

    Publishing Product Manager: Vijin Boricha

    Content Development Editor: Romy Dias

    Senior Editor: Rahul D'souza

    Technical Editor: Shruthi Shetty

    Copy Editor: Safis Editing

    Project Coordinator: Neil Dmello

    Proofreader: Safis Editing

    Indexer: Pratik Shirodkar

    Production Designer: Shankar Kalbhor

    First published: March 2021

    Production reference: 1190321

    Published by Packt Publishing Ltd.

    Livery Place

    35 Livery Street

    Birmingham

    B3 2PB, UK.

    ISBN 978-1-80107-951-8

    www.packt.com

    First, to my dear parents, Diana and Nadir Nads, for showing me how to live a happy and productive life. To my dear wife, Dilshad (an accomplished financial advisor herself), and our amazing kids, Sheroy and Danesh – thanks for all your love and patience.

    – Kaiwan N Billimoria

    Packt.com

    Subscribe to our online digital library for full access to over 7,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website.

    Why subscribe?

    Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals

    Improve your learning with Skill Plans built especially for you

    Get a free eBook or video every month

    Fully searchable for easy access to vital information

    Copy and paste, print, and bookmark content

    Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.packt.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.

    At www.packt.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks. 

    Contributors

    About the author

    Kaiwan N Billimoria taught himself BASIC programming on his dad's IBM PC back in 1983. He was programming in C and Assembly on DOS until he discovered the joys of Unix, and by around 1997, Linux!

    Kaiwan has worked on many aspects of the Linux system programming stack, including Bash scripting, system programming in C, kernel internals, device drivers, and embedded Linux work. He has actively worked on several commercial/FOSS projects. His contributions include drivers to the mainline Linux OS and many smaller projects hosted on GitHub. His Linux passion feeds well into his passion for teaching these topics to engineers, which he has done for well over two decades now. He's also the author of Hands-On System Programming with Linux. It doesn't hurt that he is a recreational ultrarunner too.

    Writing this book took a long while; I'd like to thank the team from Packt for their patience and skill! Carlton Borges, Romy Dias, Vijin Boricha, Rohit Rajkumar, Vivek Anantharaman, Nithin Varghese, Hemangi Lotlikar, and all the others. It was indeed a pleasure working with you.

    I owe a debt of gratitude to the very able technical reviewers – Donald Donnie Tevault and Anil Kumar. They caught a lot of my mistakes and omissions and greatly helped make this book better.

    About the reviewers

    Donald A. Tevault, but you can call him Donnie, got involved with Linux way back in 2006 and has been working with it ever since. He holds the Linux Professional Institute Level 3 Security certification, and the GIAC Incident Handler certification. Donnie is a professional Linux trainer, and thanks to the magic of the internet, teaches Linux classes literally the world over from the comfort of his living room. He's also a Linux security researcher for an IoT security company.

    Anil Kumar is a Linux BSP and firmware developer at Intel. He has over 12 years of software development experience across many verticals, including IoT, mobile chipsets, laptops/Chromebooks, media encoders, and transcoders. He has a master's degree in electronics design from the Indian Institute of Science and a bachelor's degree in electronics and communication from BMS College of Engineering, India. He is an electronics enthusiast and blogger and loves tinkering to create fun DIY projects.

    Table of Contents

    Title Page

    Copyright and Credits

    Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization

    Dedication

    About Packt

    Why subscribe?

    Contributors

    About the author

    About the reviewers

    Preface

    Who this book is for

    What this book covers

    To get the most out of this book

    Download the example code files

    Download the color images

    Conventions used

    Get in touch

    Reviews

    Section 1: Character Device Driver Basics

    Writing a Simple misc Character Device Driver

    Technical requirements

    Getting started with writing a simple misc character device driver

    Understanding the device basics

    A quick note on the Linux Device Model

    Writing the misc driver code – part 1

    Understanding the connection between the process, the driver, and the kernel

    Handling unsupported methods

    Writing the misc driver code – part 2

    Writing the misc driver code – part 3

    Testing our simple misc driver

    Copying data from kernel to user space and vice versa

    Leveraging kernel APIs to perform the data transfer

    A misc driver with a secret

    Writing the 'secret' misc device driver's code

    Our secret driver – the init code

    Our secret driver – the read method

    Our secret driver – the write method

    Our secret driver – cleanup

    Our secret driver – the user space test app

    Issues and security concerns

    Hacking the secret driver

    Bad driver – buggy read()

    Bad driver – buggy write() – a privesc!

    User space test app modifications

    Device driver modifications

    Let's get root now

    Summary

    Questions

    Further reading

    User-Kernel Communication Pathways

    Technical requirements

    Approaches to communicating/interfacing a kernel driver with a user space C app

    Interfacing via the proc filesystem (procfs)

    Understanding the proc filesystem

    Directories under /proc

    The purpose behind the proc filesystem

    procfs is off-bounds to driver authors

    Using procfs to interface with the user space

    Basic procfs APIs

    The four procfs files we will create

    Trying out the dynamic debug_level procfs control

    Dynamically controlling debug_level via procfs

    A few misc procfs APIs

    Interfacing via the sys filesystem (sysfs)

    Creating a sysfs (pseudo) file in code

    Creating a simple platform device

    Platform devices

    Tying it all together – setting up the device attributes and creating the sysfs file

    The code for implementing our sysfs file and its callbacks

    The one value per sysfs file rule

    Interfacing via the debug filesystem (debugfs)

    Checking for the presence of debugfs

    Looking up the debugfs API documentation

    An interfacing example with debugfs

    Creating and using the first debugfs file

    Creating and using the second debugfs file

    Helper debugfs APIs for working on numeric globals

    Removing the debugfs pseudo file(s)

    Seeing a kernel bug – an Oops!

    Debugfs – actual users

    Interfacing via netlink sockets

    Advantages using sockets

    Understanding what a netlink socket is

    Writing the user space netlink socket application

    Writing the kernel-space netlink socket code as a kernel module

    Trying out our netlink interfacing project

    Interfacing via the ioctl system call

    Using ioctl in the user and kernel space

    User space – using the ioctl system call

    Kernel space – using the ioctl system call

    ioctl as a debug interface

    Comparing the interfacing methods – a table

    Summary

    Questions

    Further reading

    Working with Hardware I/O Memory

    Technical requirements

    Accessing hardware I/O memory from the kernel

    Understanding the issue with direct access

    The solution – mapping via I/O memory or I/O port

    Asking the kernel's permission

    Understanding and using memory-mapped I/O

    Using the ioremap*() APIs

    The newer breed – the devm_* managed APIs

    Obtaining the device resources

    All in one with the devm_ioremap_resource() API

    Looking up the new mapping via /proc/iomem

    MMIO – performing the actual I/O

    Performing 1- to 8-byte reads and writes on MMIO memory regions

    Performing repeating I/O on MMIO memory regions

    Setting and copying on MMIO memory regions

    Understanding and using port-mapped I/O

    PMIO – performing the actual I/O

    A PIO example – the i8042

    Looking up the port(s) via /proc/ioports

    Port I/O – a few remaining points to note

    Summary

    Questions

    Further reading

    Handling Hardware Interrupts

    Technical requirements

    Hardware interrupts and how the kernel handles them

    Allocating the hardware IRQ

    Allocating your interrupt handler with request_irq()

    Freeing the IRQ line

    Setting interrupt flags

    Understanding level- and edge-triggered interrupts – a brief note

    Code view 1 – the IXGB network driver

    Implementing the interrupt handler routine

    Interrupt context guidelines – what to do and what not to do

    Don't block – spotting possibly blocking code paths

    Interrupt masking – the defaults and controlling it

    Keep it fast

    Writing the interrupt handler routine itself

    Code view 2 – the i8042 driver's interrupt handler

    Code view 3 – the IXGB network driver's interrupt handler

    IRQ allocation – the modern way – the managed interrupt facility

    Working with the threaded interrupts model

    Employing the threaded interrupt model – the API

    Employing the managed threaded interrupt model – the recommended way

    Code view 4 – the STM32 F7 microcontroller's threaded interrupt handler

    Internally implementing the threaded interrupt

    Why use threaded interrupts?

    Threaded interrupts – to really make it real time

    Constraints when using a threaded handler

    Working with either hardirq or threaded handlers

    Enabling and disabling IRQs

    The NMI

    Viewing all allocated interrupt (IRQ) lines

    Understanding and using top and bottom halves

    Specifying and using a tasklet

    Initializing the tasklet

    Running the tasklet

    Understanding the kernel softirq mechanism

    Available softirqs and what they are for

    Understanding how the kernel runs softirqs

    Running tasklets

    Employing the ksoftirqd kernel threads

    Softirqs and concurrency

    Hardirqs, tasklets, and threaded handlers – what to use when

    Fully figuring out the context

    Viewing the context – examples

    How Linux prioritizes activities

    A few remaining FAQs answered

    Load balancing interrupts and IRQ affinity

    Does the kernel maintain separate IRQ stacks?

    Measuring metrics and latency

    Measuring interrupts with [e]BPF

    Measuring time servicing individual hardirqs

    Measuring time servicing individual softirqs

    Using Ftrace to get a handle on system latencies

    Finding the interrupts disabled worst-case time latency with Ftrace

    Other tools

    Summary

    Questions

    Further reading

    Working with Kernel Timers, Threads, and Workqueues

    Technical requirements

    Delaying for a given time in the kernel

    Understanding how to use the *delay() atomic APIs

    Understanding how to use the *sleep() blocking APIs

    Taking timestamps within kernel code

    Let's try it – how long do delays and sleeps really take?

    The sed drivers – to demo kernel timers, kthreads, and workqueues

    Setting up and using kernel timers

    Using kernel timers

    Our simple kernel timer module – code view 1

    Our simple kernel timer module – code view 2

    Our simple kernel timer module – running it

    sed1 – implementing timeouts with our demo sed1 driver

    Deliberately missing the bus

    Creating and working with kernel threads

    A simple demo – creating a kernel thread

    Running the kthread_simple kernel thread demo

    The sed2 driver – design and implementation

    sed2 – the design

    sed2 driver – code implementation

    sed2 – trying it out

    Querying and setting the scheduling policy/priority of a kernel thread

    Using kernel workqueues

    The bare minimum workqueue internals

    Using the kernel-global workqueue

    Initializing the kernel-global workqueue for your task – INIT_WORK()

    Having your work task execute – schedule_work()

    Variations of scheduling your work task

    Cleaning up – canceling or flushing your work task

    A quick summary of the workflow

    Our simple work queue kernel module – code view

    Our simple work queue kernel module – running it

    The sed3 mini project – a very brief look

    Summary

    Questions

    Further reading

    Section 2: Delving Deeper

    Kernel Synchronization - Part 1

    Critical sections, exclusive execution, and atomicity

    What is a critical section?

    A classic case – the global i ++

    Concepts – the lock

    A summary of key points

    Concurrency concerns within the Linux kernel

    Multicore SMP systems and data races

    Preemptible kernels, blocking I/O, and data races

    Hardware interrupts and data races

    Locking guidelines and deadlocks

    Mutex or spinlock? Which to use when

    Determining which lock to use – in theory

    Determining which lock to use – in practice

    Using the mutex lock

    Initializing the mutex lock

    Correctly using the mutex lock

    Mutex lock and unlock APIs and their usage

    Mutex lock – via [un]interruptible sleep?

    Mutex locking – an example driver

    The mutex lock – a few remaining points

    Mutex lock API variants

    The mutex trylock variant

    The mutex interruptible and killable variants

    The mutex io variant

    The semaphore and the mutex

    Priority inversion and the RT-mutex

    Internal design

    Using the spinlock

    Spinlock – simple usage

    Spinlock – an example driver

    Test – sleep in an atomic context

    Testing on a 5.4 debug kernel

    Testing on a 5.4 non-debug distro kernel

    Locking and interrupts

    Using spinlocks – a quick summary

    Summary

    Questions

    Further reading

    Kernel Synchronization - Part 2

    Using the atomic_t and refcount_t interfaces

    The newer refcount_t versus older atomic_t interfaces

    The simpler atomic_t and refcount_t interfaces

    Examples of using refcount_t within the kernel code base

    64-bit atomic integer operators

    Using the RMW atomic operators

    RMW atomic operations – operating on device registers

    Using the RMW bitwise operators

    Using bitwise atomic operators – an example

    Efficiently searching a bitmask

    Using the reader-writer spinlock

    Reader-writer spinlock interfaces

    A word of caution

    The reader-writer semaphore

    Cache effects and false sharing

    Lock-free programming with per-CPU variables

    Per-CPU variables

    Working with per-CPU

    Allocating, initialization, and freeing per-CPU variables

    Performing I/O (reads and writes) on per-CPU variables

    Per-CPU – an example kernel module

    Per-CPU usage within the kernel

    Lock debugging within the kernel

    Configuring a debug kernel for lock debugging

    The lock validator lockdep – catching locking issues early

    Examples – catching deadlock bugs with lockdep

    Example 1 – catching a self deadlock bug with lockdep

    Fixing it

    Example 2 – catching an AB-BA deadlock with lockdep

    lockdep – annotations and issues

    lockdep annotations

    lockdep issues

    Lock statistics

    Viewing lock stats

    Memory barriers – an introduction

    An example of using memory barriers in a device driver

    Summary

    Questions

    Further reading

    Other Books You May Enjoy

    Leave a review - let other readers know what you think

    Preface

    This book has been written with a view to helping you learn the fundamentals of Linux character device driver development in a practical, hands-on fashion, along with the necessary theoretical background to give you a well-rounded view of this vast and interesting topic area. To do the topics justice, that book's scope is deliberately kept limited to (mostly) learning how to write misc class character device drivers on the Linux OS. This way, you will be able to deeply imbibe the fundamental and necessary driver author skills to then be able to tackle different kinds of Linux driver projects with relative ease.

    The focus is on hands-on driver development via the powerful Loadable Kernel Module (LKM) framework; the majority of kernel driver development is done in this manner. The focus is kept on working hands-on with driver code, understanding at a sufficiently deep level the internals wherever required, and keeping security in mind.

    A recommendation we can't make strongly enough: to really learn and understand the details well, it's really best that you first read and understand this book's companion, Linux Kernel Programming. It covers various key areas – building the kernel from source, writing kernel modules via the LKM framework, kernel internals including kernel architecture, the memory system, memory alloc/dealloc APIs, CPU scheduling, and more. The combination of the two books will give you a sure and deep edge.

    This book wastes no time – the first chapter has you learning the details of the Linux driver framework and how to write a simple yet complete misc class character device driver. Next, you learn how to do something very necessary: efficiently interfacing your driver with user space processes using various technologies (some of which help as debug/diagnostic aids as well!). Understanding, and working with, hardware (peripheral chip) I/O memory is then covered. Detailed coverage of handling hardware interrupts follows. This includes learning and using several modern driver techniques – using threaded IRQs, leveraging resource-managed APIs for drivers, I/O resource allocation, and so on. It covers what top/bottom halves are, working with tasklets and softirqs, and measuring interrupt latencies. Kernel mechanisms you will typically work with – using kernel timers, setting up delays, creating and managing kernel threads and workqueues – are covered next.

    The remaining two chapters of this book delve into a relatively complex yet critical-to-understand topic for the modern pro-level driver or kernel developer: understanding and working with kernel synchronization.

    The book uses the latest, at the time of writing, 5.4 Long Term Support (LTS) Linux kernel. It's a kernel that will be maintained (both bug and security fixes) from November 2019 right through December 2025! This is a key point, ensuring that this book's content remains current and valid for years to come!

    We very much believe in a hands-on empirical approach: over 20 kernel modules (besides a few user apps and shell scripts) on this book's GitHub repository make the learning come alive, making it fun, interesting, and useful.

    We really hope you learn from and enjoy this book. Happy reading!

    Who this book is for

    This book is primarily for Linux programmers beginning to find their way with device driver development. Linux device driver developers looking to overcome frequent and common kernel/driver development issues, as well as understanding and learning to perform common driver tasks – the modern Linux Device Model (LDM) framework, user-kernel interfaces, performing peripheral I/O, handling hardware interrupts, dealing with concurrency, and more – will benefit from this book. A basic understanding of Linux kernel internals (and common APIs), kernel module development, and C programming is required.

    What this book covers

    Chapter 1, Writing a Simple misc Character Device Driver, first goes through the very basics – what a driver is supposed to do, the device namespace, the sysfs, and basic tenets of the LDM. We then delve into the details of writing a simple character device driver; along the way, you will learn about the framework – in effect, the internal implementation of the if it's not a process, it's a file philosophy/architecture! You'll learn how to implement a misc class character device driver with various methods; several code examples help harden the concepts. Basic copying of data between the user-kernel space and vice versa is covered. Also covered are key security concerns and how to address them (in this context); a bad driver giving rise to a privilege escalation issue is actually demonstrated!

    Chapter 2, User-Kernel Communication Pathways, covers how to communicate between the kernel and the user space, which is critical to you, as a kernel module/driver author. Here, you'll learn about various communication interfaces, or pathways. This is an important aspect of writing kernel/driver code. Several techniques are employed: communication via traditional procfs, the better way for drivers via sysfs, and several others, via debugfs, netlink sockets, and the ioctl(2) system call.

    Chapter 3, Working with Hardware I/O Memory, covers a key aspect of driver writing – the issue with (and the solution to) accessing hardware memory (mapped memory I/O) from a peripheral device or chip. We cover using the common memory-mapped I/O (MMIO) technique as well as the (typically on x86) port I/O (PIO) techniques for hardware I/O memory access and manipulation. Several examples from existing kernel drivers are shown as well.

    Chapter 4, Handling Hardware Interrupts, shows how to handle and work with hardware interrupts in great detail. We start with a brief on how the kernel works with hardware interrupts, then move on to how you're expected to allocate an IRQ line (covering modern resource-managed APIs), and how to correctly implement the interrupt handler routine. The modern approach of using threaded handlers (and the why of it), the Non-Maskable Interrupt (NMI), and more, are then covered. The reasons for and using both top half and bottom half interrupt mechanisms (hardirq, tasklet, and softirqs) in code, as well as key information regarding the dos and don'ts of hardware interrupt handling are covered. Measuring interrupt latencies with the modern [e]BPF toolset, as well as with Ftrace, concludes this key chapter.

    Chapter 5, Working with Kernel Timers, Threads, and Workqueues, covers how to use some useful (and often employed by drivers) kernel mechanisms – delays, timers, kernel threads, and workqueues. They come in handy in many real-world situations. How to perform both blocking and non-blocking delays (as the situation warrants), setting up and using kernel timers, creating and working with kernel threads, and understanding and using kernel workqueues are all covered here. Several example modules, including three versions of a simple encrypt decrypt (sed) example driver, serve to illustrate the concepts learned in code.

    Chapter 6, Kernel Synchronization – Part 1, first covers the key concepts regarding critical sections, atomicity, what a lock conceptually achieves, and, very importantly, the why of all this. We then cover concurrency concerns when working within the Linux kernel; this moves us naturally on to important locking guidelines, what deadlock means, and key approaches to preventing deadlock. Two of the most popular kernel locking technologies – the mutex lock and the spinlock – are then discussed in depth, along with several (driver) code examples.

    Chapter 7, Kernel Synchronization – Part 2, continues the journey on kernel synchronization. Here, you'll learn about key locking optimizations – using lightweight atomic and (the more recent) refcount operators to safely operate on integers, using RMW bit operators to safely perform bit ops, and using the reader-writer spinlock over the regular one. Inherent risks, such as cache false sharing, are discussed as well. An overview of lock-free programming techniques (with an emphasis on per-CPU variables and their usage, along with examples) is then covered. A critical topic, lock debugging techniques, including the usage of the kernel's powerful lockdep lock validator, is then covered. The chapter is rounded off with a brief look at memory barriers (along with an existing kernel network driver's usage of memory barriers).

    We again stress that this book is for kernel programmers who are new to writing device drivers; several Linux driver topics are beyond this book's scope and are not covered. This includes other types of device drivers (besides character), working with the device tree, and so on. Packt offers other valuable guides to help you gain traction on these topic areas. This book would be an excellent start.

    To get the most out of this book

    To get the most out of this book, we expect you to have knowledge and experience of the following:

    Know your way around a Linux system, on the command line (the shell).

    The C programming language.

    Know how to write simple kernel modules via the Loadable Kernel Module (LKM) framework

    Understand (at least the basics) of key Linux kernel internals concepts: kernel architecture, memory management (plus common dynamic memory alloc/de-alloc APIs), and CPU scheduling.

    It's not mandatory, but experience with Linux kernel programming concepts and technologies will help greatly.

    Ideally, we highly recommend reading this book's companion, Linux Kernel Programming, first.

    The details on hardware and software requirements for this book, as well as their installation, are shown here:

    Detailed installation steps (software-wise):

    Install Linux as a VM on a host Windows system; follow one of these tutorials:

    Install Linux Inside Windows Using VirtualBox, Abhishek Prakash (It's FOSS!, August 2019)https://itsfoss.com/install-linux-in-virtualbox/

    Alternately, here's another tutorial to help you do the same: Install Ubuntu on Oracle VirtualBoxhttps://brb.nci.nih.gov/seqtools/installUbuntu.html

    Install the required software packages on the Linux VM:

    Log in to your Linux guest VM and first run the following commands within a Terminal window (on a shell):

    sudo apt update

    sudo apt install gcc make perl

    Install the Oracle VirtualBox Guest Additions now. Reference:How to Install VirtualBox Guest Additions in Ubuntuhttps://www.tecmint.com/install-virtualbox-guest-additions-in-ubuntu/

    (This step only applies if you are running Ubuntu as a VM using Oracle VirtualBox as the hypervisor app.)

    To install the packages, take the following steps:

    Within the Ubuntu VM, first run the sudo apt update command

    Now, run the sudo apt install git fakeroot build-essential tar ncurses-dev tar xz-utils libssl-dev bc stress python3-distutils libelf-dev linux-headers-$(uname -r) bison flex libncurses5-dev util-linux net-tools linux-tools-$(uname -r) exuberant-ctags cscope sysfsutils curl perf-tools-unstable gnuplot rt-tests indent tree pstree smem hwloc bpfcc-tools sparse flawfinder cppcheck tuna hexdump trace-cmd virt-what command in a single line.

    Useful resources:

    The Linux kernel official online documentation:https://www.kernel.org/doc/html/latest/.

    The Linux Driver Verification (LDV) project, particularly the Online Linux Driver Verification Servicepage: http://linuxtesting.org/ldv/online?action=rules.

    SEALS - Simple Embedded ARM Linux System: https://github.com/kaiwan/seals/.

    Every chapter of this book has a very useful Further reading section as well, detailing more resources.

    Detailed instructions, as well as additional useful projects, installing a cross-toolchain for ARM, and more, are described inChapter 1, Kernel Workspace Setup, of this book's companion guide, Linux Kernel Programming, Kaiwan N Billimoria, Packt Publishing.

    We have tested all the code in this book (it has its own GitHub repository as well) on these platforms:

    x86_64 Ubuntu 18.04 LTS guest OS (running on Oracle VirtualBox 6.1)

    x86_64 Ubuntu 20.04.1 LTS guest OS (running on Oracle VirtualBox 6.1)

    x86_64 Ubuntu 20.04.1 LTS native OS

    ARM Raspberry Pi 3B+ (running both its distro kernel as well as our custom 5.4 kernel); lightly tested.

    If you are using the digital version of this book, we advise you to type the code yourself or, better, access the code via the GitHub repository (link available in the next section). Doing so will help you avoid any potential errors related to the copying and pasting of code.

    For this book, we'll log in as the user named llkd. I strongly recommend that you follow the empirical approach: not taking anyone's word on anything at all, but trying it out and experiencing it for yourself. Hence, this book gives you many hands-on experiments and kernel driver code examples that you can and must try out yourself; this will greatly aid you in making real progress and deeply learning and understanding various aspects of Linux driver/kernel development.

    Download the example code files

    You can download the example code files for this book from GitHub at  https://github.com/PacktPublishing/Linux-Kernel-Programming-Part-2. In case there's an update to the code, it will be updated on the existing GitHub repository.

    We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!

    Download the color images

    We also provide a PDF file that has color images of the screenshots/diagrams used in this book. You can download it here: http://www.packtpub.com/sites/default/files/downloads/9781801079518_ColorImages.pdf.

    Conventions used

    There are a number of text conventions used throughout this book.

    CodeInText: Indicates code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles. Here is an example: The ioremap() API returns a KVA of the void * type (since it's an address location).

    A block of code is set as follows:

    static int __init miscdrv_init(void)

    {

        int ret;

        struct device *dev;

    When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:

    #define pr_fmt(fmt) %s:%s(): fmt, KBUILD_MODNAME, __func__

    [...]

    #include

    #include            

    [...]

    Any command-line input or output is written as follows:

    pi@raspberrypi:~ $ sudo cat /proc/iomem

    Bold: Indicates a new term, an important word, or words that you see onscreen. For example, words in menus or dialog boxes appear in the text like this. Here is an example: Select System info from the Administration panel.

    Warnings or important notes appear like this.

    Tips and tricks appear like this.

    Get in touch

    Feedback from our readers is always welcome.

    General feedback: If you have questions about any aspect of this book, mention the book title in the subject of your message and email us at [email protected].

    Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/support/errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.

    Piracy: If you come across any illegal copies of our works in any form on the Internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.

    If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.

    Reviews

    Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!

    For more information about Packt, please visit packt.com.

    Section 1: Character Device Driver Basics

    Here, we'll cover what a device driver is, namespaces, Linux Device Model (LDM) basics, and the character device driver framework. We'll implement simple misc drivers (leveraging the kernel's misc framework). We'll set up communication between the user and kernel spaces (via various interfaces, such as debugfs,  sysfs, netlink sockets, and ioctl). You will learn how to work with hardware I/O memory on a peripheral chip, as well as understanding and working with hardware interrupts. You'll also learn how to use kernel features such as kernel-level timers, create kernel threads, and use workqueues.

    This section comprises the following chapters:

    Chapter 1, Writing a Simple misc Character Device Driver

    Chapter 2, User-Kernel Communication Pathways

    Chapter 3, Working with Hardware I/O Memory

    Chapter 4, Handling Hardware Interrupts

    Chapter 5, Working with Kernel Timers, Threads, and Workqueues

    Writing a Simple misc Character Device Driver

    No doubt, device drivers are a vast and interesting topic. Not only that, they are perhaps the most common use of the Loadable Kernel Module (LKM) framework that we have been using. Here, we shall introduce you to writing a few simple yet complete Linux character device drivers, within a class called misc; yes, that's short for miscellaneous. We wish to emphasize that this chapter is limited in its scope and coverage - here, we do not attempt to delve into the deep details regarding the Linux driver model and its many frameworks; instead, we refer you to several excellent books and tutorials on this topic via the Further reading section for this chapter. Our aim here is to quickly get you familiar with the overall concepts behind writing a simple character device driver.

    Having said that, this book indeed has several chapters that are dedicated to what a driver author needs to know. Besides this introductory chapter, we cover (in detail) how a driver author works with hardware I/O memory, hardware interrupt handling (and its many sub-topics), and kernel mechanisms such as delays, timers, kernel threads, and work queues. Use of various user-kernel communication pathways or interfaces is covered in detail as well. The final two chapters of this book then focus on something very important for any kernel development, including drivers – kernel synchronization.

    The other reasons we'd prefer to write a simple Linux character device driver and not just our usual kernel module are as follows:

    Until now, our kernel modules have been quite simplistic, having only init and cleanup functions, nothing more. A device driver providesseveralentry points into the kernel; these are the file-related system calls, known as the driver's methods. So, we can have anopen()method, a read()method, awrite()method, anllseek() method, an[unlocked|compat]_ioctl()method, arelease()method, and so on.

    FYI, all possible methods (functions) the driver author can hook into are in this key kernel data structure: include/linux/fs.h:file_operations (more on this in the Understanding the connection between the process, the driver, and the kernel section).

    This situation is simply more realistic, and more interesting.

    In this chapter, we will cover the following topics:

    Getting started with writing a simple misc character device driver

    Copying data from kernel to user space and vice versa

    A misc driver with a secret

    Issues and security concerns

    Technical requirements

    I assume that you have gone through the Preface section To get the most out of this book, and have appropriately prepared a guest VM running Ubuntu 18.04 LTS (or a later stable release) and installed all the required packages. If not, I highly recommend you do this first. To get the most out of this book, I strongly recommend you first set up the workspace environment, including cloning this book's GitHub repository for the code, and work on it in a hands-on fashion. The repository can be found here: https://github.com/PacktPublishing/Linux-Kernel-Programming-Part-2.

    Getting started with writing a simple misc character device driver

    In this section, you will first learn the required background material – understanding the basics of the device file (or node) and its hierarchy. After that, you will learn – by actually writing the code of a very simple misc character driver – the kernel framework behind the raw character device driver. Along the way, we shall cover how to create the device node(s) and test the driver via a user space app. Let's get started!

    Understanding the device basics

    Some quick background is in order.

    A device driver is the interface between the OS and a peripheral hardware device. It can be written inline – that is, compiled within the kernel image file – or, more commonly, written outside of the kernel source tree as a kernel module (we covered the LKM framework in detail in the companion guide Linux Kernel Programming, Chapter 4, Writing Your First Kernel Module – LKMs Part 1, and Chapter 5, Writing Your First Kernel Module – LKMs Part 2). Either way, the driver code certainly runs at OS privilege, in kernel space (user space device drivers do exist, but can suffer performance issues; while useful in many circumstances, we don't cover them here. Take a look at the Further reading section).

    In order for a user space application to gain access to the underlying device driver within the kernel, some I/O mechanism is required. The Unix (and thus Linux) design is to have the process open a special type of file – a device file, or device node. These files typically live in the /dev directory, and on modern systems are dynamic and auto-populated. The device node serves as an entry point into the device driver.

    In order for the kernel to distinguish between device files, it uses two attributes within their inode data structure:

    The type of file – either character (char) or block

    The major and minor number

    You will see that the namespace – the device type and the {major#, minor#} pair – form a hierarchy. Devices (and thus their drivers) are organized within a tree-like hierarchy within the kernel (the driver core code within the kernel takes care of this). The hierarchy is first divided based on device type – block or char. Within that, we have some n major numbers for each type, and each major number is further classified via some m minor numbers; Figure 1.1 shows this hierarchy. 

    Now, the key difference between block and character devices is that block devices have the (kernel-level) capability to be mounted and thus become part of the user-accessible filesystem. Character devices cannot be mounted; thus, storage devices tend to be block-based. Think of it this way (a bit simplistic but useful): if the (hardware) device is not storage, nor a network device, then it's a character device. A huge number of devices

    Enjoying the preview?
    Page 1 of 1