The Pedigree Project 0.1
DmaBuffer.h
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#ifndef PEDIGREE_MODULES_DRIVERS_COMMON_DMABUFFER_H
21#define PEDIGREE_MODULES_DRIVERS_COMMON_DMABUFFER_H
22#include "pedigree/kernel/TargetInfo.h"
23
24#include <config.h>
25
26namespace DriverDma {
27
28constexpr size_t pageCountForBytes(size_t byteCount, size_t pageSize = TargetInfo::getPageSize()) {
29 return byteCount ? ((byteCount - 1) / pageSize) + 1 : 0;
30}
31
32constexpr bool physicalRangeFits(physical_uintptr_t address, size_t byteCount,
33 physical_uintptr_t highestAddress) {
34 return !byteCount || (address <= highestAddress && (byteCount - 1) <= (highestAddress - address));
35}
36
37constexpr bool physicalEndpointsAreContiguous(physical_uintptr_t first, physical_uintptr_t last,
38 size_t byteCount, physical_uintptr_t highestAddress) {
39 return byteCount && physicalRangeFits(first, byteCount, highestAddress) &&
40 last == first + (byteCount - 1);
41}
42
44template <size_t PageBytes = TargetInfo::getPageSize(), typename AddressSpace>
45bool virtualToPhysical(AddressSpace& addressSpace, uintptr_t virtualAddress,
46 physical_uintptr_t& physicalAddress) {
47 static_assert(PageBytes && !(PageBytes & (PageBytes - 1)),
48 "DMA mapping pages must be a power of two");
49
50 void* address = reinterpret_cast<void*>(virtualAddress);
51 if (!addressSpace.isMapped(address))
52 return false;
53
54 physical_uintptr_t physicalPage = 0;
55 size_t flags = 0;
56 addressSpace.getMapping(address, physicalPage, flags);
57
58 const size_t pageOffset = virtualAddress & (PageBytes - 1);
59 const physical_uintptr_t highestPhysical = static_cast<physical_uintptr_t>(-1);
60 if (physicalPage > highestPhysical - pageOffset)
61 return false;
62
63 physicalAddress = physicalPage + pageOffset;
64 return true;
65}
66
73template <size_t PageBytes = TargetInfo::getPageSize(), typename AddressSpace>
74bool contiguousVirtualRangeToPhysical(AddressSpace& addressSpace, uintptr_t virtualAddress,
75 size_t byteCount, physical_uintptr_t& physicalAddress) {
76 static_assert(PageBytes && !(PageBytes & (PageBytes - 1)),
77 "DMA mapping pages must be a power of two");
78
79 if (!byteCount)
80 return false;
81
82 const uintptr_t highestVirtual = static_cast<uintptr_t>(-1);
83 if ((byteCount - 1) > highestVirtual - virtualAddress)
84 return false;
85
86 if (!virtualToPhysical<PageBytes>(addressSpace, virtualAddress, physicalAddress))
87 return false;
88
89 const physical_uintptr_t highestPhysical = static_cast<physical_uintptr_t>(-1);
90 if ((byteCount - 1) > highestPhysical - physicalAddress)
91 return false;
92
93 size_t consumed = PageBytes - (virtualAddress & (PageBytes - 1));
94 if (consumed >= byteCount)
95 return true;
96
97 while (consumed < byteCount) {
98 physical_uintptr_t nextPhysical = 0;
99 if (!virtualToPhysical<PageBytes>(addressSpace, virtualAddress + consumed, nextPhysical) ||
100 nextPhysical != physicalAddress + consumed) {
101 return false;
102 }
103
104 const size_t remaining = byteCount - consumed;
105 consumed += remaining < PageBytes ? remaining : PageBytes;
106 }
107
108 return true;
109}
110
111static_assert(pageCountForBytes(0, 4096) == 0);
112static_assert(pageCountForBytes(4096, 4096) == 1);
113static_assert(pageCountForBytes(4097, 4096) == 2);
114static_assert(pageCountForBytes(16384, 16384) == 1);
115static_assert(pageCountForBytes(20480, 16384) == 2);
116static_assert(physicalRangeFits(0xfffff000, 4096, 0xffffffff));
117static_assert(!physicalRangeFits(0xfffff001, 4096, 0xffffffff));
118static_assert(physicalEndpointsAreContiguous(0x1000, 0x1fff, 4096, 0xffffffff));
119static_assert(!physicalEndpointsAreContiguous(0x1000, 0x2fff, 4096, 0xffffffff));
120static_assert(!physicalEndpointsAreContiguous(0xfffff001, 0xffffffff, 4096, 0xffffffff));
121
122} // namespace DriverDma
123
124#endif
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
uintptr_t physicalAddress(physical_uintptr_t address) PURE
Definition utils.h:39