Linux drivers · Course contents
Machine not started
40 min·Intermediate

Transfer a sample with DMA

Publish a coherent DMA descriptor, transfer ownership to hardware, and consume a completed buffer.

Jump to code and terminal ↓

A bus address is not a C pointer

The harness allocates a descriptor and sample buffer with dmam_alloc_coherent. It passes CPU pointers and DMA bus addresses separately. Put buffer_dma in desc->address and use descriptor_dma for the device's low/high address registers. A kernel virtual pointer is not a device address.

Publish ownership

Set length, clear completed, and set LAB_DEVICE_OWNS in flags. Call the write barrier before publishing the descriptor address and ringing LAB_DMA_START. Hardware validates ownership and writes the current sample directly into guest RAM; it then sets completed to four, clears DEVICE_OWNS, and sets DMA_DONE.

Read only completed work

The completion callback first checks that DEVICE_OWNS is clear, then executes the read barrier before inspecting DMA_DONE and completed. If the device is disabled, it rejects the request and retains device ownership; that is not a completed sample. Coherent memory removes explicit cache maintenance here, but it does not remove ordering and ownership rules.

Your goal

Give the device a valid descriptor and report completion only after hardware returns ownership and completes four bytes.

What the tests observe

  • The device DMA-writes the sample into guest RAM
  • The descriptor reports four completed bytes and CPU ownership
  • A disabled device produces an incomplete request rather than false success

Need a nudge?

Hint 1

Initialize address, length, completed, and flags before the write barrier. Then write both halves of descriptor_dma and ring the doorbell.

Hint 2

The device expects four bytes for one signed temperature sample. Successful flags are DMA_DONE with DEVICE_OWNS clear.

Hint 3

Return false immediately while desc->flags & LAB_DEVICE_OWNS. After the read barrier, verify DMA_DONE and completed == 4.

What happens inside Linux

Linux supplies coherent guest memory and bus addresses. The emulated peripheral reads its descriptor through the physical memory map and writes the sample into that RAM without a CPU store from the driver. It updates completion and raises an IRQ. The CPU regains ownership only after observing the device's completion protocol.

Optional account sync. Exercises work without an account.

Reference headers and helper functions

Read-only lesson support. Your editable implementation is below.

lab_driver.h

/* SPDX-License-Identifier: GPL-2.0-only
 * Narrow module callback ABI. Implementations execute in Linux kernel context.
 * The supplied platform harness owns registration, mappings, IRQ registration,
 * coherent allocation, sysfs lifetime, and module cleanup.
 */
#ifndef LAB_DRIVER_H
#define LAB_DRIVER_H
typedef unsigned int lab_u32;
typedef unsigned long lab_u64;
#define LAB_ID 0x00
#define LAB_TEMPERATURE 0x04
#define LAB_CONTROL 0x08
#define LAB_STATUS 0x0c
#define LAB_IRQ_STATUS 0x10
#define LAB_IRQ_ACK 0x14
#define LAB_SAMPLE 0x18
#define LAB_DMA_LO 0x20
#define LAB_DMA_HI 0x24
#define LAB_DMA_START 0x28
#define LAB_DMA_STATUS 0x2c
#define LAB_DEVICE_OWNS 1U
#define LAB_DMA_DONE 2U
struct lab_dma_descriptor {
    lab_u64 address;
    lab_u32 length, flags, completed, reserved;
};
struct lab_api {
    void *context;
    lab_u32 (*read32)(void *, unsigned int);
    void (*write32)(void *, unsigned int, lab_u32);
    void (*write_barrier)(void);
    void (*read_barrier)(void);
};
struct lab_irq_state { lab_u32 count, last_temperature; };
int student_probe(const struct lab_api *);
int student_temperature(const struct lab_api *);
void student_interrupt(const struct lab_api *, lab_u32, struct lab_irq_state *);
int student_dma_submit(const struct lab_api *, struct lab_dma_descriptor *, lab_u64, lab_u64, lab_u32);
int student_dma_complete(const struct lab_api *, struct lab_dma_descriptor *);
#endif
C/work/driver.cLoading local work…
Restoring your workspace…
⌘ / Ctrl ↵
TerminalRISC-V 64 · root · /work

Your Linux machine is ready to start.

Boot a disposable environment in this browser. You’ll get a real root shell, with no remote server or account required.

Acme temperature sensor

Linux kernel driver
42°C
Address
0x40000000
Connection
connected
Control
unobserved
Accesses shown
0
IRQ enable
unobserved
Hardware events shown
0

Start Linux and run your driver. Reads and writes to the device will appear here as they happen.

Interrupts and DMA

The emulator records hardware interrupts, acknowledgments, and DMA transfers here. Load your driver and request a sample to observe the device.