-
Notifications
You must be signed in to change notification settings - Fork 0
DMA
Java-level abstraction for the x86 ISA DMA controller, enabling hardware-initiated data transfers between I/O devices and system memory without CPU intervention.
JNode's DMA subsystem provides a complete Java abstraction over the x86 ISA DMA controller hardware. The ISA DMA controller consists of two cascaded 8237-style controllers: controller 1 manages channels 0–3 (byte transfers), and controller 2 manages channels 5–7 (word transfers), with channel 4 permanently reserved as the cascade link between the two controllers.
The architecture follows a three-layer design: device drivers interact with architecture-neutral interfaces (DMAManager, DMAResource), which are implemented by an x86-specific plugin (DMAPlugin) registered in InitialNaming-Service-Registry, which in turn delegates to a low-level hardware class (DMA) that performs raw I/O port programming. This separation means drivers never touch hardware registers directly — they claim a channel, configure a transfer, enable it, and respond to the completion IRQ.
Currently the floppy disk controller (DefaultFDC) is the only driver using DMA, but the architecture is general-purpose and supports any ISA DMA device. DMA transfers are limited to the first 16 MB of physical memory, and no transfer may cross a 64 KB (channels 0–3) or 128 KB (channels 5–7) physical page boundary.
| Class / File | Role |
|---|---|
core/src/core/org/jnode/system/resource/DMAManager.java |
Architecture-neutral service interface; looked up via InitialNaming
|
core/src/core/org/jnode/system/resource/DMAResource.java |
Architecture-neutral channel handle; drivers use this to setup/enable transfers |
core/src/core/org/jnode/system/resource/DMAException.java |
Exception type for DMA-specific errors |
core/src/core/org/jnode/system/x86/DMAPlugin.java |
Plugin implementing DMAManager; owns channel allocation and the DMA hardware instance |
core/src/core/org/jnode/system/x86/X86DMAChannel.java |
Per-channel resource handle implementing DMAResource; delegates to DMAPlugin
|
core/src/core/org/jnode/system/x86/DMA.java |
Low-level x86 DMA controller programming via I/O ports (package-private) |
core/src/core/org/jnode/system/x86/DMAConstants.java |
Hardware port addresses and mode constants |
core/src/driver/org/jnode/driver/block/floppy/DefaultFDC.java |
Primary DMA consumer; floppy driver using channel 2 |
-
Service lookup: Driver calls
InitialNaming.lookup(DMAManager.NAME)to get theDMAManagerservice. -
Claim a channel:
dmamanager.claimDMAChannel(owner, dmanr)— validates channel number (0–7), checks thechannels[]array for availability, and creates anX86DMAChannelif free. Channel 4 is permanently reserved (cascade). -
Allocate DMA buffer: Driver calls
ResourceManager.claimMemoryResource(owner, null, size, ResourceManager.MEMMODE_ALLOC_DMA)— this allocates from the bottom of physical memory (viaUnsafe.getMinAddress()), ensuring the buffer is within the 16 MB DMA zone. -
On failure:
ResourceNotFreeExceptionis thrown if the channel is already claimed;IllegalArgumentExceptionfor invalid channel numbers.
The transfer lifecycle for a typical DMA operation (e.g., floppy read):
1. dma.setup(dmaMem, length, DMAResource.MODE_READ)
→ dma.test() -- validate alignment and page boundaries
→ dma.disable(ch) -- mask the channel (prevent spurious transfers)
→ dma.clearFF(ch) -- reset byte pointer flip-flop
→ dma.setMode(ch, mode) -- program direction (read/write)
→ dma.setAddress(ch, addr) -- program 24-bit physical address
→ dma.setLength(ch, len) -- program transfer count (value = count - 1)
2. dma.enable(ch) -- unmask the channel; hardware begins transfer
3. [Hardware performs transfer: device ↔ DMA buffer]
4. IRQ fires → driver.handleInterrupt()
→ dmaMem.getBytes(data, offset, length) -- copy from DMA buffer
5. dma.release() -- free the channel slot
The x86 DMA controller uses 24-bit addressing: a 16-bit address register plus an 8-bit page register. For channels 5–7 (word mode), the address is right-shifted by 1 and the page register ignores bit 0, resulting in 128 KB page granularity. The DMA.setAddress() method handles this split automatically:
-
Channels 0–3:
page = address >>> 16,addr16 = address & 0xFFFF -
Channels 5–7:
page = (address >>> 16) & 0xFE,addr16 = (address >>> 1) & 0xFFFF
MemoryResourceImpl uses a dual-allocation strategy controlled by the mode parameter:
| Mode | Allocation Start | Use Case |
|---|---|---|
MEMMODE_ALLOC_DMA (0x01) |
Unsafe.getMinAddress() (bottom of RAM) |
DMA buffers — must be in low 16 MB |
MEMMODE_NORMAL (0x00) |
Unsafe.getMemoryEnd() (top of RAM) |
General-purpose memory |
Using MEMMODE_NORMAL for a DMA buffer would allocate memory above the 16 MB boundary on systems with more RAM, causing silent transfer failures.
| Register | Controller 1 (ch 0–3) | Controller 2 (ch 5–7) |
|---|---|---|
| Command | 0x08 | 0xD0 |
| Mask | 0x0A | 0xD4 |
| Mode | 0x0B | 0xD6 |
| Clear Flip-Flop | 0x0C | 0xD8 |
| Address (ch 0/5) | 0x00 | 0xC0 |
| Count (ch 0/5) | 0x01 | 0xC2 |
| Page (ch 0) | 0x87 | — |
| Page (ch 1/5) | 0x83 | 0x8B |
Controller 2 offsets are all even (2× the controller 1 offset for the same register), which the DMA class handles internally.
DMAPlugin extends Plugin and implements DMAManager. Its lifecycle:
-
startPlugin(): Creates theDMAhardware instance (which claims I/O port ranges fromResourceManager), then binds itself inInitialNamingasDMAManager.NAME. -
stopPlugin(): Unbinds fromInitialNaming, callsDMA.release()to free I/O port claims.
The plugin descriptor is in core/descriptors/org.jnode.runtime_x86.xml.
-
Channel 4 is permanently reserved: Pre-allocated in the
DMAPluginconstructor as a cascade channel (SimpleResourceOwner("cascade")). CallingclaimDMAChannel(owner, 4)will always throwResourceNotFreeException. This is correct hardware behavior but is not documented in theDMAManagerinterface. -
Likely bug in
DMA.getLength()andDMA.setAddress(): These methods read/write viadma1IOfor ALL channels, including channels 5–7 which should usedma2IO. Compare withsetLength()andsetMode()which correctly usedma2IOfor channels >= 4. This appears to be a copy-paste bug that would cause incorrect behavior on channels 5–7. -
Transfer count register = actual count − 1:
DMA.setLength()decrements the length before writing to the hardware register. This is correct per the x86 8237 spec, but can confuse developers reading the code. -
DMA buffers must use
MEMMODE_ALLOC_DMA: Allocating withMEMMODE_NORMALplaces memory at the top of physical RAM, which on systems with >16 MB is outside the DMA-able zone. The hardware would silently fail or corrupt data. -
No auto-initialization mode support:
DMAConstantsdefinesDMA_AUTOINIT = 0x10, butX86DMAChannel.setup()only acceptsMODE_READ(1) orMODE_WRITE(2). Any other value throwsIllegalArgumentException. Auto-init DMA (continuous transfers) is not exposed. -
X86DMAChannel.getParent()always returns null: DMA channels have no resource parent-child hierarchy; their lifecycle is managed entirely byDMAPlugin. -
DMAclass is package-private: Cannot be accessed outsideorg.jnode.system.x86. All driver access must go throughDMAManager/DMAResourceinterfaces. -
@MagicPermissionrequired: BothDMA.javaandDMAPlugin.javaare annotated with@MagicPermissionto bypass normal Java security checks for raw I/O port access. -
Resource release order matters:
DefaultFDCdocuments "PRESERVE THIS CLAIMING ORDER!" and releases in reverse:dmaMem → io2 → io1 → dma → irq. Violating this order can leave channels orphaned. -
Only the floppy driver uses DMA currently: Despite the general-purpose architecture, no other ISA DMA devices (sound cards, parallel port, etc.) are implemented in JNode.
-
DMA limited to lower 16 MB of physical memory: This is an x86 hardware constraint.
DMA.test()validates this by checking address and length, but does not enforce it at runtime if a caller bypassessetup().
- Resource-Management — I/O port, memory, and IRQ resource claiming that DMA depends on
- Driver-Framework — Device driver architecture that DMA-enabled drivers plug into
-
Plugin-System — Plugin lifecycle pattern used by
DMAPlugin -
InitialNaming-Service-Registry — Service discovery for
DMAManagerandResourceManager - Interrupt-Handling — IRQ handling pattern used alongside DMA completion
-
VM-Magic —
@MagicPermission,Address,Offsettypes used in DMA implementation -
Object-Layout — Memory resource and
Unsafelow-level memory access