From 92dcd32550126ba462f4ed8e820a9dce217b70f5 Mon Sep 17 00:00:00 2001 From: Nazerke Date: Tue, 11 Jul 2023 14:48:45 +0000 Subject: [PATCH 01/42] initial software driver for dma --- sdk/include/platform/ibex/platform-dma.hh | 118 ++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 sdk/include/platform/ibex/platform-dma.hh diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/include/platform/ibex/platform-dma.hh new file mode 100644 index 00000000..80fc52b2 --- /dev/null +++ b/sdk/include/platform/ibex/platform-dma.hh @@ -0,0 +1,118 @@ +#pragma once + +#define MALLOC_QUOTA 0x100000 +#define TEST_NAME "Allocator" +#include "tests.hh" + +#include +#include +#include +#include +#include + +#include +#include + +using thread_pool::async; +DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); + +namespace Ibex { + class GenericDMA { + private: + + Timeout noWait{0}; + SObjStruct *dmaHeap; + + struct StreamingInterface + { + uint32_t control; + uint32_t status; + uint32_t address; + uint32_t lengthInBytes; + }; + + struct DMAInterface + { + /** + * these two streaming interfaces are for + * [0] memory to target and + * [1] target to memory directions. + * target here is any I/O device, peripheral or an accelerator + */ + volatile StreamingInterface *streamingInterface[2]; + }; + + volatile DMAInterface *dmaInterface; + + public: + + void init() + { + dmaInterface->streamingInterface[0] = MMIO_CAPABILITY(StreamingInterface, dma_to_target); + dmaInterface->streamingInterface[1] = MMIO_CAPABILITY(StreamingInterface, dma_to_memory); + } + + uint32_t read_status(bool index) + { + /** + * this statement returns the less signifance bit + * to show the halted status + */ + return dmaInterface->streamingInterface[index]->status & 0x1; + } + + void write_conf(bool index, uint32_t *address, uint32_t lengthInBytes) + { + /** + * filling address and length fields + */ + dmaInterface->streamingInterface[index]->address = *address; + dmaInterface->streamingInterface[index]->lengthInBytes = lengthInBytes; + + /** + * create a heap capability for dma below. + * todo: we assume that this heap capability can serve + * for different compartments, irrespective of + * the origin because this is a driver code? + */ + dmaHeap = STATIC_SEALED_VALUE(dmaDriverHeap); + + /** + * claim the allocated memory + */ + + int claimCount = 0; + + auto claim = [&]() { + size_t claimSize = heap_claim(dmaHeap, address); + claimCount++; + TEST(claimSize == lengthInBytes, + "{}-byte allocation claimed as {} bytes (claim number {})", + lengthInBytes, + claimSize, + claimCount); + }; + + claim(); + } + + void start_dma(bool index) + { + /** + * setting a start bit + */ + dmaInterface->streamingInterface[index]->control = 0x1; + } + + void reset_dma(bool index, uint32_t *address) + { + /** + * setting a reset bit + */ + dmaInterface->streamingInterface[index]->control = 0x4; + + int ret = heap_free(dmaHeap, address); + } + + }; +} // namespace Ibex \ No newline at end of file From 1da84cdd1d38b25b0b0f0fbb650cff26c635354e Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Fri, 14 Jul 2023 19:16:48 +0100 Subject: [PATCH 02/42] initial software stack of the dma: subject to change later, but the base should be similar --- examples/10.dma_test/README.md | 4 + examples/10.dma_test/dma_test.c | 19 +++ examples/10.dma_test/xmake.lua | 35 ++++++ sdk/core/dma/dma.h | 74 +++++++++++ sdk/core/dma/dma_compartment.cc | 75 +++++++++++ sdk/core/dma/dma_compartment.h | 18 +++ sdk/include/platform/ibex/platform-dma.hh | 146 +++++++++++++--------- 7 files changed, 309 insertions(+), 62 deletions(-) create mode 100644 examples/10.dma_test/README.md create mode 100644 examples/10.dma_test/dma_test.c create mode 100644 examples/10.dma_test/xmake.lua create mode 100644 sdk/core/dma/dma.h create mode 100644 sdk/core/dma/dma_compartment.cc create mode 100644 sdk/core/dma/dma_compartment.h diff --git a/examples/10.dma_test/README.md b/examples/10.dma_test/README.md new file mode 100644 index 00000000..d44b51a5 --- /dev/null +++ b/examples/10.dma_test/README.md @@ -0,0 +1,4 @@ +DMA example +=================== + + diff --git a/examples/10.dma_test/dma_test.c b/examples/10.dma_test/dma_test.c new file mode 100644 index 00000000..7a3ad255 --- /dev/null +++ b/examples/10.dma_test/dma_test.c @@ -0,0 +1,19 @@ +// Copyright Microsoft and CHERIoT Contributors. +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include + +/// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +/// Thread entry point. +void __cheri_compartment("dma") dma_request() +{ + // Print hello world, along with the compartment's name to the default UART. + Debug::log("DMA request app entered!"); + + +} diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua new file mode 100644 index 00000000..a5cb41f4 --- /dev/null +++ b/examples/10.dma_test/xmake.lua @@ -0,0 +1,35 @@ +-- Copyright Microsoft and CHERIoT Contributors. +-- SPDX-License-Identifier: MIT + +set_project("CHERIoT Simple DMA App") +sdkdir = "../../sdk" +includes(sdkdir) +set_toolchains("cheriot-clang") + +-- Support libraries +includes(path.join(sdkdir, "lib/freestanding"), + path.join(sdkdir, "lib/atomic"), + path.join(sdkdir, "lib/crt")) + +option("board") + set_default("sail") + +compartment("dma") + add_files("dma_test.cc") + +-- Firmware image for the example. +firmware("dma_test") + add_deps("crt", "freestanding", "atomic_fixed") + add_deps("dma") + on_load(function(target) + target:values_set("board", "$(board)") + target:values_set("threads", { + { + compartment = "dma", + priority = 1, + entry_point = "dma_request", + stack_size = 0x200, + trusted_stack_frames = 1 + } + }, {expand = false}) + end) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h new file mode 100644 index 00000000..59a21e74 --- /dev/null +++ b/sdk/core/dma/dma.h @@ -0,0 +1,74 @@ +#pragma once + +#include "dma_compartment.cc" +#include "dma_compartment.h" +#include "platform/ibex/platform-dma.hh" +#include +#include +#include + +#if __has_include() +# include +#endif + +namespace DMA +{ + template + concept IsDmaDevice = requires(T device) + { + {device.init()}; + { + device.write_conf() + } -> std::same_as; + { + device.start_dma() + } -> std::same_as; + { + device.reset_dma() + } -> std::same_as; + }; + + template + typename PlatformDMA> + requires IsDmaDevice> + class GenericDMA : public PlatformDMA + { + public: + + /** + * Initialise a DMA device. + */ + + void init() + { + PlatformDMA::init(); + } + + void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) + { + /** + * Claim the DMA-able addresses first. + * If successful, then call an original + * dma write configuration function + */ + + int claimStatus = claim_dma(sourceAddress, targetAddress); + + if (claimStatus == 0) + { + PlatformDMA::write_conf(sourceAddress, targetAddress, lengthInBytes); + } + + } + + void reset_dma(uint32_t sourceAddress, uint32_t targetAddress) + { + free_dma(sourceAddress, targetAddress); + + PlatformDMA::reset_dma(); + } + + }; + + using DMA = GenericDMA; +} \ No newline at end of file diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc new file mode 100644 index 00000000..0f100808 --- /dev/null +++ b/sdk/core/dma/dma_compartment.cc @@ -0,0 +1,75 @@ +#pragma once + +#define MALLOC_QUOTA 0x100000 + +#include +#include +#include + +#include + +using thread_pool::async; +/** + * creating a global heap below. + * todo: we assume that this heap capability can serve + * for different compartments, irrespective of + * the origin because this is a driver code? +*/ +DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); + +SObjStruct *dmaHeap; + +int claim_dma(uint32_t *sourceAddress, uint32_t *targetAddress) +{ + /** + * create a heap capability for dma below. + */ + dmaHeap = STATIC_SEALED_VALUE(dmaDriverHeap); + + /** + * claim the memory + */ + + size_t sourceClaimSize = heap_claim(dmaHeap, sourceAddress); + + /** + * return with failure if + * no claim is available + */ + + if (sourceClaimSize == 0) + { + return -1; + } + + size_t targetClaimSize = heap_claim(dmaHeap, targetAddress); + + if (targetClaimSize == 0) + { + /** + * if the first claim was successful, but this one failed, + * then free the first claim and exit the function + * todo: assert a Debug::Assert() for heap_free() result later + */ + int sourceStatus = heap_free(dmaHeap, sourceAddress); + + return -1; + } + + /** + * return here, if both claims are successful + */ + return 0; + +} + +void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) +{ + int sourceStatus = heap_free(dmaHeap, sourceAddress); + int targetStatus = heap_free(dmaHeap, targetAddress); + + /** + * ideally, free should not fail if claim was successful + * todo: but in case, assert a Debug::Assert() for heap_free() later + */ +} \ No newline at end of file diff --git a/sdk/core/dma/dma_compartment.h b/sdk/core/dma/dma_compartment.h new file mode 100644 index 00000000..d79afb22 --- /dev/null +++ b/sdk/core/dma/dma_compartment.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +/** + * A function below claims the source and target addresses of the DMA interface. + * While, DMA is in progress, these addresses should not be freed + */ +__cheri_compartment("dma") int claim_dma(uint32_t *sourceAddress, + uint32_t *targetAddress); + +/** + * The function below frees the source and target addresses of the DMA interface. + * It should be called once the DMA operation is completed. + */ +__cheri_compartment("dma") void free_dma(uint32_t *sourceAddress, + uint32_t *targetAddress); \ No newline at end of file diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/include/platform/ibex/platform-dma.hh index 80fc52b2..87d24743 100644 --- a/sdk/include/platform/ibex/platform-dma.hh +++ b/sdk/include/platform/ibex/platform-dma.hh @@ -1,45 +1,61 @@ #pragma once -#define MALLOC_QUOTA 0x100000 -#define TEST_NAME "Allocator" -#include "tests.hh" - -#include #include -#include -#include +#include #include -#include -#include - -using thread_pool::async; -DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); - namespace Ibex { - class GenericDMA { + class PlatformDMA { private: - Timeout noWait{0}; - SObjStruct *dmaHeap; - - struct StreamingInterface + struct DMAInterface { + /** + * Below is DMA control register address: + * - includes start('b0), endianness conversion('b1 and 2) and reset bits('b3) + * - bit of index 1 and 2 are for enabling 2 and 4 byte swaps respectively + * - start bit is meant to be set at the end while programming + */ uint32_t control; + /** + * Below is DMA status register address + * - first bit refers to halted status + * - 0 is for idle, and 1 for running + */ uint32_t status; - uint32_t address; + /** + * Below is DMA source address register address: + * - here source address is where FROM the DMA should transfer the data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any peripheral or accelerator) + */ + uint32_t sourceAddress; + /** + * Below is DMA target address register address: + * - here target address is where TO the DMA should transfer the data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any peripheral or accelerator) + */ + uint32_t targetAddress; + /** + * Below is the amount of data IN BYTES that DMA should transfer + * between the source and a target + */ uint32_t lengthInBytes; - }; - - struct DMAInterface - { /** - * these two streaming interfaces are for - * [0] memory to target and - * [1] target to memory directions. - * target here is any I/O device, peripheral or an accelerator + * Below is the amount of strides IN 4 BYTE WORDS for a source address. + * Strides is the jump amount between each data retrieval address + * during the DMA operation. + * Minimum width of the stride is 4 byte words. + * - 0 stride is default and points to the address of the next word + * - 1 is for 1 word skippal, and 2 for 2 before the next address + * - todo: more fine grain stride configurability (in term of bytes) for the future? */ - volatile StreamingInterface *streamingInterface[2]; + uint32_t sourceStrides; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a target address. + * The same as above but just for a target address. So that we can fetch + * and transfer the data at different stride rates + */ + uint32_t targetStrides; }; volatile DMAInterface *dmaInterface; @@ -48,71 +64,77 @@ namespace Ibex { void init() { - dmaInterface->streamingInterface[0] = MMIO_CAPABILITY(StreamingInterface, dma_to_target); - dmaInterface->streamingInterface[1] = MMIO_CAPABILITY(StreamingInterface, dma_to_memory); + dmaInterface = MMIO_CAPABILITY(DMAInterface, dma); } - uint32_t read_status(bool index) + uint32_t read_status() { /** * this statement returns the less signifance bit * to show the halted status */ - return dmaInterface->streamingInterface[index]->status & 0x1; + return dmaInterface->status & 0x1; } - void write_conf(bool index, uint32_t *address, uint32_t lengthInBytes) + void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) { /** - * filling address and length fields + * filling source and target addresses, and length fields */ - dmaInterface->streamingInterface[index]->address = *address; - dmaInterface->streamingInterface[index]->lengthInBytes = lengthInBytes; + dmaInterface->sourceAddress = sourceAddress; + dmaInterface->targetAddress = targetAddress; + dmaInterface->lengthInBytes = lengthInBytes; + + } + void write_strides(uint32_t sourceStrides, uint32_t targetStrides) + { /** - * create a heap capability for dma below. - * todo: we assume that this heap capability can serve - * for different compartments, irrespective of - * the origin because this is a driver code? + * filling source and target strides */ - dmaHeap = STATIC_SEALED_VALUE(dmaDriverHeap); + dmaInterface->sourceStrides = sourceStrides; + dmaInterface->targetStrides = targetStrides; + } + int byte_swap_en(uint32_t swapAmount) + { /** - * claim the allocated memory + * filling byte swaps */ - int claimCount = 0; - - auto claim = [&]() { - size_t claimSize = heap_claim(dmaHeap, address); - claimCount++; - TEST(claimSize == lengthInBytes, - "{}-byte allocation claimed as {} bytes (claim number {})", - lengthInBytes, - claimSize, - claimCount); - }; - - claim(); + if (swapAmount == 2) + { + dmaInterface->control = 0x2; + } else if (swapAmount == 4) + { + dmaInterface->control = 0x4; + } else + { + return -1; + } + + return 0; } - void start_dma(bool index) + void start_dma() { /** * setting a start bit */ - dmaInterface->streamingInterface[index]->control = 0x1; + dmaInterface->control = 0x1; } - void reset_dma(bool index, uint32_t *address) + void reset_dma() { /** - * setting a reset bit + * setting a reset bit, which is bit 3 */ - dmaInterface->streamingInterface[index]->control = 0x4; + dmaInterface->control = 0x8; - int ret = heap_free(dmaHeap, address); } }; -} // namespace Ibex \ No newline at end of file +} // namespace Ibex + +template +using PlatformDMA = Ibex::PlatformDMA; \ No newline at end of file From 20f323fd16727e39f8d18c56d696a2e3b43c33e9 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 17 Jul 2023 11:19:30 +0100 Subject: [PATCH 03/42] Update sdk/core/dma/dma.h Co-authored-by: Nathaniel Filardo <105816689+nwf-msr@users.noreply.github.com> --- sdk/core/dma/dma.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 59a21e74..917c065c 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -44,7 +44,7 @@ namespace DMA PlatformDMA::init(); } - void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) + void write_conf(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes) { /** * Claim the DMA-able addresses first. From 6294ef0ce45130515132040ee8b318a8471cb413 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 17 Jul 2023 11:19:43 +0100 Subject: [PATCH 04/42] Update sdk/core/dma/dma.h Co-authored-by: Nathaniel Filardo <105816689+nwf-msr@users.noreply.github.com> --- sdk/core/dma/dma.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 917c065c..531d1bc1 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -14,17 +14,17 @@ namespace DMA { template - concept IsDmaDevice = requires(T device) + concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *destinationAddress, size_t length) { {device.init()}; { - device.write_conf() + device.write_conf(sourceAddress, destinationAddress, length) } -> std::same_as; { device.start_dma() } -> std::same_as; { - device.reset_dma() + device.reset_dma(sourceAddress, destinationAddress) } -> std::same_as; }; From 78f1638f4fb67c0f3c78485a4ce1023e70c3ebbb Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 17 Jul 2023 19:22:58 +0100 Subject: [PATCH 05/42] updated software stack with stronger compartment support --- sdk/core/dma/dma.h | 44 ++++------------- sdk/core/dma/dma_compartment.cc | 57 +++++++++++++++++------ sdk/core/dma/dma_compartment.h | 10 +--- sdk/include/platform/ibex/platform-dma.hh | 42 ++++++++--------- 4 files changed, 77 insertions(+), 76 deletions(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 531d1bc1..4d8ab89c 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -14,11 +14,12 @@ namespace DMA { template - concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *destinationAddress, size_t length) + concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *targetAddress, size_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { {device.init()}; { - device.write_conf(sourceAddress, destinationAddress, length) + device.write_conf(sourceAddress, destinationAddress, lengthInBytes) } -> std::same_as; { device.start_dma() @@ -35,40 +36,15 @@ namespace DMA { public: - /** - * Initialise a DMA device. - */ - - void init() - { - PlatformDMA::init(); - } - - void write_conf(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes) - { - /** - * Claim the DMA-able addresses first. - * If successful, then call an original - * dma write configuration function - */ - - int claimStatus = claim_dma(sourceAddress, targetAddress); - - if (claimStatus == 0) - { - PlatformDMA::write_conf(sourceAddress, targetAddress, lengthInBytes); - } - - } - - void reset_dma(uint32_t sourceAddress, uint32_t targetAddress) + int configure_and_launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - free_dma(sourceAddress, targetAddress); - - PlatformDMA::reset_dma(); + return dma_compartment(sourceAddress, targetAddress, lengthInBytes, + sourceStrides, targetStrides, byteSwapAmount) } }; - using DMA = GenericDMA; -} \ No newline at end of file + using DMA = GenericDMA; +} + diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 0f100808..1d905a91 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -5,9 +5,12 @@ #include #include #include - #include +#if __has_include() +# include +#endif + using thread_pool::async; /** * creating a global heap below. @@ -19,8 +22,33 @@ DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); SObjStruct *dmaHeap; -int claim_dma(uint32_t *sourceAddress, uint32_t *targetAddress) +Ibex::PlatformDMA platformDma; + +void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) +{ + int sourceStatus = heap_free(dmaHeap, sourceAddress); + int targetStatus = heap_free(dmaHeap, targetAddress); + + /** + * ideally, free should not fail if claim was successful + * todo: but in case, assert a Debug::Assert() for heap_free() later + */ +} + +int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { + /** + * return if sufficient permissions are not present + * and if not long enough + */ + + if (!check_pointer(sourceAddress, lengthInBytes) || + !check_pointer(targetAddress, lengthInBytes) ) + { + return -1; + } + /** * create a heap capability for dma below. */ @@ -56,20 +84,23 @@ int claim_dma(uint32_t *sourceAddress, uint32_t *targetAddress) return -1; } + platformDma::write_strides(sourceStrides, targetStrides); + platformDma::byte_swap_en(byteSwapAmount); + + platformDma::start_dma(); + + // todo: need to check for status via polling maybe from here + // or for the mvp, we can just check or cancel after some timeout + + // todo: eventually we need some interrupt support and the futex call here + + free_dma(sourceAddress, targetAddress); + + platformDma::reset_dma(); + /** * return here, if both claims are successful */ return 0; } - -void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) -{ - int sourceStatus = heap_free(dmaHeap, sourceAddress); - int targetStatus = heap_free(dmaHeap, targetAddress); - - /** - * ideally, free should not fail if claim was successful - * todo: but in case, assert a Debug::Assert() for heap_free() later - */ -} \ No newline at end of file diff --git a/sdk/core/dma/dma_compartment.h b/sdk/core/dma/dma_compartment.h index d79afb22..3257e668 100644 --- a/sdk/core/dma/dma_compartment.h +++ b/sdk/core/dma/dma_compartment.h @@ -7,12 +7,6 @@ * A function below claims the source and target addresses of the DMA interface. * While, DMA is in progress, these addresses should not be freed */ -__cheri_compartment("dma") int claim_dma(uint32_t *sourceAddress, - uint32_t *targetAddress); +__cheri_compartment("dma") int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); -/** - * The function below frees the source and target addresses of the DMA interface. - * It should be called once the DMA operation is completed. - */ -__cheri_compartment("dma") void free_dma(uint32_t *sourceAddress, - uint32_t *targetAddress); \ No newline at end of file diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/include/platform/ibex/platform-dma.hh index 87d24743..343cc09f 100644 --- a/sdk/include/platform/ibex/platform-dma.hh +++ b/sdk/include/platform/ibex/platform-dma.hh @@ -58,56 +58,54 @@ namespace Ibex { uint32_t targetStrides; }; - volatile DMAInterface *dmaInterface; - - public: - - void init() + __always_inline volatile DMAInterface &device() { - dmaInterface = MMIO_CAPABILITY(DMAInterface, dma); + return *MMIO_CAPABILITY(DMAInterface, dma); } + public: + uint32_t read_status() { /** * this statement returns the less signifance bit * to show the halted status */ - return dmaInterface->status & 0x1; + return device().status & 0x1; } void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) { /** - * filling source and target addresses, and length fields + * Setting source and target addresses, and length fields */ - dmaInterface->sourceAddress = sourceAddress; - dmaInterface->targetAddress = targetAddress; - dmaInterface->lengthInBytes = lengthInBytes; + device().sourceAddress = sourceAddress; + device().targetAddress = targetAddress; + device().lengthInBytes = lengthInBytes; } void write_strides(uint32_t sourceStrides, uint32_t targetStrides) { /** - * filling source and target strides + * Setting source and target strides */ - dmaInterface->sourceStrides = sourceStrides; - dmaInterface->targetStrides = targetStrides; + device().sourceStrides = sourceStrides; + device().targetStrides = targetStrides; } int byte_swap_en(uint32_t swapAmount) { /** - * filling byte swaps + * Setting byte swaps */ if (swapAmount == 2) { - dmaInterface->control = 0x2; + device().control = 0x2; } else if (swapAmount == 4) { - dmaInterface->control = 0x4; + device().control = 0x4; } else { return -1; @@ -119,17 +117,19 @@ namespace Ibex { void start_dma() { /** - * setting a start bit + * Setting a start bit */ - dmaInterface->control = 0x1; + device().control = 0x1; } void reset_dma() { /** - * setting a reset bit, which is bit 3 + * Setting a reset bit, which is bit 3. + * this clears all the registers, + * but do not transfer the current transfer status anywhere */ - dmaInterface->control = 0x8; + device().control = 0x8; } From ead572bbb99045110e2324c9313458517d1b5a0f Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Tue, 18 Jul 2023 11:45:43 +0100 Subject: [PATCH 06/42] fixed some errors and added a test example --- examples/10.dma_test/dma_test.c | 13 +++++++++++-- sdk/core/dma/dma.h | 22 +++++++++++----------- sdk/core/dma/dma_compartment.cc | 7 +++++-- sdk/include/platform/ibex/platform-dma.hh | 8 ++++---- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/examples/10.dma_test/dma_test.c b/examples/10.dma_test/dma_test.c index 7a3ad255..1e7d94e3 100644 --- a/examples/10.dma_test/dma_test.c +++ b/examples/10.dma_test/dma_test.c @@ -2,9 +2,13 @@ // SPDX-License-Identifier: MIT #include +#include +#include #include +#include + #include -#include +#include <../core/dma/dma.h> /// Expose debugging features unconditionally for this compartment. using Debug = ConditionalDebug; @@ -12,8 +16,13 @@ using Debug = ConditionalDebug; /// Thread entry point. void __cheri_compartment("dma") dma_request() { - // Print hello world, along with the compartment's name to the default UART. Debug::log("DMA request app entered!"); + // This is a dma process between two different memory addresses + uint32_t *sourceAddress =(uint32_t*) malloc(1024); + uint32_t *targetAddress =(uint32_t*) malloc(1024); + DMA::Device dmaDevice; + + dmaDevice.configure_and_launch(sourceAddress, targetAddress, 1024, 0, 0, 0); } diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 4d8ab89c..cc2d38ce 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -1,9 +1,9 @@ #pragma once -#include "dma_compartment.cc" #include "dma_compartment.h" #include "platform/ibex/platform-dma.hh" #include +#include #include #include @@ -17,34 +17,34 @@ namespace DMA concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *targetAddress, size_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - {device.init()}; { - device.write_conf(sourceAddress, destinationAddress, lengthInBytes) + device.write_conf(sourceAddress, targetAddress, lengthInBytes) } -> std::same_as; { device.start_dma() } -> std::same_as; { - device.reset_dma(sourceAddress, destinationAddress) + device.reset_dma() } -> std::same_as; }; - template - typename PlatformDMA> - requires IsDmaDevice> - class GenericDMA : public PlatformDMA + template + + requires IsDmaDevice + + class GenericDMA : public PlatformDMA { public: - int configure_and_launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + int configure_and_launch(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { return dma_compartment(sourceAddress, targetAddress, lengthInBytes, - sourceStrides, targetStrides, byteSwapAmount) + sourceStrides, targetStrides, byteSwapAmount); } }; - using DMA = GenericDMA; + using Device = GenericDMA; } diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 1d905a91..f1ad6f00 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -11,6 +11,9 @@ # include #endif +// Import some useful things from the CHERI namespace. +using namespace CHERI; + using thread_pool::async; /** * creating a global heap below. @@ -43,8 +46,8 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l * and if not long enough */ - if (!check_pointer(sourceAddress, lengthInBytes) || - !check_pointer(targetAddress, lengthInBytes) ) + if (!check_pointer(sourceAddress, lengthInBytes) || + !check_pointer(targetAddress, lengthInBytes) ) { return -1; } diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/include/platform/ibex/platform-dma.hh index 343cc09f..af119d72 100644 --- a/sdk/include/platform/ibex/platform-dma.hh +++ b/sdk/include/platform/ibex/platform-dma.hh @@ -74,13 +74,13 @@ namespace Ibex { return device().status & 0x1; } - void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) + void write_conf(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes) { /** * Setting source and target addresses, and length fields */ - device().sourceAddress = sourceAddress; - device().targetAddress = targetAddress; + device().sourceAddress = *sourceAddress; + device().targetAddress = *targetAddress; device().lengthInBytes = lengthInBytes; } @@ -136,5 +136,5 @@ namespace Ibex { }; } // namespace Ibex -template +// template using PlatformDMA = Ibex::PlatformDMA; \ No newline at end of file From 87f3e96937db3659a02883d225ad25d774bad23a Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 19 Jul 2023 16:32:53 +0100 Subject: [PATCH 07/42] fixed some logic and cheri mistakes, third iteration --- sdk/core/dma/dma.h | 14 +--- sdk/core/dma/dma_compartment.cc | 67 +++++++------------ sdk/core/dma/dma_compartment.h | 3 +- .../ibex => core/dma}/platform-dma.hh | 51 +++++++------- 4 files changed, 58 insertions(+), 77 deletions(-) rename sdk/{include/platform/ibex => core/dma}/platform-dma.hh (88%) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index cc2d38ce..a2e74d8f 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -1,16 +1,10 @@ -#pragma once - #include "dma_compartment.h" -#include "platform/ibex/platform-dma.hh" +#include "platform-dma.hh" #include #include #include #include -#if __has_include() -# include -#endif - namespace DMA { template @@ -18,10 +12,8 @@ namespace DMA uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { { - device.write_conf(sourceAddress, targetAddress, lengthInBytes) - } -> std::same_as; - { - device.start_dma() + device.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, + sourceStrides, targetStrides, byteSwapAmount) } -> std::same_as; { device.reset_dma() diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index f1ad6f00..a3fc43d7 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -5,32 +5,17 @@ #include #include #include -#include - -#if __has_include() -# include -#endif +#include "platform-dma.hh" // Import some useful things from the CHERI namespace. using namespace CHERI; -using thread_pool::async; -/** - * creating a global heap below. - * todo: we assume that this heap capability can serve - * for different compartments, irrespective of - * the origin because this is a driver code? -*/ -DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); - -SObjStruct *dmaHeap; - Ibex::PlatformDMA platformDma; void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) { - int sourceStatus = heap_free(dmaHeap, sourceAddress); - int targetStatus = heap_free(dmaHeap, targetAddress); + int sourceStatus = heap_free(MALLOC_CAPABILITY, sourceAddress); + int targetStatus = heap_free(MALLOC_CAPABILITY, targetAddress); /** * ideally, free should not fail if claim was successful @@ -41,27 +26,14 @@ void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - /** - * return if sufficient permissions are not present - * and if not long enough - */ - - if (!check_pointer(sourceAddress, lengthInBytes) || - !check_pointer(targetAddress, lengthInBytes) ) - { - return -1; - } /** - * create a heap capability for dma below. - */ - dmaHeap = STATIC_SEALED_VALUE(dmaDriverHeap); - - /** - * claim the memory + * claim the memory with default malloc capability, + * as declaring another heap capability is an extra entry + * that leaves the default capability let unused */ - size_t sourceClaimSize = heap_claim(dmaHeap, sourceAddress); + size_t sourceClaimSize = heap_claim(MALLOC_CAPABILITY, sourceAddress); /** * return with failure if @@ -73,7 +45,7 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l return -1; } - size_t targetClaimSize = heap_claim(dmaHeap, targetAddress); + size_t targetClaimSize = heap_claim(MALLOC_CAPABILITY, targetAddress); if (targetClaimSize == 0) { @@ -82,24 +54,35 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l * then free the first claim and exit the function * todo: assert a Debug::Assert() for heap_free() result later */ - int sourceStatus = heap_free(dmaHeap, sourceAddress); + int sourceStatus = heap_free(MALLOC_CAPABILITY, sourceAddress); return -1; } - platformDma::write_strides(sourceStrides, targetStrides); - platformDma::byte_swap_en(byteSwapAmount); - - platformDma::start_dma(); + /** + * return if sufficient permissions are not present + * and if not long enough + */ + + if (!check_pointer(sourceAddress, lengthInBytes) || + !check_pointer(targetAddress, lengthInBytes) ) + { + return -1; + } + + platformDma.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, + sourceStrides, targetStrides, byteSwapAmount); // todo: need to check for status via polling maybe from here // or for the mvp, we can just check or cancel after some timeout // todo: eventually we need some interrupt support and the futex call here + // todo: eventually, we wanna separate this free and reset dma + // logics from the start dma as well free_dma(sourceAddress, targetAddress); - platformDma::reset_dma(); + platformDma.reset_dma(); /** * return here, if both claims are successful diff --git a/sdk/core/dma/dma_compartment.h b/sdk/core/dma/dma_compartment.h index 3257e668..3f4865fc 100644 --- a/sdk/core/dma/dma_compartment.h +++ b/sdk/core/dma/dma_compartment.h @@ -5,7 +5,8 @@ /** * A function below claims the source and target addresses of the DMA interface. - * While, DMA is in progress, these addresses should not be freed + * While, DMA is in progress, these addresses will be claimed by the DMA compartment + * and so the memory will not be freed. */ __cheri_compartment("dma") int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/core/dma/platform-dma.hh similarity index 88% rename from sdk/include/platform/ibex/platform-dma.hh rename to sdk/core/dma/platform-dma.hh index af119d72..6b9e4c3f 100644 --- a/sdk/include/platform/ibex/platform-dma.hh +++ b/sdk/core/dma/platform-dma.hh @@ -3,6 +3,7 @@ #include #include #include +#include namespace Ibex { class PlatformDMA { @@ -63,28 +64,6 @@ namespace Ibex { return *MMIO_CAPABILITY(DMAInterface, dma); } - public: - - uint32_t read_status() - { - /** - * this statement returns the less signifance bit - * to show the halted status - */ - return device().status & 0x1; - } - - void write_conf(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes) - { - /** - * Setting source and target addresses, and length fields - */ - device().sourceAddress = *sourceAddress; - device().targetAddress = *targetAddress; - device().lengthInBytes = lengthInBytes; - - } - void write_strides(uint32_t sourceStrides, uint32_t targetStrides) { /** @@ -94,7 +73,7 @@ namespace Ibex { device().targetStrides = targetStrides; } - int byte_swap_en(uint32_t swapAmount) + int enable_byte_swap(uint32_t swapAmount) { /** * Setting byte swaps @@ -122,6 +101,32 @@ namespace Ibex { device().control = 0x1; } + public: + + uint32_t read_status() + { + /** + * this statement returns the less signifance bit + * to show the halted status + */ + return device().status & 0x1; + } + + void write_conf_and_start(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) + { + /** + * Setting source and target addresses, and length fields + */ + device().sourceAddress = CHERI::Capability{sourceAddress}.address(); + device().targetAddress = CHERI::Capability{targetAddress}.address(); + device().lengthInBytes = lengthInBytes; + + write_strides(sourceStrides, targetStrides); + enable_byte_swap(byteSwapAmount); + start_dma(); + } + void reset_dma() { /** From c565efd60bc3408930997de3ce9afd1bdde6ff61 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 19 Jul 2023 17:13:53 +0100 Subject: [PATCH 08/42] add the unique pointer support to the compartment --- sdk/core/dma/dma_compartment.cc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index a3fc43d7..82a7f7db 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -1,5 +1,7 @@ #pragma once +#include +#include #define MALLOC_QUOTA 0x100000 #include @@ -30,10 +32,16 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l /** * claim the memory with default malloc capability, * as declaring another heap capability is an extra entry - * that leaves the default capability let unused + * that leaves the default capability let unused. + * + * unique pointers are used to avoid explicit heap_free() calls. + * when this pointer is not used anymore, it is automatically deleted */ - size_t sourceClaimSize = heap_claim(MALLOC_CAPABILITY, sourceAddress); + std::unique_ptr uniqueSourceAddress(sourceAddress); + std::unique_ptr uniqueTargetAddress(targetAddress); + + size_t sourceClaimSize = heap_claim(MALLOC_CAPABILITY, static_cast(uniqueSourceAddress.get())); /** * return with failure if @@ -45,7 +53,7 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l return -1; } - size_t targetClaimSize = heap_claim(MALLOC_CAPABILITY, targetAddress); + size_t targetClaimSize = heap_claim(MALLOC_CAPABILITY, static_cast(uniqueTargetAddress.get())); if (targetClaimSize == 0) { @@ -80,7 +88,7 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l // todo: eventually, we wanna separate this free and reset dma // logics from the start dma as well - free_dma(sourceAddress, targetAddress); + // free_dma(sourceAddress, targetAddress); platformDma.reset_dma(); From e8cce574c475a3f5f04bf76264d5fa4bd394a77e Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 26 Jul 2023 15:47:38 +0100 Subject: [PATCH 09/42] uploading the json file for a check --- ibex.json | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 ibex.json diff --git a/ibex.json b/ibex.json new file mode 100644 index 00000000..288ba597 --- /dev/null +++ b/ibex.json @@ -0,0 +1,71 @@ +{ + "devices" : + { + "hspTimer" : { + "start" : 0x8f000800, + "length" : 0x20 + }, + "hspInteruptController" : { + "start" : 0x8f000600, + "length" : 0x14 + }, + "uart" : { + "start" : 0x8f00b000, + "length": 0x100 + }, + "shadow" : { + "start" : 0x200f0000, + "end" : 0x200f1800 + }, + "revoker" : { + "start" : 0x8f000000, + "length": 0xc + }, + "dmb" : { + "start" : 0x8f0f0000, + "length": 0x400 + }, + "ethernet" : { + "start" : 0x98000000, + "length": 0x204 + }, + "LEDs" : { + "start" : 0x8f00 f004, + "end": 0x2020_0040 + }, + "dma" : { + "start" : 0x2020_0000, + "end" : 0x2020_0040 + } + }, + "instruction_memory" : { + "start" : 0x20000080, + "end" : 0x20060000 + }, + "heap" : { + "end" : 0x20060000 + }, + "revokable_memory_start" : 0x20000000, + "interrupts": [ + { + "name": "Ethernet", + "number": 16, + "priority": 0 + } + ], + "defines" : [ + "IBEX", + "IBEX_SHADOW_BASE=0x200f0000U", + "IBEX_SHADOW_SIZE=0x1800U" + ], + "driver_includes" : [ + "../include/", + "${sdk}/include/platform/ibex", + "${sdk}/include/platform/generic-riscv" + ], + "timer_hz" : 20000000, + "tickrate_hz" : 100, + "stack_high_water_mark" : true, + "revoker" : "hardware", + "simulator" : "${board}/../scripts/deploy-ibex-fpga.sh" +} From 3d11a587d8c4b9f8d0723195f2ef4f31255a358d Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 26 Jul 2023 18:16:16 +0100 Subject: [PATCH 10/42] fixing naming mistakes --- examples/10.dma_test/{dma_test.c => dma_test.cc} | 2 +- examples/10.dma_test/xmake.lua | 6 +++--- sdk/core/dma/dma_compartment.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename examples/10.dma_test/{dma_test.c => dma_test.cc} (93%) diff --git a/examples/10.dma_test/dma_test.c b/examples/10.dma_test/dma_test.cc similarity index 93% rename from examples/10.dma_test/dma_test.c rename to examples/10.dma_test/dma_test.cc index 1e7d94e3..63df6407 100644 --- a/examples/10.dma_test/dma_test.c +++ b/examples/10.dma_test/dma_test.cc @@ -14,7 +14,7 @@ using Debug = ConditionalDebug; /// Thread entry point. -void __cheri_compartment("dma") dma_request() +void __cheri_compartment("dma-app") dma_request() { Debug::log("DMA request app entered!"); diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua index a5cb41f4..40dab2c6 100644 --- a/examples/10.dma_test/xmake.lua +++ b/examples/10.dma_test/xmake.lua @@ -14,18 +14,18 @@ includes(path.join(sdkdir, "lib/freestanding"), option("board") set_default("sail") -compartment("dma") +compartment("dma-app") add_files("dma_test.cc") -- Firmware image for the example. firmware("dma_test") add_deps("crt", "freestanding", "atomic_fixed") - add_deps("dma") + add_deps("dma-app") on_load(function(target) target:values_set("board", "$(board)") target:values_set("threads", { { - compartment = "dma", + compartment = "dma-app", priority = 1, entry_point = "dma_request", stack_size = 0x200, diff --git a/sdk/core/dma/dma_compartment.h b/sdk/core/dma/dma_compartment.h index 3f4865fc..8aea3e5b 100644 --- a/sdk/core/dma/dma_compartment.h +++ b/sdk/core/dma/dma_compartment.h @@ -8,6 +8,6 @@ * While, DMA is in progress, these addresses will be claimed by the DMA compartment * and so the memory will not be freed. */ -__cheri_compartment("dma") int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, +int __cheri_compartment("dma") dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); From d73d85d29678d4ede79a274fd419ea13de0c176e Mon Sep 17 00:00:00 2001 From: Nazerke Date: Tue, 11 Jul 2023 14:48:45 +0000 Subject: [PATCH 11/42] initial software driver for dma --- sdk/include/platform/ibex/platform-dma.hh | 118 ++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 sdk/include/platform/ibex/platform-dma.hh diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/include/platform/ibex/platform-dma.hh new file mode 100644 index 00000000..80fc52b2 --- /dev/null +++ b/sdk/include/platform/ibex/platform-dma.hh @@ -0,0 +1,118 @@ +#pragma once + +#define MALLOC_QUOTA 0x100000 +#define TEST_NAME "Allocator" +#include "tests.hh" + +#include +#include +#include +#include +#include + +#include +#include + +using thread_pool::async; +DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); + +namespace Ibex { + class GenericDMA { + private: + + Timeout noWait{0}; + SObjStruct *dmaHeap; + + struct StreamingInterface + { + uint32_t control; + uint32_t status; + uint32_t address; + uint32_t lengthInBytes; + }; + + struct DMAInterface + { + /** + * these two streaming interfaces are for + * [0] memory to target and + * [1] target to memory directions. + * target here is any I/O device, peripheral or an accelerator + */ + volatile StreamingInterface *streamingInterface[2]; + }; + + volatile DMAInterface *dmaInterface; + + public: + + void init() + { + dmaInterface->streamingInterface[0] = MMIO_CAPABILITY(StreamingInterface, dma_to_target); + dmaInterface->streamingInterface[1] = MMIO_CAPABILITY(StreamingInterface, dma_to_memory); + } + + uint32_t read_status(bool index) + { + /** + * this statement returns the less signifance bit + * to show the halted status + */ + return dmaInterface->streamingInterface[index]->status & 0x1; + } + + void write_conf(bool index, uint32_t *address, uint32_t lengthInBytes) + { + /** + * filling address and length fields + */ + dmaInterface->streamingInterface[index]->address = *address; + dmaInterface->streamingInterface[index]->lengthInBytes = lengthInBytes; + + /** + * create a heap capability for dma below. + * todo: we assume that this heap capability can serve + * for different compartments, irrespective of + * the origin because this is a driver code? + */ + dmaHeap = STATIC_SEALED_VALUE(dmaDriverHeap); + + /** + * claim the allocated memory + */ + + int claimCount = 0; + + auto claim = [&]() { + size_t claimSize = heap_claim(dmaHeap, address); + claimCount++; + TEST(claimSize == lengthInBytes, + "{}-byte allocation claimed as {} bytes (claim number {})", + lengthInBytes, + claimSize, + claimCount); + }; + + claim(); + } + + void start_dma(bool index) + { + /** + * setting a start bit + */ + dmaInterface->streamingInterface[index]->control = 0x1; + } + + void reset_dma(bool index, uint32_t *address) + { + /** + * setting a reset bit + */ + dmaInterface->streamingInterface[index]->control = 0x4; + + int ret = heap_free(dmaHeap, address); + } + + }; +} // namespace Ibex \ No newline at end of file From 273cc508a9e2437bd51b1dc1870a45c6b1798782 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Fri, 14 Jul 2023 19:16:48 +0100 Subject: [PATCH 12/42] initial software stack of the dma: subject to change later, but the base should be similar --- examples/10.dma_test/README.md | 4 + examples/10.dma_test/dma_test.c | 19 +++ examples/10.dma_test/xmake.lua | 35 ++++++ sdk/core/dma/dma.h | 74 +++++++++++ sdk/core/dma/dma_compartment.cc | 75 +++++++++++ sdk/core/dma/dma_compartment.h | 18 +++ sdk/include/platform/ibex/platform-dma.hh | 146 +++++++++++++--------- 7 files changed, 309 insertions(+), 62 deletions(-) create mode 100644 examples/10.dma_test/README.md create mode 100644 examples/10.dma_test/dma_test.c create mode 100644 examples/10.dma_test/xmake.lua create mode 100644 sdk/core/dma/dma.h create mode 100644 sdk/core/dma/dma_compartment.cc create mode 100644 sdk/core/dma/dma_compartment.h diff --git a/examples/10.dma_test/README.md b/examples/10.dma_test/README.md new file mode 100644 index 00000000..d44b51a5 --- /dev/null +++ b/examples/10.dma_test/README.md @@ -0,0 +1,4 @@ +DMA example +=================== + + diff --git a/examples/10.dma_test/dma_test.c b/examples/10.dma_test/dma_test.c new file mode 100644 index 00000000..7a3ad255 --- /dev/null +++ b/examples/10.dma_test/dma_test.c @@ -0,0 +1,19 @@ +// Copyright Microsoft and CHERIoT Contributors. +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include + +/// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +/// Thread entry point. +void __cheri_compartment("dma") dma_request() +{ + // Print hello world, along with the compartment's name to the default UART. + Debug::log("DMA request app entered!"); + + +} diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua new file mode 100644 index 00000000..a5cb41f4 --- /dev/null +++ b/examples/10.dma_test/xmake.lua @@ -0,0 +1,35 @@ +-- Copyright Microsoft and CHERIoT Contributors. +-- SPDX-License-Identifier: MIT + +set_project("CHERIoT Simple DMA App") +sdkdir = "../../sdk" +includes(sdkdir) +set_toolchains("cheriot-clang") + +-- Support libraries +includes(path.join(sdkdir, "lib/freestanding"), + path.join(sdkdir, "lib/atomic"), + path.join(sdkdir, "lib/crt")) + +option("board") + set_default("sail") + +compartment("dma") + add_files("dma_test.cc") + +-- Firmware image for the example. +firmware("dma_test") + add_deps("crt", "freestanding", "atomic_fixed") + add_deps("dma") + on_load(function(target) + target:values_set("board", "$(board)") + target:values_set("threads", { + { + compartment = "dma", + priority = 1, + entry_point = "dma_request", + stack_size = 0x200, + trusted_stack_frames = 1 + } + }, {expand = false}) + end) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h new file mode 100644 index 00000000..59a21e74 --- /dev/null +++ b/sdk/core/dma/dma.h @@ -0,0 +1,74 @@ +#pragma once + +#include "dma_compartment.cc" +#include "dma_compartment.h" +#include "platform/ibex/platform-dma.hh" +#include +#include +#include + +#if __has_include() +# include +#endif + +namespace DMA +{ + template + concept IsDmaDevice = requires(T device) + { + {device.init()}; + { + device.write_conf() + } -> std::same_as; + { + device.start_dma() + } -> std::same_as; + { + device.reset_dma() + } -> std::same_as; + }; + + template + typename PlatformDMA> + requires IsDmaDevice> + class GenericDMA : public PlatformDMA + { + public: + + /** + * Initialise a DMA device. + */ + + void init() + { + PlatformDMA::init(); + } + + void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) + { + /** + * Claim the DMA-able addresses first. + * If successful, then call an original + * dma write configuration function + */ + + int claimStatus = claim_dma(sourceAddress, targetAddress); + + if (claimStatus == 0) + { + PlatformDMA::write_conf(sourceAddress, targetAddress, lengthInBytes); + } + + } + + void reset_dma(uint32_t sourceAddress, uint32_t targetAddress) + { + free_dma(sourceAddress, targetAddress); + + PlatformDMA::reset_dma(); + } + + }; + + using DMA = GenericDMA; +} \ No newline at end of file diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc new file mode 100644 index 00000000..0f100808 --- /dev/null +++ b/sdk/core/dma/dma_compartment.cc @@ -0,0 +1,75 @@ +#pragma once + +#define MALLOC_QUOTA 0x100000 + +#include +#include +#include + +#include + +using thread_pool::async; +/** + * creating a global heap below. + * todo: we assume that this heap capability can serve + * for different compartments, irrespective of + * the origin because this is a driver code? +*/ +DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); + +SObjStruct *dmaHeap; + +int claim_dma(uint32_t *sourceAddress, uint32_t *targetAddress) +{ + /** + * create a heap capability for dma below. + */ + dmaHeap = STATIC_SEALED_VALUE(dmaDriverHeap); + + /** + * claim the memory + */ + + size_t sourceClaimSize = heap_claim(dmaHeap, sourceAddress); + + /** + * return with failure if + * no claim is available + */ + + if (sourceClaimSize == 0) + { + return -1; + } + + size_t targetClaimSize = heap_claim(dmaHeap, targetAddress); + + if (targetClaimSize == 0) + { + /** + * if the first claim was successful, but this one failed, + * then free the first claim and exit the function + * todo: assert a Debug::Assert() for heap_free() result later + */ + int sourceStatus = heap_free(dmaHeap, sourceAddress); + + return -1; + } + + /** + * return here, if both claims are successful + */ + return 0; + +} + +void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) +{ + int sourceStatus = heap_free(dmaHeap, sourceAddress); + int targetStatus = heap_free(dmaHeap, targetAddress); + + /** + * ideally, free should not fail if claim was successful + * todo: but in case, assert a Debug::Assert() for heap_free() later + */ +} \ No newline at end of file diff --git a/sdk/core/dma/dma_compartment.h b/sdk/core/dma/dma_compartment.h new file mode 100644 index 00000000..d79afb22 --- /dev/null +++ b/sdk/core/dma/dma_compartment.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +/** + * A function below claims the source and target addresses of the DMA interface. + * While, DMA is in progress, these addresses should not be freed + */ +__cheri_compartment("dma") int claim_dma(uint32_t *sourceAddress, + uint32_t *targetAddress); + +/** + * The function below frees the source and target addresses of the DMA interface. + * It should be called once the DMA operation is completed. + */ +__cheri_compartment("dma") void free_dma(uint32_t *sourceAddress, + uint32_t *targetAddress); \ No newline at end of file diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/include/platform/ibex/platform-dma.hh index 80fc52b2..87d24743 100644 --- a/sdk/include/platform/ibex/platform-dma.hh +++ b/sdk/include/platform/ibex/platform-dma.hh @@ -1,45 +1,61 @@ #pragma once -#define MALLOC_QUOTA 0x100000 -#define TEST_NAME "Allocator" -#include "tests.hh" - -#include #include -#include -#include +#include #include -#include -#include - -using thread_pool::async; -DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); - namespace Ibex { - class GenericDMA { + class PlatformDMA { private: - Timeout noWait{0}; - SObjStruct *dmaHeap; - - struct StreamingInterface + struct DMAInterface { + /** + * Below is DMA control register address: + * - includes start('b0), endianness conversion('b1 and 2) and reset bits('b3) + * - bit of index 1 and 2 are for enabling 2 and 4 byte swaps respectively + * - start bit is meant to be set at the end while programming + */ uint32_t control; + /** + * Below is DMA status register address + * - first bit refers to halted status + * - 0 is for idle, and 1 for running + */ uint32_t status; - uint32_t address; + /** + * Below is DMA source address register address: + * - here source address is where FROM the DMA should transfer the data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any peripheral or accelerator) + */ + uint32_t sourceAddress; + /** + * Below is DMA target address register address: + * - here target address is where TO the DMA should transfer the data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any peripheral or accelerator) + */ + uint32_t targetAddress; + /** + * Below is the amount of data IN BYTES that DMA should transfer + * between the source and a target + */ uint32_t lengthInBytes; - }; - - struct DMAInterface - { /** - * these two streaming interfaces are for - * [0] memory to target and - * [1] target to memory directions. - * target here is any I/O device, peripheral or an accelerator + * Below is the amount of strides IN 4 BYTE WORDS for a source address. + * Strides is the jump amount between each data retrieval address + * during the DMA operation. + * Minimum width of the stride is 4 byte words. + * - 0 stride is default and points to the address of the next word + * - 1 is for 1 word skippal, and 2 for 2 before the next address + * - todo: more fine grain stride configurability (in term of bytes) for the future? */ - volatile StreamingInterface *streamingInterface[2]; + uint32_t sourceStrides; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a target address. + * The same as above but just for a target address. So that we can fetch + * and transfer the data at different stride rates + */ + uint32_t targetStrides; }; volatile DMAInterface *dmaInterface; @@ -48,71 +64,77 @@ namespace Ibex { void init() { - dmaInterface->streamingInterface[0] = MMIO_CAPABILITY(StreamingInterface, dma_to_target); - dmaInterface->streamingInterface[1] = MMIO_CAPABILITY(StreamingInterface, dma_to_memory); + dmaInterface = MMIO_CAPABILITY(DMAInterface, dma); } - uint32_t read_status(bool index) + uint32_t read_status() { /** * this statement returns the less signifance bit * to show the halted status */ - return dmaInterface->streamingInterface[index]->status & 0x1; + return dmaInterface->status & 0x1; } - void write_conf(bool index, uint32_t *address, uint32_t lengthInBytes) + void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) { /** - * filling address and length fields + * filling source and target addresses, and length fields */ - dmaInterface->streamingInterface[index]->address = *address; - dmaInterface->streamingInterface[index]->lengthInBytes = lengthInBytes; + dmaInterface->sourceAddress = sourceAddress; + dmaInterface->targetAddress = targetAddress; + dmaInterface->lengthInBytes = lengthInBytes; + + } + void write_strides(uint32_t sourceStrides, uint32_t targetStrides) + { /** - * create a heap capability for dma below. - * todo: we assume that this heap capability can serve - * for different compartments, irrespective of - * the origin because this is a driver code? + * filling source and target strides */ - dmaHeap = STATIC_SEALED_VALUE(dmaDriverHeap); + dmaInterface->sourceStrides = sourceStrides; + dmaInterface->targetStrides = targetStrides; + } + int byte_swap_en(uint32_t swapAmount) + { /** - * claim the allocated memory + * filling byte swaps */ - int claimCount = 0; - - auto claim = [&]() { - size_t claimSize = heap_claim(dmaHeap, address); - claimCount++; - TEST(claimSize == lengthInBytes, - "{}-byte allocation claimed as {} bytes (claim number {})", - lengthInBytes, - claimSize, - claimCount); - }; - - claim(); + if (swapAmount == 2) + { + dmaInterface->control = 0x2; + } else if (swapAmount == 4) + { + dmaInterface->control = 0x4; + } else + { + return -1; + } + + return 0; } - void start_dma(bool index) + void start_dma() { /** * setting a start bit */ - dmaInterface->streamingInterface[index]->control = 0x1; + dmaInterface->control = 0x1; } - void reset_dma(bool index, uint32_t *address) + void reset_dma() { /** - * setting a reset bit + * setting a reset bit, which is bit 3 */ - dmaInterface->streamingInterface[index]->control = 0x4; + dmaInterface->control = 0x8; - int ret = heap_free(dmaHeap, address); } }; -} // namespace Ibex \ No newline at end of file +} // namespace Ibex + +template +using PlatformDMA = Ibex::PlatformDMA; \ No newline at end of file From ee461eb1239715564b87911ef0093f530838dc4d Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 17 Jul 2023 11:19:30 +0100 Subject: [PATCH 13/42] Update sdk/core/dma/dma.h Co-authored-by: Nathaniel Filardo <105816689+nwf-msr@users.noreply.github.com> --- sdk/core/dma/dma.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 59a21e74..917c065c 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -44,7 +44,7 @@ namespace DMA PlatformDMA::init(); } - void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) + void write_conf(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes) { /** * Claim the DMA-able addresses first. From e873b186994f681d9ba56e59f9d10fbc3bdce124 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 17 Jul 2023 11:19:43 +0100 Subject: [PATCH 14/42] Update sdk/core/dma/dma.h Co-authored-by: Nathaniel Filardo <105816689+nwf-msr@users.noreply.github.com> --- sdk/core/dma/dma.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 917c065c..531d1bc1 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -14,17 +14,17 @@ namespace DMA { template - concept IsDmaDevice = requires(T device) + concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *destinationAddress, size_t length) { {device.init()}; { - device.write_conf() + device.write_conf(sourceAddress, destinationAddress, length) } -> std::same_as; { device.start_dma() } -> std::same_as; { - device.reset_dma() + device.reset_dma(sourceAddress, destinationAddress) } -> std::same_as; }; From 87d8dd2567ea619bf2a15fd6432e85892886807d Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 17 Jul 2023 19:22:58 +0100 Subject: [PATCH 15/42] updated software stack with stronger compartment support --- sdk/core/dma/dma.h | 44 ++++------------- sdk/core/dma/dma_compartment.cc | 57 +++++++++++++++++------ sdk/core/dma/dma_compartment.h | 10 +--- sdk/include/platform/ibex/platform-dma.hh | 42 ++++++++--------- 4 files changed, 77 insertions(+), 76 deletions(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 531d1bc1..4d8ab89c 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -14,11 +14,12 @@ namespace DMA { template - concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *destinationAddress, size_t length) + concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *targetAddress, size_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { {device.init()}; { - device.write_conf(sourceAddress, destinationAddress, length) + device.write_conf(sourceAddress, destinationAddress, lengthInBytes) } -> std::same_as; { device.start_dma() @@ -35,40 +36,15 @@ namespace DMA { public: - /** - * Initialise a DMA device. - */ - - void init() - { - PlatformDMA::init(); - } - - void write_conf(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes) - { - /** - * Claim the DMA-able addresses first. - * If successful, then call an original - * dma write configuration function - */ - - int claimStatus = claim_dma(sourceAddress, targetAddress); - - if (claimStatus == 0) - { - PlatformDMA::write_conf(sourceAddress, targetAddress, lengthInBytes); - } - - } - - void reset_dma(uint32_t sourceAddress, uint32_t targetAddress) + int configure_and_launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - free_dma(sourceAddress, targetAddress); - - PlatformDMA::reset_dma(); + return dma_compartment(sourceAddress, targetAddress, lengthInBytes, + sourceStrides, targetStrides, byteSwapAmount) } }; - using DMA = GenericDMA; -} \ No newline at end of file + using DMA = GenericDMA; +} + diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 0f100808..1d905a91 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -5,9 +5,12 @@ #include #include #include - #include +#if __has_include() +# include +#endif + using thread_pool::async; /** * creating a global heap below. @@ -19,8 +22,33 @@ DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); SObjStruct *dmaHeap; -int claim_dma(uint32_t *sourceAddress, uint32_t *targetAddress) +Ibex::PlatformDMA platformDma; + +void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) +{ + int sourceStatus = heap_free(dmaHeap, sourceAddress); + int targetStatus = heap_free(dmaHeap, targetAddress); + + /** + * ideally, free should not fail if claim was successful + * todo: but in case, assert a Debug::Assert() for heap_free() later + */ +} + +int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { + /** + * return if sufficient permissions are not present + * and if not long enough + */ + + if (!check_pointer(sourceAddress, lengthInBytes) || + !check_pointer(targetAddress, lengthInBytes) ) + { + return -1; + } + /** * create a heap capability for dma below. */ @@ -56,20 +84,23 @@ int claim_dma(uint32_t *sourceAddress, uint32_t *targetAddress) return -1; } + platformDma::write_strides(sourceStrides, targetStrides); + platformDma::byte_swap_en(byteSwapAmount); + + platformDma::start_dma(); + + // todo: need to check for status via polling maybe from here + // or for the mvp, we can just check or cancel after some timeout + + // todo: eventually we need some interrupt support and the futex call here + + free_dma(sourceAddress, targetAddress); + + platformDma::reset_dma(); + /** * return here, if both claims are successful */ return 0; } - -void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) -{ - int sourceStatus = heap_free(dmaHeap, sourceAddress); - int targetStatus = heap_free(dmaHeap, targetAddress); - - /** - * ideally, free should not fail if claim was successful - * todo: but in case, assert a Debug::Assert() for heap_free() later - */ -} \ No newline at end of file diff --git a/sdk/core/dma/dma_compartment.h b/sdk/core/dma/dma_compartment.h index d79afb22..3257e668 100644 --- a/sdk/core/dma/dma_compartment.h +++ b/sdk/core/dma/dma_compartment.h @@ -7,12 +7,6 @@ * A function below claims the source and target addresses of the DMA interface. * While, DMA is in progress, these addresses should not be freed */ -__cheri_compartment("dma") int claim_dma(uint32_t *sourceAddress, - uint32_t *targetAddress); +__cheri_compartment("dma") int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); -/** - * The function below frees the source and target addresses of the DMA interface. - * It should be called once the DMA operation is completed. - */ -__cheri_compartment("dma") void free_dma(uint32_t *sourceAddress, - uint32_t *targetAddress); \ No newline at end of file diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/include/platform/ibex/platform-dma.hh index 87d24743..343cc09f 100644 --- a/sdk/include/platform/ibex/platform-dma.hh +++ b/sdk/include/platform/ibex/platform-dma.hh @@ -58,56 +58,54 @@ namespace Ibex { uint32_t targetStrides; }; - volatile DMAInterface *dmaInterface; - - public: - - void init() + __always_inline volatile DMAInterface &device() { - dmaInterface = MMIO_CAPABILITY(DMAInterface, dma); + return *MMIO_CAPABILITY(DMAInterface, dma); } + public: + uint32_t read_status() { /** * this statement returns the less signifance bit * to show the halted status */ - return dmaInterface->status & 0x1; + return device().status & 0x1; } void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) { /** - * filling source and target addresses, and length fields + * Setting source and target addresses, and length fields */ - dmaInterface->sourceAddress = sourceAddress; - dmaInterface->targetAddress = targetAddress; - dmaInterface->lengthInBytes = lengthInBytes; + device().sourceAddress = sourceAddress; + device().targetAddress = targetAddress; + device().lengthInBytes = lengthInBytes; } void write_strides(uint32_t sourceStrides, uint32_t targetStrides) { /** - * filling source and target strides + * Setting source and target strides */ - dmaInterface->sourceStrides = sourceStrides; - dmaInterface->targetStrides = targetStrides; + device().sourceStrides = sourceStrides; + device().targetStrides = targetStrides; } int byte_swap_en(uint32_t swapAmount) { /** - * filling byte swaps + * Setting byte swaps */ if (swapAmount == 2) { - dmaInterface->control = 0x2; + device().control = 0x2; } else if (swapAmount == 4) { - dmaInterface->control = 0x4; + device().control = 0x4; } else { return -1; @@ -119,17 +117,19 @@ namespace Ibex { void start_dma() { /** - * setting a start bit + * Setting a start bit */ - dmaInterface->control = 0x1; + device().control = 0x1; } void reset_dma() { /** - * setting a reset bit, which is bit 3 + * Setting a reset bit, which is bit 3. + * this clears all the registers, + * but do not transfer the current transfer status anywhere */ - dmaInterface->control = 0x8; + device().control = 0x8; } From 265db79e4b83792ca1658e136b92989ef31b8845 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Tue, 18 Jul 2023 11:45:43 +0100 Subject: [PATCH 16/42] fixed some errors and added a test example --- examples/10.dma_test/dma_test.c | 13 +++++++++++-- sdk/core/dma/dma.h | 22 +++++++++++----------- sdk/core/dma/dma_compartment.cc | 7 +++++-- sdk/include/platform/ibex/platform-dma.hh | 8 ++++---- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/examples/10.dma_test/dma_test.c b/examples/10.dma_test/dma_test.c index 7a3ad255..1e7d94e3 100644 --- a/examples/10.dma_test/dma_test.c +++ b/examples/10.dma_test/dma_test.c @@ -2,9 +2,13 @@ // SPDX-License-Identifier: MIT #include +#include +#include #include +#include + #include -#include +#include <../core/dma/dma.h> /// Expose debugging features unconditionally for this compartment. using Debug = ConditionalDebug; @@ -12,8 +16,13 @@ using Debug = ConditionalDebug; /// Thread entry point. void __cheri_compartment("dma") dma_request() { - // Print hello world, along with the compartment's name to the default UART. Debug::log("DMA request app entered!"); + // This is a dma process between two different memory addresses + uint32_t *sourceAddress =(uint32_t*) malloc(1024); + uint32_t *targetAddress =(uint32_t*) malloc(1024); + DMA::Device dmaDevice; + + dmaDevice.configure_and_launch(sourceAddress, targetAddress, 1024, 0, 0, 0); } diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 4d8ab89c..cc2d38ce 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -1,9 +1,9 @@ #pragma once -#include "dma_compartment.cc" #include "dma_compartment.h" #include "platform/ibex/platform-dma.hh" #include +#include #include #include @@ -17,34 +17,34 @@ namespace DMA concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *targetAddress, size_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - {device.init()}; { - device.write_conf(sourceAddress, destinationAddress, lengthInBytes) + device.write_conf(sourceAddress, targetAddress, lengthInBytes) } -> std::same_as; { device.start_dma() } -> std::same_as; { - device.reset_dma(sourceAddress, destinationAddress) + device.reset_dma() } -> std::same_as; }; - template - typename PlatformDMA> - requires IsDmaDevice> - class GenericDMA : public PlatformDMA + template + + requires IsDmaDevice + + class GenericDMA : public PlatformDMA { public: - int configure_and_launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + int configure_and_launch(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { return dma_compartment(sourceAddress, targetAddress, lengthInBytes, - sourceStrides, targetStrides, byteSwapAmount) + sourceStrides, targetStrides, byteSwapAmount); } }; - using DMA = GenericDMA; + using Device = GenericDMA; } diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 1d905a91..f1ad6f00 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -11,6 +11,9 @@ # include #endif +// Import some useful things from the CHERI namespace. +using namespace CHERI; + using thread_pool::async; /** * creating a global heap below. @@ -43,8 +46,8 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l * and if not long enough */ - if (!check_pointer(sourceAddress, lengthInBytes) || - !check_pointer(targetAddress, lengthInBytes) ) + if (!check_pointer(sourceAddress, lengthInBytes) || + !check_pointer(targetAddress, lengthInBytes) ) { return -1; } diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/include/platform/ibex/platform-dma.hh index 343cc09f..af119d72 100644 --- a/sdk/include/platform/ibex/platform-dma.hh +++ b/sdk/include/platform/ibex/platform-dma.hh @@ -74,13 +74,13 @@ namespace Ibex { return device().status & 0x1; } - void write_conf(uint32_t sourceAddress, uint32_t targetAddress, uint32_t lengthInBytes) + void write_conf(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes) { /** * Setting source and target addresses, and length fields */ - device().sourceAddress = sourceAddress; - device().targetAddress = targetAddress; + device().sourceAddress = *sourceAddress; + device().targetAddress = *targetAddress; device().lengthInBytes = lengthInBytes; } @@ -136,5 +136,5 @@ namespace Ibex { }; } // namespace Ibex -template +// template using PlatformDMA = Ibex::PlatformDMA; \ No newline at end of file From 8313fc11cd40a793da9a165968cb00f22be6ea39 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 19 Jul 2023 16:32:53 +0100 Subject: [PATCH 17/42] fixed some logic and cheri mistakes, third iteration --- sdk/core/dma/dma.h | 14 +--- sdk/core/dma/dma_compartment.cc | 67 +++++++------------ sdk/core/dma/dma_compartment.h | 3 +- .../ibex => core/dma}/platform-dma.hh | 51 +++++++------- 4 files changed, 58 insertions(+), 77 deletions(-) rename sdk/{include/platform/ibex => core/dma}/platform-dma.hh (88%) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index cc2d38ce..a2e74d8f 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -1,16 +1,10 @@ -#pragma once - #include "dma_compartment.h" -#include "platform/ibex/platform-dma.hh" +#include "platform-dma.hh" #include #include #include #include -#if __has_include() -# include -#endif - namespace DMA { template @@ -18,10 +12,8 @@ namespace DMA uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { { - device.write_conf(sourceAddress, targetAddress, lengthInBytes) - } -> std::same_as; - { - device.start_dma() + device.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, + sourceStrides, targetStrides, byteSwapAmount) } -> std::same_as; { device.reset_dma() diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index f1ad6f00..a3fc43d7 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -5,32 +5,17 @@ #include #include #include -#include - -#if __has_include() -# include -#endif +#include "platform-dma.hh" // Import some useful things from the CHERI namespace. using namespace CHERI; -using thread_pool::async; -/** - * creating a global heap below. - * todo: we assume that this heap capability can serve - * for different compartments, irrespective of - * the origin because this is a driver code? -*/ -DECLARE_AND_DEFINE_ALLOCATOR_CAPABILITY(dmaDriverHeap, 8144); - -SObjStruct *dmaHeap; - Ibex::PlatformDMA platformDma; void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) { - int sourceStatus = heap_free(dmaHeap, sourceAddress); - int targetStatus = heap_free(dmaHeap, targetAddress); + int sourceStatus = heap_free(MALLOC_CAPABILITY, sourceAddress); + int targetStatus = heap_free(MALLOC_CAPABILITY, targetAddress); /** * ideally, free should not fail if claim was successful @@ -41,27 +26,14 @@ void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - /** - * return if sufficient permissions are not present - * and if not long enough - */ - - if (!check_pointer(sourceAddress, lengthInBytes) || - !check_pointer(targetAddress, lengthInBytes) ) - { - return -1; - } /** - * create a heap capability for dma below. - */ - dmaHeap = STATIC_SEALED_VALUE(dmaDriverHeap); - - /** - * claim the memory + * claim the memory with default malloc capability, + * as declaring another heap capability is an extra entry + * that leaves the default capability let unused */ - size_t sourceClaimSize = heap_claim(dmaHeap, sourceAddress); + size_t sourceClaimSize = heap_claim(MALLOC_CAPABILITY, sourceAddress); /** * return with failure if @@ -73,7 +45,7 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l return -1; } - size_t targetClaimSize = heap_claim(dmaHeap, targetAddress); + size_t targetClaimSize = heap_claim(MALLOC_CAPABILITY, targetAddress); if (targetClaimSize == 0) { @@ -82,24 +54,35 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l * then free the first claim and exit the function * todo: assert a Debug::Assert() for heap_free() result later */ - int sourceStatus = heap_free(dmaHeap, sourceAddress); + int sourceStatus = heap_free(MALLOC_CAPABILITY, sourceAddress); return -1; } - platformDma::write_strides(sourceStrides, targetStrides); - platformDma::byte_swap_en(byteSwapAmount); - - platformDma::start_dma(); + /** + * return if sufficient permissions are not present + * and if not long enough + */ + + if (!check_pointer(sourceAddress, lengthInBytes) || + !check_pointer(targetAddress, lengthInBytes) ) + { + return -1; + } + + platformDma.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, + sourceStrides, targetStrides, byteSwapAmount); // todo: need to check for status via polling maybe from here // or for the mvp, we can just check or cancel after some timeout // todo: eventually we need some interrupt support and the futex call here + // todo: eventually, we wanna separate this free and reset dma + // logics from the start dma as well free_dma(sourceAddress, targetAddress); - platformDma::reset_dma(); + platformDma.reset_dma(); /** * return here, if both claims are successful diff --git a/sdk/core/dma/dma_compartment.h b/sdk/core/dma/dma_compartment.h index 3257e668..3f4865fc 100644 --- a/sdk/core/dma/dma_compartment.h +++ b/sdk/core/dma/dma_compartment.h @@ -5,7 +5,8 @@ /** * A function below claims the source and target addresses of the DMA interface. - * While, DMA is in progress, these addresses should not be freed + * While, DMA is in progress, these addresses will be claimed by the DMA compartment + * and so the memory will not be freed. */ __cheri_compartment("dma") int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); diff --git a/sdk/include/platform/ibex/platform-dma.hh b/sdk/core/dma/platform-dma.hh similarity index 88% rename from sdk/include/platform/ibex/platform-dma.hh rename to sdk/core/dma/platform-dma.hh index af119d72..6b9e4c3f 100644 --- a/sdk/include/platform/ibex/platform-dma.hh +++ b/sdk/core/dma/platform-dma.hh @@ -3,6 +3,7 @@ #include #include #include +#include namespace Ibex { class PlatformDMA { @@ -63,28 +64,6 @@ namespace Ibex { return *MMIO_CAPABILITY(DMAInterface, dma); } - public: - - uint32_t read_status() - { - /** - * this statement returns the less signifance bit - * to show the halted status - */ - return device().status & 0x1; - } - - void write_conf(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes) - { - /** - * Setting source and target addresses, and length fields - */ - device().sourceAddress = *sourceAddress; - device().targetAddress = *targetAddress; - device().lengthInBytes = lengthInBytes; - - } - void write_strides(uint32_t sourceStrides, uint32_t targetStrides) { /** @@ -94,7 +73,7 @@ namespace Ibex { device().targetStrides = targetStrides; } - int byte_swap_en(uint32_t swapAmount) + int enable_byte_swap(uint32_t swapAmount) { /** * Setting byte swaps @@ -122,6 +101,32 @@ namespace Ibex { device().control = 0x1; } + public: + + uint32_t read_status() + { + /** + * this statement returns the less signifance bit + * to show the halted status + */ + return device().status & 0x1; + } + + void write_conf_and_start(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) + { + /** + * Setting source and target addresses, and length fields + */ + device().sourceAddress = CHERI::Capability{sourceAddress}.address(); + device().targetAddress = CHERI::Capability{targetAddress}.address(); + device().lengthInBytes = lengthInBytes; + + write_strides(sourceStrides, targetStrides); + enable_byte_swap(byteSwapAmount); + start_dma(); + } + void reset_dma() { /** From e2211aad8377bb5977bd49f2b657ad8745b6230b Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 19 Jul 2023 17:13:53 +0100 Subject: [PATCH 18/42] add the unique pointer support to the compartment --- sdk/core/dma/dma_compartment.cc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index a3fc43d7..82a7f7db 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -1,5 +1,7 @@ #pragma once +#include +#include #define MALLOC_QUOTA 0x100000 #include @@ -30,10 +32,16 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l /** * claim the memory with default malloc capability, * as declaring another heap capability is an extra entry - * that leaves the default capability let unused + * that leaves the default capability let unused. + * + * unique pointers are used to avoid explicit heap_free() calls. + * when this pointer is not used anymore, it is automatically deleted */ - size_t sourceClaimSize = heap_claim(MALLOC_CAPABILITY, sourceAddress); + std::unique_ptr uniqueSourceAddress(sourceAddress); + std::unique_ptr uniqueTargetAddress(targetAddress); + + size_t sourceClaimSize = heap_claim(MALLOC_CAPABILITY, static_cast(uniqueSourceAddress.get())); /** * return with failure if @@ -45,7 +53,7 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l return -1; } - size_t targetClaimSize = heap_claim(MALLOC_CAPABILITY, targetAddress); + size_t targetClaimSize = heap_claim(MALLOC_CAPABILITY, static_cast(uniqueTargetAddress.get())); if (targetClaimSize == 0) { @@ -80,7 +88,7 @@ int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t l // todo: eventually, we wanna separate this free and reset dma // logics from the start dma as well - free_dma(sourceAddress, targetAddress); + // free_dma(sourceAddress, targetAddress); platformDma.reset_dma(); From 0f71c0e7a2eef2a828b1f8bbbb6eb1bd4ae71aa7 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 26 Jul 2023 15:47:38 +0100 Subject: [PATCH 19/42] uploading the json file for a check --- ibex.json | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 ibex.json diff --git a/ibex.json b/ibex.json new file mode 100644 index 00000000..288ba597 --- /dev/null +++ b/ibex.json @@ -0,0 +1,71 @@ +{ + "devices" : + { + "hspTimer" : { + "start" : 0x8f000800, + "length" : 0x20 + }, + "hspInteruptController" : { + "start" : 0x8f000600, + "length" : 0x14 + }, + "uart" : { + "start" : 0x8f00b000, + "length": 0x100 + }, + "shadow" : { + "start" : 0x200f0000, + "end" : 0x200f1800 + }, + "revoker" : { + "start" : 0x8f000000, + "length": 0xc + }, + "dmb" : { + "start" : 0x8f0f0000, + "length": 0x400 + }, + "ethernet" : { + "start" : 0x98000000, + "length": 0x204 + }, + "LEDs" : { + "start" : 0x8f00 f004, + "end": 0x2020_0040 + }, + "dma" : { + "start" : 0x2020_0000, + "end" : 0x2020_0040 + } + }, + "instruction_memory" : { + "start" : 0x20000080, + "end" : 0x20060000 + }, + "heap" : { + "end" : 0x20060000 + }, + "revokable_memory_start" : 0x20000000, + "interrupts": [ + { + "name": "Ethernet", + "number": 16, + "priority": 0 + } + ], + "defines" : [ + "IBEX", + "IBEX_SHADOW_BASE=0x200f0000U", + "IBEX_SHADOW_SIZE=0x1800U" + ], + "driver_includes" : [ + "../include/", + "${sdk}/include/platform/ibex", + "${sdk}/include/platform/generic-riscv" + ], + "timer_hz" : 20000000, + "tickrate_hz" : 100, + "stack_high_water_mark" : true, + "revoker" : "hardware", + "simulator" : "${board}/../scripts/deploy-ibex-fpga.sh" +} From 05076b625beb7dd2265109b51055c6d212b036e9 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 26 Jul 2023 18:16:16 +0100 Subject: [PATCH 20/42] fixing naming mistakes --- examples/10.dma_test/{dma_test.c => dma_test.cc} | 2 +- examples/10.dma_test/xmake.lua | 6 +++--- sdk/core/dma/dma_compartment.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename examples/10.dma_test/{dma_test.c => dma_test.cc} (93%) diff --git a/examples/10.dma_test/dma_test.c b/examples/10.dma_test/dma_test.cc similarity index 93% rename from examples/10.dma_test/dma_test.c rename to examples/10.dma_test/dma_test.cc index 1e7d94e3..63df6407 100644 --- a/examples/10.dma_test/dma_test.c +++ b/examples/10.dma_test/dma_test.cc @@ -14,7 +14,7 @@ using Debug = ConditionalDebug; /// Thread entry point. -void __cheri_compartment("dma") dma_request() +void __cheri_compartment("dma-app") dma_request() { Debug::log("DMA request app entered!"); diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua index a5cb41f4..40dab2c6 100644 --- a/examples/10.dma_test/xmake.lua +++ b/examples/10.dma_test/xmake.lua @@ -14,18 +14,18 @@ includes(path.join(sdkdir, "lib/freestanding"), option("board") set_default("sail") -compartment("dma") +compartment("dma-app") add_files("dma_test.cc") -- Firmware image for the example. firmware("dma_test") add_deps("crt", "freestanding", "atomic_fixed") - add_deps("dma") + add_deps("dma-app") on_load(function(target) target:values_set("board", "$(board)") target:values_set("threads", { { - compartment = "dma", + compartment = "dma-app", priority = 1, entry_point = "dma_request", stack_size = 0x200, diff --git a/sdk/core/dma/dma_compartment.h b/sdk/core/dma/dma_compartment.h index 3f4865fc..8aea3e5b 100644 --- a/sdk/core/dma/dma_compartment.h +++ b/sdk/core/dma/dma_compartment.h @@ -8,6 +8,6 @@ * While, DMA is in progress, these addresses will be claimed by the DMA compartment * and so the memory will not be freed. */ -__cheri_compartment("dma") int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, +int __cheri_compartment("dma") dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); From 1651284fc997ac6701bcd2d557270b33381484d9 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 26 Jul 2023 18:46:03 +0100 Subject: [PATCH 21/42] adding a dma compartment to a xmake --- examples/10.dma_test/xmake.lua | 5 ++++- sdk/core/dma/dma_compartment.cc | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua index 40dab2c6..5e2f7685 100644 --- a/examples/10.dma_test/xmake.lua +++ b/examples/10.dma_test/xmake.lua @@ -17,10 +17,13 @@ option("board") compartment("dma-app") add_files("dma_test.cc") +compartment("dma") + add_files("../../sdk/core/dma/dma_compartment.cc") + -- Firmware image for the example. firmware("dma_test") add_deps("crt", "freestanding", "atomic_fixed") - add_deps("dma-app") + add_deps("dma-app", "dma") on_load(function(target) target:values_set("board", "$(board)") target:values_set("threads", { diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 82a7f7db..b8374723 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -1,8 +1,7 @@ -#pragma once +#define MALLOC_QUOTA 0x100000 #include #include -#define MALLOC_QUOTA 0x100000 #include #include From 6df7a1be1bff55554b3ec862b0ff5f127bba9747 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Thu, 27 Jul 2023 13:59:24 +0100 Subject: [PATCH 22/42] successfully compiled and linked version --- examples/10.dma_test/dma_test.cc | 4 ++-- examples/10.dma_test/xmake.lua | 17 +++++++++-------- sdk/core/dma/dma.h | 6 ++++-- sdk/core/dma/dma_compartment.cc | 3 ++- .../{dma_compartment.h => dma_compartment.hh} | 3 ++- 5 files changed, 19 insertions(+), 14 deletions(-) rename sdk/core/dma/{dma_compartment.h => dma_compartment.hh} (76%) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index 63df6407..d9fbad79 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -8,13 +8,13 @@ #include #include -#include <../core/dma/dma.h> +#include <../../sdk/core/dma/dma.h> /// Expose debugging features unconditionally for this compartment. using Debug = ConditionalDebug; /// Thread entry point. -void __cheri_compartment("dma-app") dma_request() +void __cheri_compartment("dma_app") dma_request() { Debug::log("DMA request app entered!"); diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua index 5e2f7685..e29b1ae7 100644 --- a/examples/10.dma_test/xmake.lua +++ b/examples/10.dma_test/xmake.lua @@ -14,25 +14,26 @@ includes(path.join(sdkdir, "lib/freestanding"), option("board") set_default("sail") -compartment("dma-app") - add_files("dma_test.cc") - compartment("dma") - add_files("../../sdk/core/dma/dma_compartment.cc") + add_files(path.join(sdkdir, "core/dma/dma_compartment.cc")) + +compartment("dma_app") + add_files("dma_test.cc") -- Firmware image for the example. firmware("dma_test") add_deps("crt", "freestanding", "atomic_fixed") - add_deps("dma-app", "dma") + add_deps("dma", "dma_app") + -- add_deps("dma_app") on_load(function(target) target:values_set("board", "$(board)") target:values_set("threads", { { - compartment = "dma-app", + compartment = "dma_app", priority = 1, entry_point = "dma_request", - stack_size = 0x200, - trusted_stack_frames = 1 + stack_size = 0x400, + trusted_stack_frames = 2 } }, {expand = false}) end) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index a2e74d8f..b6fa9c98 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -1,4 +1,4 @@ -#include "dma_compartment.h" +#include "dma_compartment.hh" #include "platform-dma.hh" #include #include @@ -31,8 +31,10 @@ namespace DMA int configure_and_launch(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - return dma_compartment(sourceAddress, targetAddress, lengthInBytes, + launch_dma(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); + + return 0; } }; diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index b8374723..16421a51 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -1,5 +1,6 @@ #define MALLOC_QUOTA 0x100000 +#include "dma_compartment.hh" #include #include @@ -24,7 +25,7 @@ void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) */ } -int dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, +int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { diff --git a/sdk/core/dma/dma_compartment.h b/sdk/core/dma/dma_compartment.hh similarity index 76% rename from sdk/core/dma/dma_compartment.h rename to sdk/core/dma/dma_compartment.hh index 8aea3e5b..0eda7f62 100644 --- a/sdk/core/dma/dma_compartment.h +++ b/sdk/core/dma/dma_compartment.hh @@ -8,6 +8,7 @@ * While, DMA is in progress, these addresses will be claimed by the DMA compartment * and so the memory will not be freed. */ -int __cheri_compartment("dma") dma_compartment(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, + +int __cheri_compartment("dma") launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); From 7ba5106e149ff006a288404453affeb3b8e93133 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 2 Aug 2023 18:56:08 +0100 Subject: [PATCH 23/42] sw after few fixes for smart pointers --- examples/10.dma_test/dma_test.cc | 22 ++++++++++--- sdk/core/dma/dma.h | 3 +- sdk/core/dma/dma_compartment.cc | 54 +++++++++++++++++--------------- sdk/core/dma/platform-dma.hh | 24 +++++++++++--- 4 files changed, 66 insertions(+), 37 deletions(-) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index d9fbad79..26d026bd 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -19,10 +19,24 @@ void __cheri_compartment("dma_app") dma_request() Debug::log("DMA request app entered!"); // This is a dma process between two different memory addresses - uint32_t *sourceAddress =(uint32_t*) malloc(1024); - uint32_t *targetAddress =(uint32_t*) malloc(1024); - + uint32_t *sourceAddress =(uint32_t*) malloc(4); + uint32_t *targetAddress =(uint32_t*) malloc(4); + + // for (int i=0; i<4; i++) + // { + // *(sourceAddress + i) = i + 100; + // *(targetAddress + i) = i + 200; + // } + + Debug::log("Source Address BEFORE: {}", *(sourceAddress)); + Debug::log("Target Address BEFORE: {}", *(sourceAddress)); + DMA::Device dmaDevice; - dmaDevice.configure_and_launch(sourceAddress, targetAddress, 1024, 0, 0, 0); + int ret = dmaDevice.configure_and_launch(sourceAddress, targetAddress, 4, 0, 0, 0); + + Debug::log("Source Address AFTER: {}", *(sourceAddress)); + Debug::log("Target Address AFTER: {}", *(sourceAddress)); + + Debug::log("ret: {}", ret); } diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index b6fa9c98..14c101ff 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -31,10 +31,9 @@ namespace DMA int configure_and_launch(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - launch_dma(sourceAddress, targetAddress, lengthInBytes, + return launch_dma(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); - return 0; } }; diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 16421a51..e1a360a8 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -3,11 +3,16 @@ #include "dma_compartment.hh" #include #include +#include #include #include #include #include "platform-dma.hh" +#include + +/// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; // Import some useful things from the CHERI namespace. using namespace CHERI; @@ -38,34 +43,25 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length * when this pointer is not used anymore, it is automatically deleted */ - std::unique_ptr uniqueSourceAddress(sourceAddress); - std::unique_ptr uniqueTargetAddress(targetAddress); - - size_t sourceClaimSize = heap_claim(MALLOC_CAPABILITY, static_cast(uniqueSourceAddress.get())); - - /** - * return with failure if - * no claim is available - */ - - if (sourceClaimSize == 0) + auto claim = [](void *ptr) -> std::unique_ptr { - return -1; - } - - size_t targetClaimSize = heap_claim(MALLOC_CAPABILITY, static_cast(uniqueTargetAddress.get())); - - if (targetClaimSize == 0) - { - /** - * if the first claim was successful, but this one failed, - * then free the first claim and exit the function - * todo: assert a Debug::Assert() for heap_free() result later - */ - int sourceStatus = heap_free(MALLOC_CAPABILITY, sourceAddress); + if (heap_claim(MALLOC_CAPABILITY, ptr) == 0) + { + return {nullptr}; + } + + return std::unique_ptr { static_cast(ptr)}; + }; + + auto claimedSource = claim(sourceAddress); + auto claimedDestination = claim(targetAddress); + + if (!claimedSource || !claimedDestination) + { + return -EINVAL; + } - return -1; - } + Debug::log("after claim check"); /** * return if sufficient permissions are not present @@ -78,9 +74,13 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length return -1; } + Debug::log("after right check"); + platformDma.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); + Debug::log("after conf write"); + // todo: need to check for status via polling maybe from here // or for the mvp, we can just check or cancel after some timeout @@ -92,6 +92,8 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length platformDma.reset_dma(); + Debug::log("after reset write"); + /** * return here, if both claims are successful */ diff --git a/sdk/core/dma/platform-dma.hh b/sdk/core/dma/platform-dma.hh index 6b9e4c3f..2257088f 100644 --- a/sdk/core/dma/platform-dma.hh +++ b/sdk/core/dma/platform-dma.hh @@ -4,6 +4,10 @@ #include #include #include +#include + +/// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; namespace Ibex { class PlatformDMA { @@ -70,7 +74,9 @@ namespace Ibex { * Setting source and target strides */ device().sourceStrides = sourceStrides; + Debug::log("after s stride addr"); device().targetStrides = targetStrides; + Debug::log("after t stride addr"); } int enable_byte_swap(uint32_t swapAmount) @@ -79,17 +85,18 @@ namespace Ibex { * Setting byte swaps */ + Debug::log("byte swap entered"); + if (swapAmount == 2) { device().control = 0x2; } else if (swapAmount == 4) { device().control = 0x4; - } else - { - return -1; } + Debug::log("after byte swap addr"); + return 0; } @@ -98,7 +105,9 @@ namespace Ibex { /** * Setting a start bit */ - device().control = 0x1; + Debug::log("before"); + device().control = 1; + Debug::log("after"); } public: @@ -119,12 +128,17 @@ namespace Ibex { * Setting source and target addresses, and length fields */ device().sourceAddress = CHERI::Capability{sourceAddress}.address(); + Debug::log("after source addr"); device().targetAddress = CHERI::Capability{targetAddress}.address(); + Debug::log("after target addr"); device().lengthInBytes = lengthInBytes; + Debug::log("after length addr"); write_strides(sourceStrides, targetStrides); enable_byte_swap(byteSwapAmount); - start_dma(); + Debug::log("before start dma"); + start_dma(); + Debug::log("after start dma"); } void reset_dma() From cd5b205c0a2279cfd3aa37a95e507bc7ee9f7dab Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Thu, 3 Aug 2023 17:01:27 +0100 Subject: [PATCH 24/42] software that worked with dma! --- examples/10.dma_test/dma_test.cc | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index 26d026bd..72fe65e7 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -19,24 +19,27 @@ void __cheri_compartment("dma_app") dma_request() Debug::log("DMA request app entered!"); // This is a dma process between two different memory addresses - uint32_t *sourceAddress =(uint32_t*) malloc(4); - uint32_t *targetAddress =(uint32_t*) malloc(4); + uint32_t *sourceAddress =(uint32_t*) malloc(16); + uint32_t *targetAddress =(uint32_t*) malloc(16); - // for (int i=0; i<4; i++) - // { - // *(sourceAddress + i) = i + 100; - // *(targetAddress + i) = i + 200; - // } + for (int i=0; i<4; i++) + { + *(sourceAddress + i) = i + 100; + *(targetAddress + i) = 0; - Debug::log("Source Address BEFORE: {}", *(sourceAddress)); - Debug::log("Target Address BEFORE: {}", *(sourceAddress)); + Debug::log("Ind: {}, Source values BEFORE dma: {}", i, *(sourceAddress + i)); + Debug::log("Ind: {}, Dest-n values BEFORE dma: {}", i, *(targetAddress + i)); + } DMA::Device dmaDevice; - int ret = dmaDevice.configure_and_launch(sourceAddress, targetAddress, 4, 0, 0, 0); - - Debug::log("Source Address AFTER: {}", *(sourceAddress)); - Debug::log("Target Address AFTER: {}", *(sourceAddress)); + int ret = dmaDevice.configure_and_launch(sourceAddress, targetAddress, 16, 0, 0, 0); + + for (int i=0; i<4; i++) + { + Debug::log("Ind: {}, Source values AFTER dma: {}", i, *(sourceAddress + i)); + Debug::log("Ind: {}, Dest-n values AFTER dma: {}", i, *(targetAddress + i)); + }; Debug::log("ret: {}", ret); } From 199a217bba95f1f54b0506fcf5c1cbcfc79017c5 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Fri, 4 Aug 2023 14:08:11 +0100 Subject: [PATCH 25/42] sw for interrupt code --- sdk/core/dma/dma.h | 42 +++++++++++++++++++++++++++++++-- sdk/core/dma/dma_compartment.cc | 26 ++++++++++++-------- sdk/core/dma/dma_compartment.hh | 3 +++ 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 14c101ff..666fc87b 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -1,9 +1,14 @@ #include "dma_compartment.hh" +#include "futex.h" #include "platform-dma.hh" #include #include #include #include +#include +#include + +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, true, true); namespace DMA { @@ -31,10 +36,43 @@ namespace DMA int configure_and_launch(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - return launch_dma(sourceAddress, targetAddress, lengthInBytes, + int launchDmaStatus = launch_dma(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); - } + + // once dma is launched, we need to check for the interrupt status + const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + // todo: do some checks as well for below and the validity + // if (dmaFutex.permissions().contains(Permission::Store)) + + uint32_t last = *dmaFutex; + + do + { + last = *dmaFutex; + // Handle interrupt here + // dma interrupt means that the dma operation is finished + // and it is time to reset and clear the dma configurations + Debug::log("intc futex val: {}", last); + + // non 0 return means fail + int resetFailed = reset_and_clear_dma(last); + + if (resetFailed) + { + return -EINVAL; + } + + // Acknowledging interrupt here + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + // todo: once interrupt is served we just return? + return 0; + } while (futex_wait(dmaFutex, last) == 0); + + return 0; + } }; diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index e1a360a8..ca09e444 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -81,22 +81,28 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length Debug::log("after conf write"); - // todo: need to check for status via polling maybe from here - // or for the mvp, we can just check or cancel after some timeout + /** + * return here, if both claims are successful + */ + return 0; +} + +int reset_and_clear_dma(uint32_t interruptStatus) +{ // todo: eventually we need some interrupt support and the futex call here // todo: eventually, we wanna separate this free and reset dma // logics from the start dma as well // free_dma(sourceAddress, targetAddress); - platformDma.reset_dma(); - - Debug::log("after reset write"); - - /** - * return here, if both claims are successful - */ - return 0; + if (interruptStatus) + { + platformDma.reset_dma(); + Debug::log("after reset write"); + return 0; + } + + return -EINVAL; } diff --git a/sdk/core/dma/dma_compartment.hh b/sdk/core/dma/dma_compartment.hh index 0eda7f62..28ea72ab 100644 --- a/sdk/core/dma/dma_compartment.hh +++ b/sdk/core/dma/dma_compartment.hh @@ -12,3 +12,6 @@ int __cheri_compartment("dma") launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); + + +int __cheri_compartment("dma") reset_and_clear_dma(uint32_t interruptStatus); \ No newline at end of file From f697ecee80b15c1d902bc1ad2c998d9e5e0880d6 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Fri, 4 Aug 2023 14:55:30 +0100 Subject: [PATCH 26/42] fixed interrupt sw errors --- sdk/core/dma/dma.h | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 666fc87b..1b836f05 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -41,37 +41,34 @@ namespace DMA // once dma is launched, we need to check for the interrupt status + // no need for validity and permissions checks for the scheduler once futex is created as well const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); - // todo: do some checks as well for below and the validity - // if (dmaFutex.permissions().contains(Permission::Store)) + uint32_t previous = *dmaFutex; + Debug::log("intc futex val: {}", previous); - uint32_t last = *dmaFutex; + // sleep until interrupt fires with futex_wait + futex_wait(dmaFutex, previous); - do + // Handle the interrupt here + // dma interrupt means that the dma operation is finished + // and it is time to reset and clear the dma configurations + + // non 0 return means fail + int resetFailed = reset_and_clear_dma(previous); + + // Acknowledging interrupt here + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + // todo: once interrupt is served we just return? + + if (resetFailed) { - last = *dmaFutex; - // Handle interrupt here - // dma interrupt means that the dma operation is finished - // and it is time to reset and clear the dma configurations - Debug::log("intc futex val: {}", last); - - // non 0 return means fail - int resetFailed = reset_and_clear_dma(last); - - if (resetFailed) - { - return -EINVAL; - } - - // Acknowledging interrupt here - interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); - - // todo: once interrupt is served we just return? - return 0; - } while (futex_wait(dmaFutex, last) == 0); + return -EINVAL; + } + return 0; + } }; From b03924c533f2ec49822669ea9ae6d9ea4bf83611 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Fri, 4 Aug 2023 14:58:03 +0100 Subject: [PATCH 27/42] removed redundant comments --- sdk/core/dma/dma.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 1b836f05..5511e98d 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -57,9 +57,8 @@ namespace DMA // non 0 return means fail int resetFailed = reset_and_clear_dma(previous); - // Acknowledging interrupt here + // Acknowledging interrupt here irrespective of the reset status interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); - // todo: once interrupt is served we just return? if (resetFailed) { From 759eb83dcdd375a05a712a6365bebfa596664196 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 7 Aug 2023 15:23:23 +0100 Subject: [PATCH 28/42] byte swap bug fixed --- examples/10.dma_test/dma_test.cc | 32 +++++++++--------- sdk/core/dma/dma.h | 17 +++------- sdk/core/dma/dma_compartment.cc | 21 +++--------- sdk/core/dma/dma_compartment.hh | 2 +- sdk/core/dma/platform-dma.hh | 57 +++++++++++++++----------------- 5 files changed, 53 insertions(+), 76 deletions(-) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index 72fe65e7..96448c8f 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -10,36 +10,38 @@ #include #include <../../sdk/core/dma/dma.h> -/// Expose debugging features unconditionally for this compartment. -using Debug = ConditionalDebug; +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; /// Thread entry point. void __cheri_compartment("dma_app") dma_request() { - Debug::log("DMA request app entered!"); + Debug::log("DMA app entered!"); // This is a dma process between two different memory addresses - uint32_t *sourceAddress =(uint32_t*) malloc(16); - uint32_t *targetAddress =(uint32_t*) malloc(16); + uint32_t bytes = 1024; + uint32_t words = bytes/4; + uint32_t byteSwap = 4; - for (int i=0; i<4; i++) + uint32_t *sourceAddress =(uint32_t*) malloc(bytes); + uint32_t *targetAddress =(uint32_t*) malloc(bytes); + + for (int i=0; i < words; i++) { - *(sourceAddress + i) = i + 100; + *(sourceAddress + i) = i + 200; *(targetAddress + i) = 0; - Debug::log("Ind: {}, Source values BEFORE dma: {}", i, *(sourceAddress + i)); - Debug::log("Ind: {}, Dest-n values BEFORE dma: {}", i, *(targetAddress + i)); } + Debug::log("Ind: 0 and last, Source values BEFORE dma: {}, {}", *(sourceAddress), *(sourceAddress + words -1 )); + Debug::log("Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + DMA::Device dmaDevice; - int ret = dmaDevice.configure_and_launch(sourceAddress, targetAddress, 16, 0, 0, 0); + int ret = dmaDevice.configure_and_launch(sourceAddress, targetAddress, bytes, 0, 0, byteSwap); - for (int i=0; i<4; i++) - { - Debug::log("Ind: {}, Source values AFTER dma: {}", i, *(sourceAddress + i)); - Debug::log("Ind: {}, Dest-n values AFTER dma: {}", i, *(targetAddress + i)); - }; + Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", *(sourceAddress), *(sourceAddress + words -1 )); + Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); Debug::log("ret: {}", ret); } diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 5511e98d..f4cf9dca 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -45,27 +45,18 @@ namespace DMA const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); uint32_t previous = *dmaFutex; - Debug::log("intc futex val: {}", previous); // sleep until interrupt fires with futex_wait - futex_wait(dmaFutex, previous); + futex_wait(dmaFutex, 0); - // Handle the interrupt here - // dma interrupt means that the dma operation is finished + // Handle the interrupt here, once dmaFutex woke up via scheduler. + // DMA interrupt means that the dma operation is finished // and it is time to reset and clear the dma configurations - - // non 0 return means fail - int resetFailed = reset_and_clear_dma(previous); + reset_and_clear_dma(); // Acknowledging interrupt here irrespective of the reset status interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); - if (resetFailed) - { - return -EINVAL; - } - - return 0; } diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index ca09e444..089da4d4 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -11,8 +11,8 @@ #include "platform-dma.hh" #include -/// Expose debugging features unconditionally for this compartment. -using Debug = ConditionalDebug; +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; // Import some useful things from the CHERI namespace. using namespace CHERI; @@ -61,8 +61,6 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length return -EINVAL; } - Debug::log("after claim check"); - /** * return if sufficient permissions are not present * and if not long enough @@ -74,13 +72,9 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length return -1; } - Debug::log("after right check"); - platformDma.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); - Debug::log("after conf write"); - /** * return here, if both claims are successful */ @@ -88,7 +82,7 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length } -int reset_and_clear_dma(uint32_t interruptStatus) +void reset_and_clear_dma() { // todo: eventually we need some interrupt support and the futex call here // todo: eventually, we wanna separate this free and reset dma @@ -96,13 +90,6 @@ int reset_and_clear_dma(uint32_t interruptStatus) // free_dma(sourceAddress, targetAddress); - if (interruptStatus) - { - platformDma.reset_dma(); - Debug::log("after reset write"); + platformDma.reset_dma(); - return 0; - } - - return -EINVAL; } diff --git a/sdk/core/dma/dma_compartment.hh b/sdk/core/dma/dma_compartment.hh index 28ea72ab..e2dd31ce 100644 --- a/sdk/core/dma/dma_compartment.hh +++ b/sdk/core/dma/dma_compartment.hh @@ -14,4 +14,4 @@ int __cheri_compartment("dma") launch_dma(uint32_t *sourceAddress, uint32_t *tar -int __cheri_compartment("dma") reset_and_clear_dma(uint32_t interruptStatus); \ No newline at end of file +void __cheri_compartment("dma") reset_and_clear_dma(); \ No newline at end of file diff --git a/sdk/core/dma/platform-dma.hh b/sdk/core/dma/platform-dma.hh index 2257088f..12e24e6c 100644 --- a/sdk/core/dma/platform-dma.hh +++ b/sdk/core/dma/platform-dma.hh @@ -1,13 +1,14 @@ #pragma once #include +#include #include #include #include #include -/// Expose debugging features unconditionally for this compartment. -using Debug = ConditionalDebug; +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; namespace Ibex { class PlatformDMA { @@ -74,41 +75,42 @@ namespace Ibex { * Setting source and target strides */ device().sourceStrides = sourceStrides; - Debug::log("after s stride addr"); device().targetStrides = targetStrides; - Debug::log("after t stride addr"); } - int enable_byte_swap(uint32_t swapAmount) + int swap_bytes_and_start_dma(uint32_t swapAmount) { /** - * Setting byte swaps + * Setting byte swaps + * and start bit. + * + * Swap amount can be equal to + * only either 2 or 4. */ - Debug::log("byte swap entered"); - - if (swapAmount == 2) - { - device().control = 0x2; - } else if (swapAmount == 4) + if (swapAmount != 2) { - device().control = 0x4; + if (swapAmount != 4) + { + swapAmount = 0; + } } - Debug::log("after byte swap addr"); + uint32_t controlConfiguration = swapAmount | 0x1; + + device().control = controlConfiguration; + return 0; } - void start_dma() - { - /** - * Setting a start bit - */ - Debug::log("before"); - device().control = 1; - Debug::log("after"); - } + // void start_dma() + // { + // /** + // * Setting a start bit + // */ + // device().control = 1; + // } public: @@ -128,17 +130,12 @@ namespace Ibex { * Setting source and target addresses, and length fields */ device().sourceAddress = CHERI::Capability{sourceAddress}.address(); - Debug::log("after source addr"); device().targetAddress = CHERI::Capability{targetAddress}.address(); - Debug::log("after target addr"); device().lengthInBytes = lengthInBytes; - Debug::log("after length addr"); write_strides(sourceStrides, targetStrides); - enable_byte_swap(byteSwapAmount); - Debug::log("before start dma"); - start_dma(); - Debug::log("after start dma"); + + swap_bytes_and_start_dma(byteSwapAmount); } void reset_dma() From 8d76a58a4e5c241805d509b732d68d7bd1da8576 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 7 Aug 2023 18:50:32 +0100 Subject: [PATCH 29/42] claims are dropped after the interrupts --- sdk/core/dma/dma_compartment.cc | 37 ++++++++++++++++++--------------- sdk/xmake.lua | 1 + 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 089da4d4..91556071 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -19,16 +19,12 @@ using namespace CHERI; Ibex::PlatformDMA platformDma; -void free_dma(uint32_t *sourceAddress, uint32_t *targetAddress) -{ - int sourceStatus = heap_free(MALLOC_CAPABILITY, sourceAddress); - int targetStatus = heap_free(MALLOC_CAPABILITY, targetAddress); +namespace { - /** - * ideally, free should not fail if claim was successful - * todo: but in case, assert a Debug::Assert() for heap_free() later - */ -} + std::unique_ptr claimedSource; + std::unique_ptr claimedDestination; + +} // namespace int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) @@ -53,8 +49,8 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length return std::unique_ptr { static_cast(ptr)}; }; - auto claimedSource = claim(sourceAddress); - auto claimedDestination = claim(targetAddress); + claimedSource = claim(sourceAddress); + claimedDestination = claim(targetAddress); if (!claimedSource || !claimedDestination) { @@ -69,7 +65,7 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length if (!check_pointer(sourceAddress, lengthInBytes) || !check_pointer(targetAddress, lengthInBytes) ) { - return -1; + return -EINVAL; } platformDma.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, @@ -83,12 +79,19 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length } void reset_and_clear_dma() -{ - // todo: eventually we need some interrupt support and the futex call here - // todo: eventually, we wanna separate this free and reset dma - // logics from the start dma as well +{ + // Resetting the claim pointers + // and cleaning up the dma registers. + + // Claim registers are meant to be + // cleared by every DMA operation, + // that is why we are explicitely resetting + // them at this exit function. - // free_dma(sourceAddress, targetAddress); + Debug::log("before dropping claims"); + + claimedSource.reset(); + claimedDestination.reset(); platformDma.reset_dma(); diff --git a/sdk/xmake.lua b/sdk/xmake.lua index c37de221..0300c060 100644 --- a/sdk/xmake.lua +++ b/sdk/xmake.lua @@ -78,6 +78,7 @@ toolchain("cheriot-clang") "-fno-builtin", "-fno-exceptions", "-fno-asynchronous-unwind-tables", + "-fno-c++-static-destructors", "-fno-rtti", "-Werror", "-I" .. path.join(include_directory, "c++-config"), From afbb809743e8cc5497197fde31a68bf3e99b68a4 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Tue, 8 Aug 2023 19:52:16 +0100 Subject: [PATCH 30/42] some multithreading model --- examples/10.dma_test/dma_test.cc | 7 +++++ sdk/core/dma/dma.h | 27 +++++++++++----- sdk/core/dma/dma_compartment.cc | 54 ++++++++++++++++++++++++++------ 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index 96448c8f..6c67437d 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -44,4 +44,11 @@ void __cheri_compartment("dma_app") dma_request() Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); Debug::log("ret: {}", ret); + + ret = dmaDevice.configure_and_launch(targetAddress, sourceAddress, bytes, 0, 0, 0); + + Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", *(sourceAddress), *(sourceAddress + words -1 )); + Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + + Debug::log("ret: {}", ret); } diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index f4cf9dca..909ba699 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -11,7 +11,12 @@ DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, true, true); namespace DMA -{ +{ + + // Once dma is launched, we need to check for the interrupt status + // no need for validity and permissions checks for the scheduler once futex is created as well + const uint32_t *dmaFutex; + template concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *targetAddress, size_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) @@ -36,22 +41,28 @@ namespace DMA int configure_and_launch(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { + /** + * Dma launch call: + * - checks for the dma ownership status, + * - for access rights, + * - creates claims for each source and destination addresses, + * - automatically resets the claims and the dma registers + * at the end of the transfer. + */ int launchDmaStatus = launch_dma(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); - - // once dma is launched, we need to check for the interrupt status - // no need for validity and permissions checks for the scheduler once futex is created as well - const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + // Check whether interrupt fired via scheduler + dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); - uint32_t previous = *dmaFutex; + uint32_t current = *dmaFutex; - // sleep until interrupt fires with futex_wait + // sleep until fired interrupt is served with futex_wait futex_wait(dmaFutex, 0); // Handle the interrupt here, once dmaFutex woke up via scheduler. // DMA interrupt means that the dma operation is finished - // and it is time to reset and clear the dma configurations + // and it is time to reset and clear the dma configuration registers reset_and_clear_dma(); // Acknowledging interrupt here irrespective of the reset status diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 91556071..51600548 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -10,6 +10,7 @@ #include #include "platform-dma.hh" #include +#include // Expose debugging features unconditionally for this compartment. using Debug = ConditionalDebug; @@ -20,7 +21,16 @@ using namespace CHERI; Ibex::PlatformDMA platformDma; namespace { + /** + * Flag lock to control ownership + * over the dma controller + */ + FlagLock dmaOwnershipLock; + /** + * Claims pointers so that the following + * addresses will not be freed ahead of time + */ std::unique_ptr claimedSource; std::unique_ptr claimedDestination; @@ -29,13 +39,24 @@ namespace { int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { + + /** + * Before launching a dma, check whether dma + * is occupied by another thread, + * else lock the ownership and start the dma + * via LockGuard + */ + LockGuard g{dmaOwnershipLock}; /** - * claim the memory with default malloc capability, + * After acquiring a ownership over lock, + * now it is the time to launch dma. + * + * Here, we claim the memory with default malloc capability, * as declaring another heap capability is an extra entry * that leaves the default capability let unused. * - * unique pointers are used to avoid explicit heap_free() calls. + * Unique pointers are used to avoid explicit heap_free() calls. * when this pointer is not used anymore, it is automatically deleted */ @@ -80,19 +101,34 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length void reset_and_clear_dma() { - // Resetting the claim pointers - // and cleaning up the dma registers. - - // Claim registers are meant to be - // cleared by every DMA operation, - // that is why we are explicitely resetting - // them at this exit function. + /** + * Resetting the claim pointers + * and cleaning up the dma registers. + * + * Claim registers are meant to be + * cleared by every DMA operation, + * that is why we are explicitely resetting + * them at this exit function. + * + */ Debug::log("before dropping claims"); claimedSource.reset(); claimedDestination.reset(); + /** + * Resetting the dma registers + */ platformDma.reset_dma(); + /** + * Unlocking the ownership for + * the next dma thread to proceed. + * todo: However, in this situation, + * adversary might gain access to the + * dma controller before the interrupt is served + */ + dmaOwnershipLock.unlock(); + } From 0ed541cd796645d11c2da57285583ddd22bce184 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 9 Aug 2023 12:14:06 +0100 Subject: [PATCH 31/42] multi-threaded threat model response v1 --- sdk/core/dma/dma.h | 32 ++++---------------- sdk/core/dma/dma_compartment.cc | 52 +++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 909ba699..5f31218a 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -8,14 +8,10 @@ #include #include -DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, true, true); - namespace DMA -{ +{ - // Once dma is launched, we need to check for the interrupt status - // no need for validity and permissions checks for the scheduler once futex is created as well - const uint32_t *dmaFutex; + uint32_t previousFutexValue; template concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *targetAddress, size_t lengthInBytes, @@ -40,7 +36,7 @@ namespace DMA int configure_and_launch(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) - { + { /** * Dma launch call: * - checks for the dma ownership status, @@ -48,27 +44,9 @@ namespace DMA * - creates claims for each source and destination addresses, * - automatically resets the claims and the dma registers * at the end of the transfer. - */ - int launchDmaStatus = launch_dma(sourceAddress, targetAddress, lengthInBytes, + */ + return launch_dma(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); - - // Check whether interrupt fired via scheduler - dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); - - uint32_t current = *dmaFutex; - - // sleep until fired interrupt is served with futex_wait - futex_wait(dmaFutex, 0); - - // Handle the interrupt here, once dmaFutex woke up via scheduler. - // DMA interrupt means that the dma operation is finished - // and it is time to reset and clear the dma configuration registers - reset_and_clear_dma(); - - // Acknowledging interrupt here irrespective of the reset status - interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); - - return 0; } diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 51600548..106e3f00 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -1,3 +1,4 @@ +#include "futex.h" #define MALLOC_QUOTA 0x100000 #include "dma_compartment.hh" @@ -10,6 +11,7 @@ #include #include "platform-dma.hh" #include +#include #include // Expose debugging features unconditionally for this compartment. @@ -19,13 +21,15 @@ using Debug = ConditionalDebug; using namespace CHERI; Ibex::PlatformDMA platformDma; +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, true, true); namespace { /** * Flag lock to control ownership * over the dma controller */ - FlagLock dmaOwnershipLock; + FlagLock dmaOwnershipLock; + uint32_t dmaIsRunning; /** * Claims pointers so that the following @@ -39,7 +43,13 @@ namespace { int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - + /** + * If dma is already launched, we need to check for the interrupt status + * no need for validity and permissions checks for the scheduler + * once futex is created + */ + const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + /** * Before launching a dma, check whether dma * is occupied by another thread, @@ -47,6 +57,20 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length * via LockGuard */ LockGuard g{dmaOwnershipLock}; + if (dmaIsRunning) + { + /** + * If dma is already running, wait for the interrupt with a timeout. + * If timeout, then wait return to the original function + */ + + Timeout t{10}; + return futex_timed_wait(&t, dmaFutex, 0); + } + + dmaIsRunning = 1; + + dmaOwnershipLock.unlock(); /** * After acquiring a ownership over lock, @@ -92,8 +116,21 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length platformDma.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); + + // sleep until fired interrupt is fired with futex_wait + futex_wait(dmaFutex, 0); + + // Handle the interrupt here, once dmaFutex woke up via scheduler. + // DMA interrupt means that the dma operation is finished + // and it is time to reset and clear the dma configuration registers + reset_and_clear_dma(); + + // Acknowledging interrupt here irrespective of the reset status + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + /** - * return here, if both claims are successful + * return here, if all operations + * were successful */ return 0; @@ -122,13 +159,4 @@ void reset_and_clear_dma() */ platformDma.reset_dma(); - /** - * Unlocking the ownership for - * the next dma thread to proceed. - * todo: However, in this situation, - * adversary might gain access to the - * dma controller before the interrupt is served - */ - dmaOwnershipLock.unlock(); - } From caf18a3bad49ef27e1b49c55dc096f39173dc82f Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Thu, 10 Aug 2023 14:50:05 +0100 Subject: [PATCH 32/42] multithreaded execution v2 --- examples/10.dma_test/dma_test.cc | 41 +++++++++---- sdk/core/dma/dma.h | 49 +++++++++++++++- sdk/core/dma/dma_compartment.cc | 98 +++++++++++++++++++++----------- sdk/core/dma/dma_compartment.hh | 6 +- 4 files changed, 144 insertions(+), 50 deletions(-) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index 6c67437d..da4a65ee 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -10,10 +10,15 @@ #include #include <../../sdk/core/dma/dma.h> +#include +#include + // Expose debugging features unconditionally for this compartment. using Debug = ConditionalDebug; -/// Thread entry point. +using namespace thread_pool; + +// Thread entry point. void __cheri_compartment("dma_app") dma_request() { Debug::log("DMA app entered!"); @@ -23,11 +28,13 @@ void __cheri_compartment("dma_app") dma_request() uint32_t words = bytes/4; uint32_t byteSwap = 4; - uint32_t *sourceAddress =(uint32_t*) malloc(bytes); - uint32_t *targetAddress =(uint32_t*) malloc(bytes); + uint32_t *sourceAddress = (uint32_t*) malloc(bytes); + uint32_t *targetAddress = (uint32_t*) malloc(bytes); + uint32_t *alternateAddress = (uint32_t*) malloc(bytes); for (int i=0; i < words; i++) { + *(sourceAddress + i) = i + 100; *(sourceAddress + i) = i + 200; *(targetAddress + i) = 0; @@ -38,17 +45,29 @@ void __cheri_compartment("dma_app") dma_request() DMA::Device dmaDevice; - int ret = dmaDevice.configure_and_launch(sourceAddress, targetAddress, bytes, 0, 0, byteSwap); + async([&]() { + Debug::log("Thread 1, start"); - Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", *(sourceAddress), *(sourceAddress + words -1 )); - Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + int ret = dmaDevice.configure_and_launch(sourceAddress, targetAddress, bytes, 0, 0, byteSwap); - Debug::log("ret: {}", ret); + Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", *(sourceAddress), *(sourceAddress + words -1 )); + Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); - ret = dmaDevice.configure_and_launch(targetAddress, sourceAddress, bytes, 0, 0, 0); + Debug::log("Thread 1, ret: {}", ret); + }); - Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", *(sourceAddress), *(sourceAddress + words -1 )); - Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + async([&]() + { + Debug::log("Thread 2, start"); - Debug::log("ret: {}", ret); + int ret = dmaDevice.configure_and_launch(alternateAddress, targetAddress, bytes, 0, 0, 0); + + Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", *(alternateAddress), *(alternateAddress + words -1 )); + Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + + Debug::log("Thread 2, ret: {}", ret); + }); + + Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + Debug::log("End of test"); } diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 5f31218a..785665c4 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -8,10 +8,10 @@ #include #include +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, true, true); + namespace DMA { - - uint32_t previousFutexValue; template concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *targetAddress, size_t lengthInBytes, @@ -37,6 +37,13 @@ namespace DMA int configure_and_launch(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { + /** + * Fetch the interrupt counter for the dma + * to use for future operations + */ + + const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + /** * Dma launch call: * - checks for the dma ownership status, @@ -45,9 +52,45 @@ namespace DMA * - automatically resets the claims and the dma registers * at the end of the transfer. */ - return launch_dma(sourceAddress, targetAddress, lengthInBytes, + + int dmaInterruptReturn = launch_dma(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); + /** + * Negative interrupt return is for + * the failed dma launch and forces to wait for the interrupt. + * However, wait only until timeout elapses + */ + + Timeout t{10}; + + while (dmaInterruptReturn < 0) + { + uint32_t realInterruptNumber = -(dmaInterruptReturn)-1; + + futex_timed_wait(&t, dmaFutex, realInterruptNumber); + + dmaInterruptReturn = launch_dma(sourceAddress, targetAddress, lengthInBytes, + sourceStrides, targetStrides, byteSwapAmount); + + if (!t.remaining) + { + return -EINVAL; + } + } + + /** + * Handle the interrupt here, once dmaFutex woke up via scheduler. + * DMA interrupt means that the dma operation is finished + * and it is time to reset and clear the dma configuration registers. + * Unlike with futex wait of other threads, as an occupying thread we + * wait indefinitely as much as needed for the dma completion + */ + futex_wait(dmaFutex, dmaInterruptReturn); + + reset_and_clear_dma(dmaInterruptReturn); + + return 0; } }; diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 106e3f00..cb866e13 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -21,6 +21,7 @@ using Debug = ConditionalDebug; using namespace CHERI; Ibex::PlatformDMA platformDma; + DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, true, true); namespace { @@ -29,7 +30,9 @@ namespace { * over the dma controller */ FlagLock dmaOwnershipLock; - uint32_t dmaIsRunning; + + uint32_t dmaIsLaunched = 0; + uint32_t previousInterruptCounter = 0; /** * Claims pointers so that the following @@ -43,34 +46,57 @@ namespace { int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) { - /** - * If dma is already launched, we need to check for the interrupt status - * no need for validity and permissions checks for the scheduler - * once futex is created - */ - const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); /** * Before launching a dma, check whether dma * is occupied by another thread, * else lock the ownership and start the dma - * via LockGuard + * via LockGuard. + * + * This lock automatically unlocks at the end of this function. */ LockGuard g{dmaOwnershipLock}; - if (dmaIsRunning) + + /** + * If dma is already launched, we need to check for the interrupt status. + * No need for validity and permissions checks though for the scheduler + * once futex is created + */ + + const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + uint32_t currentInterruptCounter = *dmaFutex; + + /** + * sleep a bit, so that dmaIsLaunched can be flipped successfully + * in the worst case, the caller thread will fail too + */ + + if (dmaIsLaunched) { /** - * If dma is already running, wait for the interrupt with a timeout. - * If timeout, then wait return to the original function + * If dma is already running and interrupt has not been received yet, + * return the negative incremented interrupt value, + * so that it can wait for timeout via passing this value */ + if (previousInterruptCounter == currentInterruptCounter) + { + return -(currentInterruptCounter + 1); + } - Timeout t{10}; - return futex_timed_wait(&t, dmaFutex, 0); + /* + * Potentially, redundant futex wait and context switch + * todo: do we wanna remove it and let the thread pass? + */ + uint32_t *dmaIsLaunchedPtr = &dmaIsLaunched; + futex_wait(dmaIsLaunchedPtr, dmaIsLaunched); + } - dmaIsRunning = 1; - - dmaOwnershipLock.unlock(); + /** + * Designate that the dmaIsLaunched, + * if the thread accessed the dma first + */ + dmaIsLaunched = 1; /** * After acquiring a ownership over lock, @@ -116,27 +142,19 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length platformDma.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); - - // sleep until fired interrupt is fired with futex_wait - futex_wait(dmaFutex, 0); - - // Handle the interrupt here, once dmaFutex woke up via scheduler. - // DMA interrupt means that the dma operation is finished - // and it is time to reset and clear the dma configuration registers - reset_and_clear_dma(); - - // Acknowledging interrupt here irrespective of the reset status - interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); - /** * return here, if all operations - * were successful + * were successful. + * Save the current interrupt counter to the previous one + * to differentiate betweent the start and end afterwards */ - return 0; + + previousInterruptCounter = currentInterruptCounter; + return currentInterruptCounter;; } -void reset_and_clear_dma() +void reset_and_clear_dma(uint32_t interruptNumber) { /** * Resetting the claim pointers @@ -146,9 +164,18 @@ void reset_and_clear_dma() * cleared by every DMA operation, * that is why we are explicitely resetting * them at this exit function. - * */ + + /** + * Flip the dmaIsRunnning value to show that launch is finished, + * in case finishing thread wakes from the interrupt first + */ + dmaIsLaunched = 0; + /** + * sleep until fired interrupt is fired with futex_wait + */ + Debug::log("before dropping claims"); claimedSource.reset(); @@ -159,4 +186,11 @@ void reset_and_clear_dma() */ platformDma.reset_dma(); + /** + * Acknowledging interrupt here irrespective of the reset status + */ + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + + } diff --git a/sdk/core/dma/dma_compartment.hh b/sdk/core/dma/dma_compartment.hh index e2dd31ce..12ad16bf 100644 --- a/sdk/core/dma/dma_compartment.hh +++ b/sdk/core/dma/dma_compartment.hh @@ -11,7 +11,5 @@ int __cheri_compartment("dma") launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); - - - -void __cheri_compartment("dma") reset_and_clear_dma(); \ No newline at end of file + +void __cheri_compartment("dma") reset_and_clear_dma(uint32_t interruptNumber); \ No newline at end of file From 4848b860287007c2c4f15afa86cbe61b23ed06b9 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Thu, 10 Aug 2023 17:34:17 +0100 Subject: [PATCH 33/42] combining functions to each other --- examples/10.dma_test/dma_test.cc | 2 +- examples/10.dma_test/xmake.lua | 7 ++- sdk/core/dma/dma.h | 34 +---------- sdk/core/dma/dma_compartment.cc | 98 +++++++++++++++----------------- 4 files changed, 52 insertions(+), 89 deletions(-) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index da4a65ee..e8656e0b 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -35,7 +35,7 @@ void __cheri_compartment("dma_app") dma_request() for (int i=0; i < words; i++) { *(sourceAddress + i) = i + 100; - *(sourceAddress + i) = i + 200; + *(alternateAddress + i) = i + 200; *(targetAddress + i) = 0; } diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua index e29b1ae7..c93df5ae 100644 --- a/examples/10.dma_test/xmake.lua +++ b/examples/10.dma_test/xmake.lua @@ -8,8 +8,10 @@ set_toolchains("cheriot-clang") -- Support libraries includes(path.join(sdkdir, "lib/freestanding"), + path.join(sdkdir, "lib/cxxrt"), path.join(sdkdir, "lib/atomic"), - path.join(sdkdir, "lib/crt")) + path.join(sdkdir, "lib/crt"), + path.join(sdkdir, "lib/thread_pool")) option("board") set_default("sail") @@ -22,9 +24,8 @@ compartment("dma_app") -- Firmware image for the example. firmware("dma_test") - add_deps("crt", "freestanding", "atomic_fixed") + add_deps("crt", "cxxrt", "freestanding", "atomic_fixed", "thread_pool") add_deps("dma", "dma_app") - -- add_deps("dma_app") on_load(function(target) target:values_set("board", "$(board)") target:values_set("threads", { diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 785665c4..992c38ea 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -55,40 +55,8 @@ namespace DMA int dmaInterruptReturn = launch_dma(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); + - /** - * Negative interrupt return is for - * the failed dma launch and forces to wait for the interrupt. - * However, wait only until timeout elapses - */ - - Timeout t{10}; - - while (dmaInterruptReturn < 0) - { - uint32_t realInterruptNumber = -(dmaInterruptReturn)-1; - - futex_timed_wait(&t, dmaFutex, realInterruptNumber); - - dmaInterruptReturn = launch_dma(sourceAddress, targetAddress, lengthInBytes, - sourceStrides, targetStrides, byteSwapAmount); - - if (!t.remaining) - { - return -EINVAL; - } - } - - /** - * Handle the interrupt here, once dmaFutex woke up via scheduler. - * DMA interrupt means that the dma operation is finished - * and it is time to reset and clear the dma configuration registers. - * Unlike with futex wait of other threads, as an occupying thread we - * wait indefinitely as much as needed for the dma completion - */ - futex_wait(dmaFutex, dmaInterruptReturn); - - reset_and_clear_dma(dmaInterruptReturn); return 0; } diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index cb866e13..a8bdb045 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -32,7 +32,7 @@ namespace { FlagLock dmaOwnershipLock; uint32_t dmaIsLaunched = 0; - uint32_t previousInterruptCounter = 0; + uint32_t expectedValue = 0; /** * Claims pointers so that the following @@ -48,10 +48,8 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length { /** - * Before launching a dma, check whether dma - * is occupied by another thread, - * else lock the ownership and start the dma - * via LockGuard. + * Lock this compartment via LockGuard, + * to prevent data race. * * This lock automatically unlocks at the end of this function. */ @@ -65,31 +63,25 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); uint32_t currentInterruptCounter = *dmaFutex; - - /** - * sleep a bit, so that dmaIsLaunched can be flipped successfully - * in the worst case, the caller thread will fail too + + /** + * If dma is already running, check for the + * expected and current interrupt values. + * If they do not match, wait for the interrupt. + * + * Expected Value is expected to be incremented only per thread, + * assuming that every thread enters the launch_dma() + * only once per each transfer */ + + expectedValue++; - if (dmaIsLaunched) + if (expectedValue != currentInterruptCounter) { - /** - * If dma is already running and interrupt has not been received yet, - * return the negative incremented interrupt value, - * so that it can wait for timeout via passing this value - */ - if (previousInterruptCounter == currentInterruptCounter) - { - return -(currentInterruptCounter + 1); - } + Timeout t{10}; + futex_timed_wait(&t, dmaFutex, currentInterruptCounter); - /* - * Potentially, redundant futex wait and context switch - * todo: do we wanna remove it and let the thread pass? - */ - uint32_t *dmaIsLaunchedPtr = &dmaIsLaunched; - futex_wait(dmaIsLaunchedPtr, dmaIsLaunched); - + reset_and_clear_dma(currentInterruptCounter); } /** @@ -142,15 +134,23 @@ int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t length platformDma.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); + /** + * Handle the interrupt here, once dmaFutex woke up via scheduler. + * DMA interrupt means that the dma operation is finished + * and it is time to reset and clear the dma configuration registers. + * Unlike with futex wait of other threads, as an occupying thread we + * wait indefinitely as much as needed for the dma completion + */ + futex_wait(dmaFutex, currentInterruptCounter); + + reset_and_clear_dma(currentInterruptCounter); + /** * return here, if all operations * were successful. - * Save the current interrupt counter to the previous one - * to differentiate betweent the start and end afterwards */ - previousInterruptCounter = currentInterruptCounter; - return currentInterruptCounter;; + return 0; } @@ -167,30 +167,24 @@ void reset_and_clear_dma(uint32_t interruptNumber) */ /** - * Flip the dmaIsRunnning value to show that launch is finished, - * in case finishing thread wakes from the interrupt first - */ - dmaIsLaunched = 0; - - /** - * sleep until fired interrupt is fired with futex_wait - */ - - Debug::log("before dropping claims"); - - claimedSource.reset(); - claimedDestination.reset(); - - /** - * Resetting the dma registers + * However, clear only if the addresses are not reset yet. + * Because this function can be called from two different points */ - platformDma.reset_dma(); + if (!claimedSource && !claimedDestination) + { + Debug::log("before dropping claims"); + claimedSource.reset(); + claimedDestination.reset(); - /** - * Acknowledging interrupt here irrespective of the reset status - */ - interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + /** + * Resetting the dma registers + */ + platformDma.reset_dma(); - + /** + * Acknowledging interrupt here irrespective of the reset status + */ + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + } } From a84a49a92e48fc27f56ae4a8b09a8feaef381cc7 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Fri, 11 Aug 2023 11:12:31 +0100 Subject: [PATCH 34/42] formatting fixed and locks are added --- examples/10.dma_test/dma_test.cc | 61 +++--- sdk/core/dma/dma.h | 77 ++++---- sdk/core/dma/dma_compartment.cc | 310 ++++++++++++++++--------------- sdk/core/dma/dma_compartment.hh | 18 +- sdk/core/dma/platform-dma.hh | 285 ++++++++++++++-------------- 5 files changed, 398 insertions(+), 353 deletions(-) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index e8656e0b..032c0237 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -7,8 +7,9 @@ #include #include -#include #include <../../sdk/core/dma/dma.h> +#include + #include #include @@ -24,50 +25,64 @@ void __cheri_compartment("dma_app") dma_request() Debug::log("DMA app entered!"); // This is a dma process between two different memory addresses - uint32_t bytes = 1024; - uint32_t words = bytes/4; + uint32_t bytes = 1024; + uint32_t words = bytes / 4; uint32_t byteSwap = 4; - uint32_t *sourceAddress = (uint32_t*) malloc(bytes); - uint32_t *targetAddress = (uint32_t*) malloc(bytes); - uint32_t *alternateAddress = (uint32_t*) malloc(bytes); + uint32_t *sourceAddress = (uint32_t *)malloc(bytes); + uint32_t *targetAddress = (uint32_t *)malloc(bytes); + uint32_t *alternateAddress = (uint32_t *)malloc(bytes); - for (int i=0; i < words; i++) + for (int i = 0; i < words; i++) { - *(sourceAddress + i) = i + 100; + *(sourceAddress + i) = i + 100; *(alternateAddress + i) = i + 200; - *(targetAddress + i) = 0; - + *(targetAddress + i) = 0; } - Debug::log("Ind: 0 and last, Source values BEFORE dma: {}, {}", *(sourceAddress), *(sourceAddress + words -1 )); - Debug::log("Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + Debug::log("Ind: 0 and last, Source values BEFORE dma: {}, {}", + *(sourceAddress), + *(sourceAddress + words - 1)); + Debug::log("Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); DMA::Device dmaDevice; async([&]() { Debug::log("Thread 1, start"); - int ret = dmaDevice.configure_and_launch(sourceAddress, targetAddress, bytes, 0, 0, byteSwap); + int ret = dmaDevice.configure_and_launch( + sourceAddress, targetAddress, bytes, 0, 0, byteSwap); - Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", *(sourceAddress), *(sourceAddress + words -1 )); - Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", + *(sourceAddress), + *(sourceAddress + words - 1)); + Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); - Debug::log("Thread 1, ret: {}", ret); + Debug::log("Thread 1, ret: {}", ret); }); - async([&]() - { + async([&]() { Debug::log("Thread 2, start"); - int ret = dmaDevice.configure_and_launch(alternateAddress, targetAddress, bytes, 0, 0, 0); + int ret = dmaDevice.configure_and_launch( + alternateAddress, targetAddress, bytes, 0, 0, 0); - Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", *(alternateAddress), *(alternateAddress + words -1 )); - Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", + *(alternateAddress), + *(alternateAddress + words - 1)); + Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); - Debug::log("Thread 2, ret: {}", ret); + Debug::log("Thread 2, ret: {}", ret); }); - Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words -1 )); + Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); Debug::log("End of test"); } diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 992c38ea..6e6377f9 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -3,66 +3,75 @@ #include "platform-dma.hh" #include #include +#include +#include #include #include -#include -#include -DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, true, true); -namespace DMA -{ +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, + dma, + true, + true); - template - concept IsDmaDevice = requires(T device, uint32_t *sourceAddress, uint32_t *targetAddress, size_t lengthInBytes, - uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) +namespace DMA +{ + + template + concept IsDmaDevice = requires(T device, + uint32_t *sourceAddress, + uint32_t *targetAddress, + size_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) { { - device.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, - sourceStrides, targetStrides, byteSwapAmount) + device.write_conf_and_start(sourceAddress, + targetAddress, + lengthInBytes, + sourceStrides, + targetStrides, + byteSwapAmount) } -> std::same_as; - { + { device.reset_dma() } -> std::same_as; }; - template + template requires IsDmaDevice class GenericDMA : public PlatformDMA { public: - - int configure_and_launch(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, - uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) - { + int configure_and_launch(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) + { /** - * Fetch the interrupt counter for the dma - * to use for future operations - */ - - const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); - - /** - * Dma launch call: - * - checks for the dma ownership status, + * Dma launch call: + * - checks for the dma ownership status, * - for access rights, * - creates claims for each source and destination addresses, * - automatically resets the claims and the dma registers * at the end of the transfer. */ - - int dmaInterruptReturn = launch_dma(sourceAddress, targetAddress, lengthInBytes, - sourceStrides, targetStrides, byteSwapAmount); - - + + int dmaInterruptReturn = launch_dma(sourceAddress, + targetAddress, + lengthInBytes, + sourceStrides, + targetStrides, + byteSwapAmount); return 0; } - }; - using Device = GenericDMA; -} - + using Device = GenericDMA; +} // namespace DMA diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index a8bdb045..3b35b32e 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -3,16 +3,18 @@ #include "dma_compartment.hh" #include -#include #include +#include + +#include "platform-dma.hh" #include #include -#include -#include "platform-dma.hh" #include #include #include +#include + // Expose debugging features unconditionally for this compartment. using Debug = ConditionalDebug; @@ -22,169 +24,183 @@ using namespace CHERI; Ibex::PlatformDMA platformDma; -DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, true, true); +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, + dma, + true, + true); -namespace { - /** - * Flag lock to control ownership +namespace +{ + /** + * Flag lock to control ownership * over the dma controller - */ + */ FlagLock dmaOwnershipLock; - uint32_t dmaIsLaunched = 0; - uint32_t expectedValue = 0; + uint32_t dmaIsLaunched = 0; + uint32_t expectedValue = 0; - /** - * Claims pointers so that the following - * addresses will not be freed ahead of time - */ - std::unique_ptr claimedSource; - std::unique_ptr claimedDestination; + /** + * Claims pointers so that the following + * addresses will not be freed ahead of time + */ + std::unique_ptr claimedSource; + std::unique_ptr claimedDestination; } // namespace -int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, - uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) -{ - - /** - * Lock this compartment via LockGuard, - * to prevent data race. - * - * This lock automatically unlocks at the end of this function. - */ +int launch_dma(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) +{ + /** + * Lock this compartment via LockGuard, + * to prevent data race. + * + * This lock automatically unlocks at the end of this function. + */ LockGuard g{dmaOwnershipLock}; - /** - * If dma is already launched, we need to check for the interrupt status. - * No need for validity and permissions checks though for the scheduler - * once futex is created - */ - - const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); - uint32_t currentInterruptCounter = *dmaFutex; - - /** - * If dma is already running, check for the - * expected and current interrupt values. - * If they do not match, wait for the interrupt. - * - * Expected Value is expected to be incremented only per thread, - * assuming that every thread enters the launch_dma() - * only once per each transfer - */ - - expectedValue++; - - if (expectedValue != currentInterruptCounter) - { - Timeout t{10}; - futex_timed_wait(&t, dmaFutex, currentInterruptCounter); - - reset_and_clear_dma(currentInterruptCounter); - } - - /** - * Designate that the dmaIsLaunched, - * if the thread accessed the dma first - */ - dmaIsLaunched = 1; - - /** - * After acquiring a ownership over lock, + /** + * If dma is already launched, we need to check for the interrupt status. + * No need for validity and permissions checks though for the scheduler + * once futex is created + */ + + const uint32_t *dmaFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + uint32_t currentInterruptCounter = *dmaFutex; + + /** + * If dma is already running, check for the + * expected and current interrupt values. + * If they do not match, wait for the interrupt. + * + * Expected Value is expected to be incremented only per thread, + * assuming that every thread enters the launch_dma() + * only once per each transfer + */ + + if (expectedValue != currentInterruptCounter) + { + Timeout t{10}; + futex_timed_wait(&t, dmaFutex, expectedValue - 1); + + reset_and_clear_dma(); + } + + /** + * After acquiring a ownership over lock, * now it is the time to launch dma. - * - * Here, we claim the memory with default malloc capability, - * as declaring another heap capability is an extra entry - * that leaves the default capability let unused. - * - * Unique pointers are used to avoid explicit heap_free() calls. - * when this pointer is not used anymore, it is automatically deleted - */ - - auto claim = [](void *ptr) -> std::unique_ptr - { - if (heap_claim(MALLOC_CAPABILITY, ptr) == 0) - { - return {nullptr}; - } - - return std::unique_ptr { static_cast(ptr)}; - }; - - claimedSource = claim(sourceAddress); - claimedDestination = claim(targetAddress); - - if (!claimedSource || !claimedDestination) - { - return -EINVAL; - } - - /** - * return if sufficient permissions are not present - * and if not long enough - */ - - if (!check_pointer(sourceAddress, lengthInBytes) || - !check_pointer(targetAddress, lengthInBytes) ) - { - return -EINVAL; - } - - platformDma.write_conf_and_start(sourceAddress, targetAddress, lengthInBytes, - sourceStrides, targetStrides, byteSwapAmount); - - /** + * + * Here, we claim the memory with default malloc capability, + * as declaring another heap capability is an extra entry + * that leaves the default capability let unused. + * + * Unique pointers are used to avoid explicit heap_free() calls. + * when this pointer is not used anymore, it is automatically deleted + */ + + auto claim = [](void *ptr) -> std::unique_ptr { + if (heap_claim(MALLOC_CAPABILITY, ptr) == 0) + { + return {nullptr}; + } + + return std::unique_ptr{static_cast(ptr)}; + }; + + claimedSource = claim(sourceAddress); + claimedDestination = claim(targetAddress); + + if (!claimedSource || !claimedDestination) + { + return -EINVAL; + } + + /** + * return if sufficient permissions are not present + * and if not long enough + */ + + if (!check_pointer( + sourceAddress, lengthInBytes) || + !check_pointer( + targetAddress, lengthInBytes)) + { + return -EINVAL; + } + + platformDma.write_conf_and_start(sourceAddress, + targetAddress, + lengthInBytes, + sourceStrides, + targetStrides, + byteSwapAmount); + + /** + * Increment the expected value only when + * dma has started to avoid the potential deadlock + * of this function is returned with failure earlier + */ + expectedValue++; + + /** * Handle the interrupt here, once dmaFutex woke up via scheduler. - * DMA interrupt means that the dma operation is finished + * DMA interrupt means that the dma operation is finished * and it is time to reset and clear the dma configuration registers. - * Unlike with futex wait of other threads, as an occupying thread we - * wait indefinitely as much as needed for the dma completion + * Unlike with futex wait of other threads, as an occupying thread we + * wait indefinitely as much as needed for the dma completion */ futex_wait(dmaFutex, currentInterruptCounter); - reset_and_clear_dma(currentInterruptCounter); + reset_and_clear_dma(); - /** - * return here, if all operations - * were successful. - */ - - return 0; + /** + * return here, if all operations + * were successful. + */ + return 0; } -void reset_and_clear_dma(uint32_t interruptNumber) -{ - /** - * Resetting the claim pointers - * and cleaning up the dma registers. - * - * Claim registers are meant to be - * cleared by every DMA operation, - * that is why we are explicitely resetting - * them at this exit function. - */ - - /** - * However, clear only if the addresses are not reset yet. - * Because this function can be called from two different points - */ - if (!claimedSource && !claimedDestination) - { - Debug::log("before dropping claims"); - claimedSource.reset(); - claimedDestination.reset(); - - /** - * Resetting the dma registers - */ - platformDma.reset_dma(); - - /** - * Acknowledging interrupt here irrespective of the reset status - */ - interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); - } +void reset_and_clear_dma() +{ + /** + * Resetting the claim pointers + * and cleaning up the dma registers. + * + * Claim registers are meant to be + * cleared by every DMA operation, + * that is why we are explicitely resetting + * them at this exit function. + */ + + /** + * However, clear only if the addresses are not reset yet. + * Because this function can be called from two different points + */ + + LockGuard g{dmaOwnershipLock}; + if (claimedSource || claimedDestination) + { + Debug::log("before dropping claims"); + claimedSource.reset(); + claimedDestination.reset(); + + /** + * Resetting the dma registers + */ + platformDma.reset_dma(); + + /** + * Acknowledging interrupt here irrespective of the reset status + */ + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + } } diff --git a/sdk/core/dma/dma_compartment.hh b/sdk/core/dma/dma_compartment.hh index 12ad16bf..8e6f9dc3 100644 --- a/sdk/core/dma/dma_compartment.hh +++ b/sdk/core/dma/dma_compartment.hh @@ -5,11 +5,15 @@ /** * A function below claims the source and target addresses of the DMA interface. - * While, DMA is in progress, these addresses will be claimed by the DMA compartment - * and so the memory will not be freed. + * While, DMA is in progress, these addresses will be claimed by the DMA + * compartment and so the memory will not be freed. */ - -int __cheri_compartment("dma") launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, - uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount); - -void __cheri_compartment("dma") reset_and_clear_dma(uint32_t interruptNumber); \ No newline at end of file + +int __cheri_compartment("dma") launch_dma(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount); + +void __cheri_compartment("dma") reset_and_clear_dma(); \ No newline at end of file diff --git a/sdk/core/dma/platform-dma.hh b/sdk/core/dma/platform-dma.hh index 12e24e6c..02c96f8d 100644 --- a/sdk/core/dma/platform-dma.hh +++ b/sdk/core/dma/platform-dma.hh @@ -1,155 +1,156 @@ #pragma once +#include #include #include #include -#include -#include #include +#include + // Expose debugging features unconditionally for this compartment. using Debug = ConditionalDebug; -namespace Ibex { - class PlatformDMA { - private: - - struct DMAInterface - { - /** - * Below is DMA control register address: - * - includes start('b0), endianness conversion('b1 and 2) and reset bits('b3) - * - bit of index 1 and 2 are for enabling 2 and 4 byte swaps respectively - * - start bit is meant to be set at the end while programming - */ - uint32_t control; - /** - * Below is DMA status register address - * - first bit refers to halted status - * - 0 is for idle, and 1 for running - */ - uint32_t status; - /** - * Below is DMA source address register address: - * - here source address is where FROM the DMA should transfer the data - * - it can be either memory buffer or MMAP-ed I/O device (i.e. any peripheral or accelerator) - */ - uint32_t sourceAddress; - /** - * Below is DMA target address register address: - * - here target address is where TO the DMA should transfer the data - * - it can be either memory buffer or MMAP-ed I/O device (i.e. any peripheral or accelerator) - */ - uint32_t targetAddress; - /** - * Below is the amount of data IN BYTES that DMA should transfer - * between the source and a target - */ - uint32_t lengthInBytes; - /** - * Below is the amount of strides IN 4 BYTE WORDS for a source address. - * Strides is the jump amount between each data retrieval address - * during the DMA operation. - * Minimum width of the stride is 4 byte words. - * - 0 stride is default and points to the address of the next word - * - 1 is for 1 word skippal, and 2 for 2 before the next address - * - todo: more fine grain stride configurability (in term of bytes) for the future? - */ - uint32_t sourceStrides; - /** - * Below is the amount of strides IN 4 BYTE WORDS for a target address. - * The same as above but just for a target address. So that we can fetch - * and transfer the data at different stride rates - */ - uint32_t targetStrides; - }; - - __always_inline volatile DMAInterface &device() - { - return *MMIO_CAPABILITY(DMAInterface, dma); - } - - void write_strides(uint32_t sourceStrides, uint32_t targetStrides) - { - /** - * Setting source and target strides - */ - device().sourceStrides = sourceStrides; - device().targetStrides = targetStrides; - } - - int swap_bytes_and_start_dma(uint32_t swapAmount) - { - /** - * Setting byte swaps - * and start bit. - * - * Swap amount can be equal to - * only either 2 or 4. - */ - - if (swapAmount != 2) - { - if (swapAmount != 4) - { - swapAmount = 0; - } - } - - uint32_t controlConfiguration = swapAmount | 0x1; - - device().control = controlConfiguration; - - - return 0; - } - - // void start_dma() - // { - // /** - // * Setting a start bit - // */ - // device().control = 1; - // } - - public: - - uint32_t read_status() - { - /** - * this statement returns the less signifance bit - * to show the halted status - */ - return device().status & 0x1; - } - - void write_conf_and_start(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, - uint32_t sourceStrides, uint32_t targetStrides, uint32_t byteSwapAmount) - { - /** - * Setting source and target addresses, and length fields - */ - device().sourceAddress = CHERI::Capability{sourceAddress}.address(); - device().targetAddress = CHERI::Capability{targetAddress}.address(); - device().lengthInBytes = lengthInBytes; - - write_strides(sourceStrides, targetStrides); - - swap_bytes_and_start_dma(byteSwapAmount); - } - - void reset_dma() - { - /** - * Setting a reset bit, which is bit 3. - * this clears all the registers, - * but do not transfer the current transfer status anywhere - */ - device().control = 0x8; - - } - - }; +namespace Ibex +{ + class PlatformDMA + { + private: + struct DMAInterface + { + /** + * Below is DMA control register address: + * - includes start('b0), endianness conversion('b1 and 2) and + * reset bits('b3) + * - bit of index 1 and 2 are for enabling 2 and 4 byte swaps + * respectively + * - start bit is meant to be set at the end while programming + */ + uint32_t control; + /** + * Below is DMA status register address + * - first bit refers to halted status + * - 0 is for idle, and 1 for running + */ + uint32_t status; + /** + * Below is DMA source address register address: + * - here source address is where FROM the DMA should transfer the + * data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any + * peripheral or accelerator) + */ + uint32_t sourceAddress; + /** + * Below is DMA target address register address: + * - here target address is where TO the DMA should transfer the + * data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any + * peripheral or accelerator) + */ + uint32_t targetAddress; + /** + * Below is the amount of data IN BYTES that DMA should transfer + * between the source and a target + */ + uint32_t lengthInBytes; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a source + * address. Strides is the jump amount between each data retrieval + * address during the DMA operation. Minimum width of the stride is + * 4 byte words. + * - 0 stride is default and points to the address of the next word + * - 1 is for 1 word skippal, and 2 for 2 before the next address + * - todo: more fine grain stride configurability (in term of + * bytes) for the future? + */ + uint32_t sourceStrides; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a target + * address. The same as above but just for a target address. So that + * we can fetch and transfer the data at different stride rates + */ + uint32_t targetStrides; + }; + + __always_inline volatile DMAInterface &device() + { + return *MMIO_CAPABILITY(DMAInterface, dma); + } + + void write_strides(uint32_t sourceStrides, uint32_t targetStrides) + { + /** + * Setting source and target strides + */ + device().sourceStrides = sourceStrides; + device().targetStrides = targetStrides; + } + + int swap_bytes_and_start_dma(uint32_t swapAmount) + { + /** + * Setting byte swaps + * and start bit. + * + * Swap amount can be equal to + * only either 2 or 4. + */ + + if (swapAmount != 2) + { + if (swapAmount != 4) + { + swapAmount = 0; + } + } + + uint32_t controlConfiguration = swapAmount | 0x1; + + device().control = controlConfiguration; + + return 0; + } + + public: + uint32_t read_status() + { + /** + * this statement returns the less signifance bit + * to show the halted status + */ + return device().status & 0x1; + } + + void write_conf_and_start(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) + { + /** + * Setting source and target addresses, and length fields + */ + device().sourceAddress = CHERI::Capability{sourceAddress}.address(); + device().targetAddress = CHERI::Capability{targetAddress}.address(); + device().lengthInBytes = lengthInBytes; + + write_strides(sourceStrides, targetStrides); + + swap_bytes_and_start_dma(byteSwapAmount); + } + + void reset_dma() + { + /** + * Setting a reset bit, which is bit 3. + * this clears all the registers, + * but do not transfer the current transfer status anywhere + */ + device().control = 0x8; + } + }; } // namespace Ibex // template From 166213d9ea71d2e6d96364df6efff21f1507636b Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Fri, 11 Aug 2023 11:38:20 +0100 Subject: [PATCH 35/42] added worker threads to the firmware --- examples/10.dma_test/xmake.lua | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua index c93df5ae..1b716d66 100644 --- a/examples/10.dma_test/xmake.lua +++ b/examples/10.dma_test/xmake.lua @@ -31,10 +31,24 @@ firmware("dma_test") target:values_set("threads", { { compartment = "dma_app", - priority = 1, + priority = 2, entry_point = "dma_request", stack_size = 0x400, - trusted_stack_frames = 2 + trusted_stack_frames = 9 + }, + { + compartment = "thread_pool", + priority = 1, + entry_point = "thread_pool_run", + stack_size = 0x600, + trusted_stack_frames = 8 + }, + { + compartment = "thread_pool", + priority = 1, + entry_point = "thread_pool_run", + stack_size = 0x600, + trusted_stack_frames = 8 } }, {expand = false}) end) From 3f0dba742421997f35eb1cb473896de1a0b20701 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Fri, 11 Aug 2023 13:00:14 +0100 Subject: [PATCH 36/42] fixed copy by reference from stack and returned to two finction api for dma.h --- examples/10.dma_test/dma_test.cc | 6 +-- sdk/core/dma/dma.h | 4 +- sdk/core/dma/dma_compartment.cc | 63 +++++++++++++++++++------------- sdk/core/dma/dma_compartment.hh | 2 +- 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index 032c0237..776843e3 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -47,9 +47,9 @@ void __cheri_compartment("dma_app") dma_request() *(targetAddress), *(targetAddress + words - 1)); - DMA::Device dmaDevice; + static DMA::Device dmaDevice; - async([&]() { + async([=]() { Debug::log("Thread 1, start"); int ret = dmaDevice.configure_and_launch( @@ -65,7 +65,7 @@ void __cheri_compartment("dma_app") dma_request() Debug::log("Thread 1, ret: {}", ret); }); - async([&]() { + async([=]() { Debug::log("Thread 2, start"); int ret = dmaDevice.configure_and_launch( diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 6e6377f9..87feb345 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -62,13 +62,15 @@ namespace DMA * at the end of the transfer. */ - int dmaInterruptReturn = launch_dma(sourceAddress, + uint32_t dmaInterruptReturn = launch_dma(sourceAddress, targetAddress, lengthInBytes, sourceStrides, targetStrides, byteSwapAmount); + wait_and_reset_dma(dmaInterruptReturn); + return 0; } }; diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma/dma_compartment.cc index 3b35b32e..ccdb39ed 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma/dma_compartment.cc @@ -6,7 +6,6 @@ #include #include - #include "platform-dma.hh" #include #include @@ -15,7 +14,6 @@ #include #include - // Expose debugging features unconditionally for this compartment. using Debug = ConditionalDebug; @@ -37,7 +35,6 @@ namespace */ FlagLock dmaOwnershipLock; - uint32_t dmaIsLaunched = 0; uint32_t expectedValue = 0; /** @@ -49,6 +46,8 @@ namespace } // namespace +void internal_wait_and_reset_dma(uint32_t interruptNumber); + int launch_dma(uint32_t *sourceAddress, uint32_t *targetAddress, uint32_t lengthInBytes, @@ -70,7 +69,7 @@ int launch_dma(uint32_t *sourceAddress, * once futex is created */ - const uint32_t *dmaFutex = + static const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); uint32_t currentInterruptCounter = *dmaFutex; @@ -84,15 +83,16 @@ int launch_dma(uint32_t *sourceAddress, * assuming that every thread enters the launch_dma() * only once per each transfer */ - + if (expectedValue != currentInterruptCounter) { - Timeout t{10}; - futex_timed_wait(&t, dmaFutex, expectedValue - 1); - - reset_and_clear_dma(); + internal_wait_and_reset_dma(currentInterruptCounter); } + Debug::Assert( + expectedValue == *dmaFutex, + "ExpectedValue is not equal to the current interrupt counter!"); + /** * After acquiring a ownership over lock, * now it is the time to launch dma. @@ -143,33 +143,22 @@ int launch_dma(uint32_t *sourceAddress, byteSwapAmount); /** - * Increment the expected value only when + * Increment the expected value only when * dma has started to avoid the potential deadlock * of this function is returned with failure earlier */ expectedValue++; - /** - * Handle the interrupt here, once dmaFutex woke up via scheduler. - * DMA interrupt means that the dma operation is finished - * and it is time to reset and clear the dma configuration registers. - * Unlike with futex wait of other threads, as an occupying thread we - * wait indefinitely as much as needed for the dma completion - */ - futex_wait(dmaFutex, currentInterruptCounter); - - reset_and_clear_dma(); - /** * return here, if all operations * were successful. */ - return 0; + return currentInterruptCounter; } -void reset_and_clear_dma() -{ +void internal_wait_and_reset_dma(uint32_t interruptNumber) +{ /** * Resetting the claim pointers * and cleaning up the dma registers. @@ -185,8 +174,20 @@ void reset_and_clear_dma() * Because this function can be called from two different points */ - LockGuard g{dmaOwnershipLock}; + /** + * Also, handle the interrupt here, once dmaFutex woke up via scheduler. + * DMA interrupt means that the dma operation is finished + * and it is time to reset and clear the dma configuration registers. + * Unlike with futex wait of other threads, as an occupying thread we + * wait indefinitely as much as needed for the dma completion + */ + + static const uint32_t *dmaFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + Timeout t{10}; + futex_timed_wait(&t, dmaFutex, interruptNumber); + if (claimedSource || claimedDestination) { Debug::log("before dropping claims"); @@ -197,10 +198,20 @@ void reset_and_clear_dma() * Resetting the dma registers */ platformDma.reset_dma(); - + /** * Acknowledging interrupt here irrespective of the reset status */ interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); } } + +void wait_and_reset_dma(uint32_t interruptNumber) +{ + /** + * This lock is to avoid the data race as well + */ + LockGuard g{dmaOwnershipLock}; + + internal_wait_and_reset_dma(interruptNumber); +} diff --git a/sdk/core/dma/dma_compartment.hh b/sdk/core/dma/dma_compartment.hh index 8e6f9dc3..07f928c0 100644 --- a/sdk/core/dma/dma_compartment.hh +++ b/sdk/core/dma/dma_compartment.hh @@ -16,4 +16,4 @@ int __cheri_compartment("dma") launch_dma(uint32_t *sourceAddress, uint32_t targetStrides, uint32_t byteSwapAmount); -void __cheri_compartment("dma") reset_and_clear_dma(); \ No newline at end of file +void __cheri_compartment("dma") wait_and_reset_dma(uint32_t interruptNumber); \ No newline at end of file From 39b5299125e11b7929faab601086060d28553d22 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Fri, 11 Aug 2023 16:30:32 +0100 Subject: [PATCH 37/42] some debugging code --- examples/10.dma_test/dma_test.cc | 71 +++++++++++++++++++++----------- examples/10.dma_test/xmake.lua | 2 +- sdk/core/dma/dma.h | 10 ++++- 3 files changed, 56 insertions(+), 27 deletions(-) diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma_test.cc index 776843e3..7f88c348 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma_test.cc @@ -8,8 +8,7 @@ #include #include <../../sdk/core/dma/dma.h> -#include - +// #include #include #include @@ -19,10 +18,32 @@ using Debug = ConditionalDebug; using namespace thread_pool; +#include <../../tests/crash_recovery.h> + +using DebugErrorHandler = ConditionalDebug; + +int crashes = 0; + +extern "C" enum ErrorRecoveryBehaviour +compartment_error_handler(struct ErrorState *frame, size_t mcause, size_t mtval) +{ + Debug::log("Test saw error for PCC {}", frame->pcc); + Debug::log("Error cause: {}, mtval: {}", mcause, mtval); + if (mcause == 0x2) + { + Debug::log("Test hit assertion failure, unwinding"); + return ErrorRecoveryBehaviour::ForceUnwind; + } + + crashes++; + Debug::log("Resuming test at failure location"); + return ErrorRecoveryBehaviour::InstallContext; +} + // Thread entry point. void __cheri_compartment("dma_app") dma_request() { - Debug::log("DMA app entered!"); + Debug::log("DMA app entered, v2!"); // This is a dma process between two different memory addresses uint32_t bytes = 1024; @@ -40,10 +61,10 @@ void __cheri_compartment("dma_app") dma_request() *(targetAddress + i) = 0; } - Debug::log("Ind: 0 and last, Source values BEFORE dma: {}, {}", + Debug::log("M:Ind: 0 and last, Source values BEFORE dma: {}, {}", *(sourceAddress), *(sourceAddress + words - 1)); - Debug::log("Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", + Debug::log("M: Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", *(targetAddress), *(targetAddress + words - 1)); @@ -52,37 +73,37 @@ void __cheri_compartment("dma_app") dma_request() async([=]() { Debug::log("Thread 1, start"); - int ret = dmaDevice.configure_and_launch( - sourceAddress, targetAddress, bytes, 0, 0, byteSwap); + // int ret = dmaDevice.configure_and_launch( + // sourceAddress, targetAddress, bytes, 0, 0, byteSwap); - Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", - *(sourceAddress), - *(sourceAddress + words - 1)); - Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", - *(targetAddress), - *(targetAddress + words - 1)); + // Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", + // *(sourceAddress), + // *(sourceAddress + words - 1)); + // Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + // *(targetAddress), + // *(targetAddress + words - 1)); - Debug::log("Thread 1, ret: {}", ret); + // Debug::log("Thread 1, ret: {}", ret); }); async([=]() { Debug::log("Thread 2, start"); - int ret = dmaDevice.configure_and_launch( - alternateAddress, targetAddress, bytes, 0, 0, 0); + // int ret = dmaDevice.configure_and_launch( + // alternateAddress, targetAddress, bytes, 0, 0, 0); - Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", - *(alternateAddress), - *(alternateAddress + words - 1)); - Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", - *(targetAddress), - *(targetAddress + words - 1)); + // Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", + // *(alternateAddress), + // *(alternateAddress + words - 1)); + // Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + // *(targetAddress), + // *(targetAddress + words - 1)); - Debug::log("Thread 2, ret: {}", ret); + // Debug::log("Thread 2, ret: {}", ret); }); - Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + Debug::log("M: Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words - 1)); - Debug::log("End of test"); + Debug::log("M: End of test"); } diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua index 1b716d66..1ba9de7f 100644 --- a/examples/10.dma_test/xmake.lua +++ b/examples/10.dma_test/xmake.lua @@ -33,7 +33,7 @@ firmware("dma_test") compartment = "dma_app", priority = 2, entry_point = "dma_request", - stack_size = 0x400, + stack_size = 0x1000, trusted_stack_frames = 9 }, { diff --git a/sdk/core/dma/dma.h b/sdk/core/dma/dma.h index 87feb345..44767c83 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma/dma.h @@ -61,7 +61,8 @@ namespace DMA * - automatically resets the claims and the dma registers * at the end of the transfer. */ - + Debug::log("before launch dma"); + uint32_t dmaInterruptReturn = launch_dma(sourceAddress, targetAddress, lengthInBytes, @@ -69,6 +70,13 @@ namespace DMA targetStrides, byteSwapAmount); + Debug::log("launch dma return: {}", dmaInterruptReturn); + + if (dmaInterruptReturn < 0) + { + return -EINVAL; + } + wait_and_reset_dma(dmaInterruptReturn); return 0; From f4c2ffb20fabdaecc14453ba0dd95606e0582a87 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 14 Aug 2023 19:48:09 +0100 Subject: [PATCH 38/42] changes to integrate the dma to the tests and multithreaded execution --- .../10.dma_test/{dma_test.cc => dma-test.cc} | 6 +- examples/10.dma_test/xmake.lua | 12 +-- tests/dma-test.cc | 90 +++++++++++++++++++ tests/test-runner.cc | 23 ++--- tests/tests.hh | 1 + tests/xmake.lua | 86 +++++++++--------- 6 files changed, 158 insertions(+), 60 deletions(-) rename examples/10.dma_test/{dma_test.cc => dma-test.cc} (96%) create mode 100644 tests/dma-test.cc diff --git a/examples/10.dma_test/dma_test.cc b/examples/10.dma_test/dma-test.cc similarity index 96% rename from examples/10.dma_test/dma_test.cc rename to examples/10.dma_test/dma-test.cc index 7f88c348..c9a4a7f8 100644 --- a/examples/10.dma_test/dma_test.cc +++ b/examples/10.dma_test/dma-test.cc @@ -24,7 +24,7 @@ using DebugErrorHandler = ConditionalDebug; int crashes = 0; -extern "C" enum ErrorRecoveryBehaviour +extern "C" ErrorRecoveryBehaviour compartment_error_handler(struct ErrorState *frame, size_t mcause, size_t mtval) { Debug::log("Test saw error for PCC {}", frame->pcc); @@ -41,7 +41,7 @@ compartment_error_handler(struct ErrorState *frame, size_t mcause, size_t mtval) } // Thread entry point. -void __cheri_compartment("dma_app") dma_request() +void __cheri_compartment("dma_test") test_dma() { Debug::log("DMA app entered, v2!"); @@ -68,7 +68,7 @@ void __cheri_compartment("dma_app") dma_request() *(targetAddress), *(targetAddress + words - 1)); - static DMA::Device dmaDevice; + // static DMA::Device dmaDevice; async([=]() { Debug::log("Thread 1, start"); diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua index 1ba9de7f..d84e2b78 100644 --- a/examples/10.dma_test/xmake.lua +++ b/examples/10.dma_test/xmake.lua @@ -19,20 +19,20 @@ option("board") compartment("dma") add_files(path.join(sdkdir, "core/dma/dma_compartment.cc")) -compartment("dma_app") - add_files("dma_test.cc") +compartment("dma_test") + add_files("dma-test.cc") -- Firmware image for the example. -firmware("dma_test") +firmware("dma-test") add_deps("crt", "cxxrt", "freestanding", "atomic_fixed", "thread_pool") - add_deps("dma", "dma_app") + add_deps("dma", "dma_test") on_load(function(target) target:values_set("board", "$(board)") target:values_set("threads", { { - compartment = "dma_app", + compartment = "dma_test", priority = 2, - entry_point = "dma_request", + entry_point = "test_dma", stack_size = 0x1000, trusted_stack_frames = 9 }, diff --git a/tests/dma-test.cc b/tests/dma-test.cc new file mode 100644 index 00000000..40036181 --- /dev/null +++ b/tests/dma-test.cc @@ -0,0 +1,90 @@ +// Copyright Microsoft and CHERIoT Contributors. +// SPDX-License-Identifier: MIT +#define TEST_NAME "DMA" +#include "tests.hh" +#include + +#include +#include +#include +#include +#include + +#include <../../sdk/core/dma/dma.h> + +#include +#include + +using namespace thread_pool; + +// Thread entry point. +void test_dma() +{ + debug_log("DMA app entered, v14!"); + + // This is a dma process between two different memory addresses + uint32_t bytes = 1024; + uint32_t words = bytes / 4; + uint32_t byteSwap = 0; + + uint32_t *sourceAddress = (uint32_t *)malloc(bytes); + uint32_t *targetAddress = (uint32_t *)malloc(bytes); + uint32_t *alternateAddress = (uint32_t *)malloc(bytes); + + for (int i = 0; i < words; i++) + { + *(sourceAddress + i) = i + 100; + *(alternateAddress + i) = i + 200; + *(targetAddress + i) = 0; + } + + debug_log("M:Ind: 0 and last, Source values BEFORE dma: {}, {}", + *(sourceAddress), + *(sourceAddress + words - 1)); + + debug_log("M:Ind: 0 and last, Source values BEFORE dma: {}, {}", + *(alternateAddress), + *(alternateAddress + words - 1)); + + debug_log("M: Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + + static DMA::Device dmaDevice; + + async([=]() { + debug_log("Thread 1, start"); + + int ret = dmaDevice.configure_and_launch( + sourceAddress, targetAddress, bytes, 0, 0, byteSwap); + + debug_log("Thread 1, ret: {}", ret); + }); + + async([=]() { + debug_log("Thread 2, start"); + + int ret = dmaDevice.configure_and_launch( + alternateAddress, targetAddress, bytes, 0, 0, 0); + + debug_log("Thread 2, ret: {}", ret); + }); + + // here, we are just forcing to sleep + // however, for experimental numbers + // we need to make sure to make a fare analysis + Timeout t{50}; + thread_sleep(&t); + + debug_log("Ind: 0 and last, Source values AFTER dma: {}, {}", + *(sourceAddress), + *(sourceAddress + words - 1)); + debug_log("Ind: 0 and last, Source values AFTER dma: {}, {}", + *(alternateAddress), + *(alternateAddress + words - 1)); + debug_log("M: Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + + debug_log("M: End of test"); +} diff --git a/tests/test-runner.cc b/tests/test-runner.cc index 8f3e8be5..ea20b730 100644 --- a/tests/test-runner.cc +++ b/tests/test-runner.cc @@ -92,17 +92,18 @@ void __cheri_compartment("test_runner") run_tests() } run_timed("All tests", []() { - run_timed("Static sealing", test_static_sealing); - run_timed("Crash recovery", test_crash_recovery); - run_timed("Compartment calls", test_compartment_call); - run_timed("Stacks exhaustion in the switcher", test_stack); - run_timed("Thread pool", test_thread_pool); - run_timed("Global Constructors", test_global_constructors); - run_timed("Queue", test_queue); - run_timed("Futex", test_futex); - run_timed("Locks", test_locks); - run_timed("Multiwaiter", test_multiwaiter); - run_timed("Allocator", test_allocator); + // run_timed("Static sealing", test_static_sealing); + // run_timed("Crash recovery", test_crash_recovery); + // run_timed("Compartment calls", test_compartment_call); + // run_timed("Stacks exhaustion in the switcher", test_stack); + // run_timed("Thread pool", test_thread_pool); + // run_timed("Global Constructors", test_global_constructors); + // run_timed("Queue", test_queue); + // run_timed("Futex", test_futex); + // run_timed("Locks", test_locks); + // run_timed("Multiwaiter", test_multiwaiter); + // run_timed("Allocator", test_allocator); + run_timed("DMA", test_dma); }); TEST(crashDetected == false, "One or more tests failed"); diff --git a/tests/tests.hh b/tests/tests.hh index 9b9687fb..10441a5e 100644 --- a/tests/tests.hh +++ b/tests/tests.hh @@ -15,6 +15,7 @@ __cheri_compartment("multiwaiter_test") void test_multiwaiter(); __cheri_compartment("stack_test") void test_stack(); __cheri_compartment("compartment_calls_test") void test_compartment_call(); __cheri_compartment("static_sealing_test") void test_static_sealing(); +__cheri_compartment("dma_test") void test_dma(); // Simple tests don't need a separate compartment. void test_global_constructors(); diff --git a/tests/xmake.lua b/tests/xmake.lua index 605958bd..1129ee3b 100644 --- a/tests/xmake.lua +++ b/tests/xmake.lua @@ -23,38 +23,42 @@ function test_c(name) end -- Test the allocator and the revoker. -test("allocator") +-- test("allocator") -- Test the thread pool test("thread_pool") --- Test the futex implementation -test("futex") --- Test locks built on top of the futex -test("queue") --- Test queues -test("locks") --- Test the static sealing types -test("static_sealing") -compartment("static_sealing_inner") - add_files("static_sealing_inner.cc") --- Test crash recovery. -compartment("crash_recovery_inner") - add_files("crash_recovery_inner.cc") -compartment("crash_recovery_outer") - add_files("crash_recovery_outer.cc") -test("crash_recovery") --- Test the multiwaiter -test("multiwaiter") --- Test that C code can compile -test_c("ccompile") --- Test stacks -compartment("stack_integrity_thread") - add_files("stack_integrity_thread.cc") -test("stack") -compartment("compartment_calls_inner") - add_files("compartment_calls_inner.cc") -compartment("compartment_calls_inner_with_handler") - add_files("compartment_calls_inner_with_handler.cc") -test("compartment_calls") +-- -- Test the futex implementation +-- test("futex") +-- -- Test locks built on top of the futex +-- test("queue") +-- -- Test queues +-- test("locks") +-- -- Test the static sealing types +-- test("static_sealing") +-- compartment("static_sealing_inner") +-- add_files("static_sealing_inner.cc") +-- -- Test crash recovery. +-- compartment("crash_recovery_inner") +-- add_files("crash_recovery_inner.cc") +-- compartment("crash_recovery_outer") +-- add_files("crash_recovery_outer.cc") +-- test("crash_recovery") +-- -- Test the multiwaiter +-- test("multiwaiter") +-- -- Test that C code can compile +-- test_c("ccompile") +-- -- Test stacks +-- compartment("stack_integrity_thread") +-- add_files("stack_integrity_thread.cc") +-- test("stack") +-- compartment("compartment_calls_inner") +-- add_files("compartment_calls_inner.cc") +-- compartment("compartment_calls_inner_with_handler") +-- add_files("compartment_calls_inner_with_handler.cc") +-- test("compartment_calls") +compartment("dma") + add_files(path.join(sdkdir, "core/dma/dma_compartment.cc")) + +test("dma") includes(path.join(sdkdir, "lib/atomic"), path.join(sdkdir, "lib/cxxrt"), @@ -74,17 +78,19 @@ firmware("test-suite") -- Helper libraries add_deps("freestanding", "string", "crt", "cxxrt", "atomic_fixed") -- Tests - add_deps("allocator_test") + -- add_deps("allocator_test") add_deps("thread_pool_test") - add_deps("futex_test") - add_deps("queue_test") - add_deps("locks_test") - add_deps("static_sealing_test", "static_sealing_inner") - add_deps("crash_recovery_test", "crash_recovery_inner", "crash_recovery_outer") - add_deps("multiwaiter_test") - add_deps("ccompile_test") - add_deps("stack_test", "stack_integrity_thread") - add_deps("compartment_calls_test", "compartment_calls_inner", "compartment_calls_inner_with_handler") + -- add_deps("futex_test") + -- add_deps("queue_test") + -- add_deps("locks_test") + -- add_deps("static_sealing_test", "static_sealing_inner") + -- add_deps("crash_recovery_test", "crash_recovery_inner", "crash_recovery_outer") + -- add_deps("multiwaiter_test") + -- add_deps("ccompile_test") + -- add_deps("stack_test", "stack_integrity_thread") + -- add_deps("compartment_calls_test", "compartment_calls_inner", "compartment_calls_inner_with_handler") + add_deps("dma_test") + add_deps("dma") -- Set the thread entry point to the test runner. on_load(function(target) target:values_set("board", "$(board)") From 4d0d45ea68bc20c8d304df4f35d53fb6f03ae9a4 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Sun, 3 Sep 2023 19:03:28 +0100 Subject: [PATCH 39/42] reorganized software for working v1 and v2 --- examples/10.dma_test/dma-test-v1.cc | 52 ++++++ examples/10.dma_test/dma-test-v2.cc | 54 ++++++ examples/10.dma_test/dma-test.cc | 109 ------------ examples/10.dma_test/xmake.lua | 25 +-- sdk/core/allocator/alloc.h | 12 ++ sdk/core/{dma => dma-v1}/dma.h | 2 + sdk/core/{dma => dma-v1}/dma_compartment.cc | 21 +-- sdk/core/{dma => dma-v1}/dma_compartment.hh | 0 sdk/core/{dma => dma-v1}/platform-dma.hh | 21 ++- sdk/core/dma-v2/dma.h | 94 ++++++++++ sdk/core/dma-v2/dma_compartment.cc | 181 ++++++++++++++++++++ sdk/core/dma-v2/dma_compartment.hh | 19 ++ sdk/core/dma-v2/platform-dma.hh | 170 ++++++++++++++++++ tests/dma-test.cc | 38 ++-- tests/xmake.lua | 16 +- 15 files changed, 645 insertions(+), 169 deletions(-) create mode 100644 examples/10.dma_test/dma-test-v1.cc create mode 100644 examples/10.dma_test/dma-test-v2.cc delete mode 100644 examples/10.dma_test/dma-test.cc rename sdk/core/{dma => dma-v1}/dma.h (95%) rename sdk/core/{dma => dma-v1}/dma_compartment.cc (94%) rename sdk/core/{dma => dma-v1}/dma_compartment.hh (100%) rename sdk/core/{dma => dma-v1}/platform-dma.hh (89%) create mode 100644 sdk/core/dma-v2/dma.h create mode 100644 sdk/core/dma-v2/dma_compartment.cc create mode 100644 sdk/core/dma-v2/dma_compartment.hh create mode 100644 sdk/core/dma-v2/platform-dma.hh diff --git a/examples/10.dma_test/dma-test-v1.cc b/examples/10.dma_test/dma-test-v1.cc new file mode 100644 index 00000000..8940a4b2 --- /dev/null +++ b/examples/10.dma_test/dma-test-v1.cc @@ -0,0 +1,52 @@ +// Copyright Microsoft and CHERIoT Contributors. +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include + +#include <../../sdk/core/dma-v1/dma.h> + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +// Thread entry point. +void __cheri_compartment("dma_test") test_dma() +{ + Debug::log("DMA app entered, v1!"); + + // This is a dma process between two different memory addresses + uint32_t bytes = 1024; + uint32_t words = bytes / 4; + uint32_t byteSwap = 0; + + uint32_t *sourceAddress = (uint32_t *)malloc(bytes); + uint32_t *targetAddress = (uint32_t *)malloc(bytes); + + for (int i = 0; i < words; i++) + { + *(sourceAddress + i) = i + 100; + *(targetAddress + i) = 0; + } + + Debug::log("M:Ind: 0 and last, Source values BEFORE dma: {}, {}", + *(sourceAddress), + *(sourceAddress + words - 1)); + Debug::log("M: Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + + static DMA::Device dmaDevice; + + int ret = dmaDevice.configure_and_launch( + sourceAddress, targetAddress, bytes, 0, 0, byteSwap); + + Debug::log("Main, ret: {}", ret); + + Debug::log("M: Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + Debug::log("M: End of test"); +} diff --git a/examples/10.dma_test/dma-test-v2.cc b/examples/10.dma_test/dma-test-v2.cc new file mode 100644 index 00000000..17789d4c --- /dev/null +++ b/examples/10.dma_test/dma-test-v2.cc @@ -0,0 +1,54 @@ +// Copyright Microsoft and CHERIoT Contributors. +// SPDX-License-Identifier: MIT +#define MALLOC_QUOTA 0x10000000 + +#include +#include +#include +#include +#include + +#include <../../sdk/core/dma-v2/dma.h> + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +// Thread entry point. +void __cheri_compartment("dma_test") test_dma() +{ + Debug::log("DMA app entered, v2.8!"); + + // This is a dma process between two different memory addresses + uint32_t bytes = 8096; + uint32_t words = bytes / 4; + uint32_t byteSwap = 0; + + uint32_t *sourceAddress = (uint32_t *)malloc(bytes); + uint32_t *targetAddress = (uint32_t *)malloc(bytes); + + for (int i = 0; i < words; i++) + { + *(sourceAddress + i) = i + 100; + *(targetAddress + i) = 0; + } + + Debug::log("M:Ind: 0 and last, Source values BEFORE dma: {}, {}", + *(sourceAddress), + *(sourceAddress + words - 1)); + Debug::log("M: Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + + static DMA::Device dmaDevice; + + int ret = dmaDevice.configure_and_launch( + sourceAddress, targetAddress, bytes, 0, 0, byteSwap); + + Debug::log("Main, ret: {}", ret); + + Debug::log("M: Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + + Debug::log("M: End of test"); +} diff --git a/examples/10.dma_test/dma-test.cc b/examples/10.dma_test/dma-test.cc deleted file mode 100644 index c9a4a7f8..00000000 --- a/examples/10.dma_test/dma-test.cc +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright Microsoft and CHERIoT Contributors. -// SPDX-License-Identifier: MIT - -#include -#include -#include -#include -#include - -#include <../../sdk/core/dma/dma.h> -// #include - -#include -#include - -// Expose debugging features unconditionally for this compartment. -using Debug = ConditionalDebug; - -using namespace thread_pool; - -#include <../../tests/crash_recovery.h> - -using DebugErrorHandler = ConditionalDebug; - -int crashes = 0; - -extern "C" ErrorRecoveryBehaviour -compartment_error_handler(struct ErrorState *frame, size_t mcause, size_t mtval) -{ - Debug::log("Test saw error for PCC {}", frame->pcc); - Debug::log("Error cause: {}, mtval: {}", mcause, mtval); - if (mcause == 0x2) - { - Debug::log("Test hit assertion failure, unwinding"); - return ErrorRecoveryBehaviour::ForceUnwind; - } - - crashes++; - Debug::log("Resuming test at failure location"); - return ErrorRecoveryBehaviour::InstallContext; -} - -// Thread entry point. -void __cheri_compartment("dma_test") test_dma() -{ - Debug::log("DMA app entered, v2!"); - - // This is a dma process between two different memory addresses - uint32_t bytes = 1024; - uint32_t words = bytes / 4; - uint32_t byteSwap = 4; - - uint32_t *sourceAddress = (uint32_t *)malloc(bytes); - uint32_t *targetAddress = (uint32_t *)malloc(bytes); - uint32_t *alternateAddress = (uint32_t *)malloc(bytes); - - for (int i = 0; i < words; i++) - { - *(sourceAddress + i) = i + 100; - *(alternateAddress + i) = i + 200; - *(targetAddress + i) = 0; - } - - Debug::log("M:Ind: 0 and last, Source values BEFORE dma: {}, {}", - *(sourceAddress), - *(sourceAddress + words - 1)); - Debug::log("M: Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", - *(targetAddress), - *(targetAddress + words - 1)); - - // static DMA::Device dmaDevice; - - async([=]() { - Debug::log("Thread 1, start"); - - // int ret = dmaDevice.configure_and_launch( - // sourceAddress, targetAddress, bytes, 0, 0, byteSwap); - - // Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", - // *(sourceAddress), - // *(sourceAddress + words - 1)); - // Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", - // *(targetAddress), - // *(targetAddress + words - 1)); - - // Debug::log("Thread 1, ret: {}", ret); - }); - - async([=]() { - Debug::log("Thread 2, start"); - - // int ret = dmaDevice.configure_and_launch( - // alternateAddress, targetAddress, bytes, 0, 0, 0); - - // Debug::log("Ind: 0 and last, Source values AFTER dma: {}, {}", - // *(alternateAddress), - // *(alternateAddress + words - 1)); - // Debug::log("Ind: 0 and last, Dest-n values AFTER dma: {}, {}", - // *(targetAddress), - // *(targetAddress + words - 1)); - - // Debug::log("Thread 2, ret: {}", ret); - }); - - Debug::log("M: Ind: 0 and last, Dest-n values AFTER dma: {}, {}", - *(targetAddress), - *(targetAddress + words - 1)); - Debug::log("M: End of test"); -} diff --git a/examples/10.dma_test/xmake.lua b/examples/10.dma_test/xmake.lua index d84e2b78..f91370bc 100644 --- a/examples/10.dma_test/xmake.lua +++ b/examples/10.dma_test/xmake.lua @@ -10,21 +10,20 @@ set_toolchains("cheriot-clang") includes(path.join(sdkdir, "lib/freestanding"), path.join(sdkdir, "lib/cxxrt"), path.join(sdkdir, "lib/atomic"), - path.join(sdkdir, "lib/crt"), - path.join(sdkdir, "lib/thread_pool")) + path.join(sdkdir, "lib/crt")) option("board") set_default("sail") compartment("dma") - add_files(path.join(sdkdir, "core/dma/dma_compartment.cc")) + add_files(path.join(sdkdir, "core/dma-v2/dma_compartment.cc")) compartment("dma_test") - add_files("dma-test.cc") + add_files("dma-test-v2.cc") -- Firmware image for the example. -firmware("dma-test") - add_deps("crt", "cxxrt", "freestanding", "atomic_fixed", "thread_pool") +firmware("dma-test-v2") + add_deps("crt", "cxxrt", "freestanding", "atomic_fixed") add_deps("dma", "dma_test") on_load(function(target) target:values_set("board", "$(board)") @@ -35,20 +34,6 @@ firmware("dma-test") entry_point = "test_dma", stack_size = 0x1000, trusted_stack_frames = 9 - }, - { - compartment = "thread_pool", - priority = 1, - entry_point = "thread_pool_run", - stack_size = 0x600, - trusted_stack_frames = 8 - }, - { - compartment = "thread_pool", - priority = 1, - entry_point = "thread_pool_run", - stack_size = 0x600, - trusted_stack_frames = 8 } }, {expand = false}) end) diff --git a/sdk/core/allocator/alloc.h b/sdk/core/allocator/alloc.h index 9ec81630..98e33aeb 100644 --- a/sdk/core/allocator/alloc.h +++ b/sdk/core/allocator/alloc.h @@ -3,6 +3,7 @@ #pragma once +#include "../dma-v2/platform-dma.hh" #include "alloc_config.h" #include "revoker.h" #include @@ -21,6 +22,8 @@ extern Revocation::Revoker revoker; +Ibex::PlatformDMA platformDma; + /// Do we have temporal safety support in hardware? constexpr bool HasTemporalSafety = !std::is_same_v; @@ -1161,6 +1164,8 @@ class MState */ int mspace_free(MChunkHeader &chunk, size_t bodySize) { + Debug::log("Entered mspace"); + // Expand the bounds of the freed object to the whole heap and set the // address that we're looking at to the base of the requested // capability. @@ -1174,6 +1179,13 @@ class MState * the user capability (or its progeny) that undid our work of zeroing! */ revoker.shadow_paint_range(mem.address(), chunk.cell_next()); + + /** + * notify the DMA, so it can stop the transfer if its the DMA's source + * and destination addresses that are freed. + */ + platformDma.notify_the_dma(); + Debug::log("freed address: {}", mem.address()); /* * Shadow bits have been painted. From now on user caps to this chunk diff --git a/sdk/core/dma/dma.h b/sdk/core/dma-v1/dma.h similarity index 95% rename from sdk/core/dma/dma.h rename to sdk/core/dma-v1/dma.h index 44767c83..12f18eeb 100644 --- a/sdk/core/dma/dma.h +++ b/sdk/core/dma-v1/dma.h @@ -8,6 +8,8 @@ #include #include +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, diff --git a/sdk/core/dma/dma_compartment.cc b/sdk/core/dma-v1/dma_compartment.cc similarity index 94% rename from sdk/core/dma/dma_compartment.cc rename to sdk/core/dma-v1/dma_compartment.cc index ccdb39ed..13a8cf0c 100644 --- a/sdk/core/dma/dma_compartment.cc +++ b/sdk/core/dma-v1/dma_compartment.cc @@ -61,8 +61,10 @@ int launch_dma(uint32_t *sourceAddress, * * This lock automatically unlocks at the end of this function. */ - LockGuard g{dmaOwnershipLock}; + LockGuard g{dmaOwnershipLock}; + Debug::log("dma v1"); + /** * If dma is already launched, we need to check for the interrupt status. * No need for validity and permissions checks though for the scheduler @@ -160,9 +162,12 @@ int launch_dma(uint32_t *sourceAddress, void internal_wait_and_reset_dma(uint32_t interruptNumber) { /** - * Resetting the claim pointers - * and cleaning up the dma registers. - * + * Handle the interrupt here, once dmaFutex woke up via scheduler. + * DMA interrupt means that the dma operation is finished + * and it is time to reset and clear the dma configuration registers. + */ + + /** * Claim registers are meant to be * cleared by every DMA operation, * that is why we are explicitely resetting @@ -174,14 +179,6 @@ void internal_wait_and_reset_dma(uint32_t interruptNumber) * Because this function can be called from two different points */ - /** - * Also, handle the interrupt here, once dmaFutex woke up via scheduler. - * DMA interrupt means that the dma operation is finished - * and it is time to reset and clear the dma configuration registers. - * Unlike with futex wait of other threads, as an occupying thread we - * wait indefinitely as much as needed for the dma completion - */ - static const uint32_t *dmaFutex = interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); diff --git a/sdk/core/dma/dma_compartment.hh b/sdk/core/dma-v1/dma_compartment.hh similarity index 100% rename from sdk/core/dma/dma_compartment.hh rename to sdk/core/dma-v1/dma_compartment.hh diff --git a/sdk/core/dma/platform-dma.hh b/sdk/core/dma-v1/platform-dma.hh similarity index 89% rename from sdk/core/dma/platform-dma.hh rename to sdk/core/dma-v1/platform-dma.hh index 02c96f8d..e3992d9d 100644 --- a/sdk/core/dma/platform-dma.hh +++ b/sdk/core/dma-v1/platform-dma.hh @@ -7,10 +7,6 @@ #include #include - -// Expose debugging features unconditionally for this compartment. -using Debug = ConditionalDebug; - namespace Ibex { class PlatformDMA @@ -71,6 +67,16 @@ namespace Ibex * we can fetch and transfer the data at different stride rates */ uint32_t targetStrides; + /** + * Below is the capability for source and target addresses + */ + uint32_t sourceCapability; + uint32_t targetCapability; + /** + * Below is the MMIO interface to tell the DMA that free() + * call occurred at the allocator compartment + */ + uint32_t callFromMalloc; }; __always_inline volatile DMAInterface &device() @@ -135,11 +141,18 @@ namespace Ibex device().sourceAddress = CHERI::Capability{sourceAddress}.address(); device().targetAddress = CHERI::Capability{targetAddress}.address(); device().lengthInBytes = lengthInBytes; + device().sourceCapability = CHERI::Capability{sourceAddress}.base(); + device().targetCapability = CHERI::Capability{targetAddress}.base(); write_strides(sourceStrides, targetStrides); swap_bytes_and_start_dma(byteSwapAmount); } + + void notify_the_dma() + { + device().callFromMalloc = 1; + } void reset_dma() { diff --git a/sdk/core/dma-v2/dma.h b/sdk/core/dma-v2/dma.h new file mode 100644 index 00000000..19fb69c1 --- /dev/null +++ b/sdk/core/dma-v2/dma.h @@ -0,0 +1,94 @@ +#include "dma_compartment.hh" +#include "futex.h" +#include "platform-dma.hh" +#include +#include +#include +#include +#include +#include + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, + dma, + true, + true); + +namespace DMA +{ + + template + concept IsDmaDevice = requires(T device, + uint32_t *sourceAddress, + uint32_t *targetAddress, + size_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) + { + { + device.write_conf_and_start(sourceAddress, + targetAddress, + lengthInBytes, + sourceStrides, + targetStrides, + byteSwapAmount) + } -> std::same_as; + { + device.reset_dma() + } -> std::same_as; + }; + + template + + requires IsDmaDevice + + class GenericDMA : public PlatformDMA + { + public: + int configure_and_launch(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) + { + /** + * Dma launch call: + * - checks for the dma ownership status, + * - for access rights, + * - creates claims for each source and destination addresses, + * - automatically resets the claims and the dma registers + * at the end of the transfer. + */ + Debug::log("before launch"); + + uint32_t dmaInterruptReturn = launch_dma(sourceAddress, + targetAddress, + lengthInBytes, + sourceStrides, + targetStrides, + byteSwapAmount); + + Debug::log("after launch: {}", dmaInterruptReturn); + + int loadValue = *sourceAddress; + int freeStatus; + freeStatus = free(sourceAddress); + Debug::log("driver, freeStatus: {}", freeStatus); + + if (dmaInterruptReturn < 0) + { + return -EINVAL; + } + + wait_and_reset_dma(dmaInterruptReturn); + + return 0; + } + }; + + using Device = GenericDMA; +} // namespace DMA diff --git a/sdk/core/dma-v2/dma_compartment.cc b/sdk/core/dma-v2/dma_compartment.cc new file mode 100644 index 00000000..bd7a322a --- /dev/null +++ b/sdk/core/dma-v2/dma_compartment.cc @@ -0,0 +1,181 @@ +#include "futex.h" + +#include "dma_compartment.hh" +#include +#include +#include + +#include "platform-dma.hh" +#include +#include +#include +#include +#include +#include + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +// Import some useful things from the CHERI namespace. +using namespace CHERI; + +Ibex::PlatformDMA platformDma; + +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, + dma, + true, + true); + +namespace +{ + /** + * Flag lock to control ownership + * over the dma controller + */ + FlagLock dmaOwnershipLock; + + uint32_t expectedValue = 0; + + bool alreadyReset = 0; + + +} // namespace + +void internal_wait_and_reset_dma(uint32_t interruptNumber); + +int launch_dma(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) +{ + /** + * Lock this compartment via LockGuard, + * to prevent data race. + * + * This lock automatically unlocks at the end of this function. + */ + LockGuard g{dmaOwnershipLock}; + + /** + * If dma is already launched, we need to check for the interrupt status. + * No need for validity and permissions checks though for the scheduler + * once futex is created + */ + + static const uint32_t *dmaFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + uint32_t currentInterruptCounter = *dmaFutex; + + /** + * If dma is already running, check for the + * expected and current interrupt values. + * If they do not match, wait for the interrupt. + * + * Expected Value is expected to be incremented only per thread, + * assuming that every thread enters the launch_dma() + * only once per each transfer + */ + + if (expectedValue != currentInterruptCounter) + { + internal_wait_and_reset_dma(currentInterruptCounter); + } + + Debug::Assert( + expectedValue == *dmaFutex, + "ExpectedValue is not equal to the current interrupt counter!"); + + /** + * return if sufficient permissions are not present + * and if not long enough + */ + + if (!check_pointer( + sourceAddress, lengthInBytes) || + !check_pointer( + targetAddress, lengthInBytes)) + { + return -EINVAL; + } + + /** + * After passing all the checks, + * we can reset the alreadyReset value + */ + alreadyReset = 0; + + platformDma.write_conf_and_start(sourceAddress, + targetAddress, + lengthInBytes, + sourceStrides, + targetStrides, + byteSwapAmount); + + /** + * Increment the expected value only when + * dma has started to avoid the potential deadlock + * of this function is returned with failure earlier + */ + expectedValue++; + + /** + * return here, if all operations + * were successful. + */ + + return currentInterruptCounter; +} + +void internal_wait_and_reset_dma(uint32_t interruptNumber) +{ + /** + * Handle the interrupt here, once dmaFutex woke up via scheduler. + * DMA interrupt means that the dma operation is finished + * and it is time to reset and clear the dma configuration registers. + */ + + /** + * However, clear only if the addresses are not reset yet. + * Because this function can be called from two different points + */ + + static const uint32_t *dmaFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + Debug::log("before futex wait"); + + Timeout t{10}; + futex_timed_wait(&t, dmaFutex, interruptNumber); + + if (!alreadyReset) + { + /** + * Resetting the dma registers + * todo: we need some check here to avoid double checks + */ + Debug::log("inside the reset condition"); + + platformDma.reset_dma(); + + alreadyReset = 1; + + /** + * Acknowledging interrupt here irrespective of the reset status + */ + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + } + +} + +void wait_and_reset_dma(uint32_t interruptNumber) +{ + /** + * This lock is to avoid the data race as well + */ + LockGuard g{dmaOwnershipLock}; + + internal_wait_and_reset_dma(interruptNumber); +} \ No newline at end of file diff --git a/sdk/core/dma-v2/dma_compartment.hh b/sdk/core/dma-v2/dma_compartment.hh new file mode 100644 index 00000000..07f928c0 --- /dev/null +++ b/sdk/core/dma-v2/dma_compartment.hh @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +/** + * A function below claims the source and target addresses of the DMA interface. + * While, DMA is in progress, these addresses will be claimed by the DMA + * compartment and so the memory will not be freed. + */ + +int __cheri_compartment("dma") launch_dma(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount); + +void __cheri_compartment("dma") wait_and_reset_dma(uint32_t interruptNumber); \ No newline at end of file diff --git a/sdk/core/dma-v2/platform-dma.hh b/sdk/core/dma-v2/platform-dma.hh new file mode 100644 index 00000000..e3992d9d --- /dev/null +++ b/sdk/core/dma-v2/platform-dma.hh @@ -0,0 +1,170 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Ibex +{ + class PlatformDMA + { + private: + struct DMAInterface + { + /** + * Below is DMA control register address: + * - includes start('b0), endianness conversion('b1 and 2) and + * reset bits('b3) + * - bit of index 1 and 2 are for enabling 2 and 4 byte swaps + * respectively + * - start bit is meant to be set at the end while programming + */ + uint32_t control; + /** + * Below is DMA status register address + * - first bit refers to halted status + * - 0 is for idle, and 1 for running + */ + uint32_t status; + /** + * Below is DMA source address register address: + * - here source address is where FROM the DMA should transfer the + * data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any + * peripheral or accelerator) + */ + uint32_t sourceAddress; + /** + * Below is DMA target address register address: + * - here target address is where TO the DMA should transfer the + * data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any + * peripheral or accelerator) + */ + uint32_t targetAddress; + /** + * Below is the amount of data IN BYTES that DMA should transfer + * between the source and a target + */ + uint32_t lengthInBytes; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a source + * address. Strides is the jump amount between each data retrieval + * address during the DMA operation. Minimum width of the stride is + * 4 byte words. + * - 0 stride is default and points to the address of the next word + * - 1 is for 1 word skippal, and 2 for 2 before the next address + * - todo: more fine grain stride configurability (in term of + * bytes) for the future? + */ + uint32_t sourceStrides; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a target + * address. The same as above but just for a target address. So that + * we can fetch and transfer the data at different stride rates + */ + uint32_t targetStrides; + /** + * Below is the capability for source and target addresses + */ + uint32_t sourceCapability; + uint32_t targetCapability; + /** + * Below is the MMIO interface to tell the DMA that free() + * call occurred at the allocator compartment + */ + uint32_t callFromMalloc; + }; + + __always_inline volatile DMAInterface &device() + { + return *MMIO_CAPABILITY(DMAInterface, dma); + } + + void write_strides(uint32_t sourceStrides, uint32_t targetStrides) + { + /** + * Setting source and target strides + */ + device().sourceStrides = sourceStrides; + device().targetStrides = targetStrides; + } + + int swap_bytes_and_start_dma(uint32_t swapAmount) + { + /** + * Setting byte swaps + * and start bit. + * + * Swap amount can be equal to + * only either 2 or 4. + */ + + if (swapAmount != 2) + { + if (swapAmount != 4) + { + swapAmount = 0; + } + } + + uint32_t controlConfiguration = swapAmount | 0x1; + + device().control = controlConfiguration; + + return 0; + } + + public: + uint32_t read_status() + { + /** + * this statement returns the less signifance bit + * to show the halted status + */ + return device().status & 0x1; + } + + void write_conf_and_start(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) + { + /** + * Setting source and target addresses, and length fields + */ + device().sourceAddress = CHERI::Capability{sourceAddress}.address(); + device().targetAddress = CHERI::Capability{targetAddress}.address(); + device().lengthInBytes = lengthInBytes; + device().sourceCapability = CHERI::Capability{sourceAddress}.base(); + device().targetCapability = CHERI::Capability{targetAddress}.base(); + + write_strides(sourceStrides, targetStrides); + + swap_bytes_and_start_dma(byteSwapAmount); + } + + void notify_the_dma() + { + device().callFromMalloc = 1; + } + + void reset_dma() + { + /** + * Setting a reset bit, which is bit 3. + * this clears all the registers, + * but do not transfer the current transfer status anywhere + */ + device().control = 0x8; + } + }; +} // namespace Ibex + +// template +using PlatformDMA = Ibex::PlatformDMA; \ No newline at end of file diff --git a/tests/dma-test.cc b/tests/dma-test.cc index 40036181..31eac1e4 100644 --- a/tests/dma-test.cc +++ b/tests/dma-test.cc @@ -10,7 +10,7 @@ #include #include -#include <../../sdk/core/dma/dma.h> +#include <../../sdk/core/dma-v2/dma.h> #include #include @@ -20,7 +20,7 @@ using namespace thread_pool; // Thread entry point. void test_dma() { - debug_log("DMA app entered, v14!"); + debug_log("DMA app entered, v2.10!"); // This is a dma process between two different memory addresses uint32_t bytes = 1024; @@ -61,27 +61,33 @@ void test_dma() debug_log("Thread 1, ret: {}", ret); }); - async([=]() { - debug_log("Thread 2, start"); - - int ret = dmaDevice.configure_and_launch( - alternateAddress, targetAddress, bytes, 0, 0, 0); + // async([=]() { + // debug_log("Thread Free, start"); - debug_log("Thread 2, ret: {}", ret); - }); + // free(sourceAddress); + // }); + // async([=]() { + // debug_log("Thread 2, start"); + + // int ret = dmaDevice.configure_and_launch( + // alternateAddress, targetAddress, bytes, 0, 0, 0); + + // debug_log("Thread 2, ret: {}", ret); + // }); + // here, we are just forcing to sleep // however, for experimental numbers // we need to make sure to make a fare analysis - Timeout t{50}; + Timeout t{100}; thread_sleep(&t); - debug_log("Ind: 0 and last, Source values AFTER dma: {}, {}", - *(sourceAddress), - *(sourceAddress + words - 1)); - debug_log("Ind: 0 and last, Source values AFTER dma: {}, {}", - *(alternateAddress), - *(alternateAddress + words - 1)); + // debug_log("Ind: 0 and last, Source values AFTER dma: {}, {}", + // *(sourceAddress), + // *(sourceAddress + words - 1)); + // debug_log("Ind: 0 and last, Source values AFTER dma: {}, {}", + // *(alternateAddress), + // *(alternateAddress + words - 1)); debug_log("M: Ind: 0 and last, Dest-n values AFTER dma: {}, {}", *(targetAddress), *(targetAddress + words - 1)); diff --git a/tests/xmake.lua b/tests/xmake.lua index 1129ee3b..2963abac 100644 --- a/tests/xmake.lua +++ b/tests/xmake.lua @@ -56,7 +56,7 @@ test("thread_pool") -- add_files("compartment_calls_inner_with_handler.cc") -- test("compartment_calls") compartment("dma") - add_files(path.join(sdkdir, "core/dma/dma_compartment.cc")) + add_files(path.join(sdkdir, "core/dma-v2/dma_compartment.cc")) test("dma") @@ -104,13 +104,6 @@ firmware("test-suite") -- test to fail in the right compartment. trusted_stack_frames = 9 }, - { - compartment = "thread_pool", - priority = 1, - entry_point = "thread_pool_run", - stack_size = 0x600, - trusted_stack_frames = 8 - }, { compartment = "thread_pool", priority = 1, @@ -118,5 +111,12 @@ firmware("test-suite") stack_size = 0x600, trusted_stack_frames = 8 } + -- { + -- compartment = "thread_pool", + -- priority = 1, + -- entry_point = "thread_pool_run", + -- stack_size = 0x600, + -- trusted_stack_frames = 8 + -- } }, {expand = false}) end) From 160e2e8b032c77b813dd2fec185049ee87fe01cd Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Mon, 4 Sep 2023 19:24:31 +0100 Subject: [PATCH 40/42] sw for dma v3 --- examples/10.dma_test/dma-test-v2.cc | 2 +- examples/10.dma_test/dma-test-v3.cc | 54 +++++++++ sdk/core/allocator/alloc.h | 4 +- sdk/core/dma-v2/dma.h | 1 - sdk/core/dma-v2/platform-dma.hh | 8 +- sdk/core/dma-v3/dma.h | 93 ++++++++++++++ sdk/core/dma-v3/dma_compartment.cc | 181 ++++++++++++++++++++++++++++ sdk/core/dma-v3/dma_compartment.hh | 19 +++ sdk/core/dma-v3/platform-dma.hh | 173 ++++++++++++++++++++++++++ 9 files changed, 526 insertions(+), 9 deletions(-) create mode 100644 examples/10.dma_test/dma-test-v3.cc create mode 100644 sdk/core/dma-v3/dma.h create mode 100644 sdk/core/dma-v3/dma_compartment.cc create mode 100644 sdk/core/dma-v3/dma_compartment.hh create mode 100644 sdk/core/dma-v3/platform-dma.hh diff --git a/examples/10.dma_test/dma-test-v2.cc b/examples/10.dma_test/dma-test-v2.cc index 17789d4c..5848726f 100644 --- a/examples/10.dma_test/dma-test-v2.cc +++ b/examples/10.dma_test/dma-test-v2.cc @@ -16,7 +16,7 @@ using Debug = ConditionalDebug; // Thread entry point. void __cheri_compartment("dma_test") test_dma() { - Debug::log("DMA app entered, v2.8!"); + Debug::log("DMA app entered, v3.3!"); // This is a dma process between two different memory addresses uint32_t bytes = 8096; diff --git a/examples/10.dma_test/dma-test-v3.cc b/examples/10.dma_test/dma-test-v3.cc new file mode 100644 index 00000000..7d05e177 --- /dev/null +++ b/examples/10.dma_test/dma-test-v3.cc @@ -0,0 +1,54 @@ +// Copyright Microsoft and CHERIoT Contributors. +// SPDX-License-Identifier: MIT +#define MALLOC_QUOTA 0x10000000 + +#include +#include +#include +#include +#include + +#include <../../sdk/core/dma-v3/dma.h> + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +// Thread entry point. +void __cheri_compartment("dma_test") test_dma() +{ + Debug::log("DMA app entered, v3.0!"); + + // This is a dma process between two different memory addresses + uint32_t bytes = 8096; + uint32_t words = bytes / 4; + uint32_t byteSwap = 0; + + uint32_t *sourceAddress = (uint32_t *)malloc(bytes); + uint32_t *targetAddress = (uint32_t *)malloc(bytes); + + for (int i = 0; i < words; i++) + { + *(sourceAddress + i) = i + 100; + *(targetAddress + i) = 0; + } + + Debug::log("M:Ind: 0 and last, Source values BEFORE dma: {}, {}", + *(sourceAddress), + *(sourceAddress + words - 1)); + Debug::log("M: Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + + static DMA::Device dmaDevice; + + int ret = dmaDevice.configure_and_launch( + sourceAddress, targetAddress, bytes, 0, 0, byteSwap); + + Debug::log("Main, ret: {}", ret); + + Debug::log("M: Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + + Debug::log("M: End of test"); +} diff --git a/sdk/core/allocator/alloc.h b/sdk/core/allocator/alloc.h index 98e33aeb..d180a4bd 100644 --- a/sdk/core/allocator/alloc.h +++ b/sdk/core/allocator/alloc.h @@ -1164,8 +1164,6 @@ class MState */ int mspace_free(MChunkHeader &chunk, size_t bodySize) { - Debug::log("Entered mspace"); - // Expand the bounds of the freed object to the whole heap and set the // address that we're looking at to the base of the requested // capability. @@ -1184,7 +1182,7 @@ class MState * notify the DMA, so it can stop the transfer if its the DMA's source * and destination addresses that are freed. */ - platformDma.notify_the_dma(); + // platformDma.notify_the_dma(); Debug::log("freed address: {}", mem.address()); /* diff --git a/sdk/core/dma-v2/dma.h b/sdk/core/dma-v2/dma.h index 19fb69c1..d5749584 100644 --- a/sdk/core/dma-v2/dma.h +++ b/sdk/core/dma-v2/dma.h @@ -74,7 +74,6 @@ namespace DMA Debug::log("after launch: {}", dmaInterruptReturn); - int loadValue = *sourceAddress; int freeStatus; freeStatus = free(sourceAddress); Debug::log("driver, freeStatus: {}", freeStatus); diff --git a/sdk/core/dma-v2/platform-dma.hh b/sdk/core/dma-v2/platform-dma.hh index e3992d9d..dd6ef201 100644 --- a/sdk/core/dma-v2/platform-dma.hh +++ b/sdk/core/dma-v2/platform-dma.hh @@ -70,8 +70,8 @@ namespace Ibex /** * Below is the capability for source and target addresses */ - uint32_t sourceCapability; - uint32_t targetCapability; + void* sourceCapability; + void* targetCapability; /** * Below is the MMIO interface to tell the DMA that free() * call occurred at the allocator compartment @@ -141,8 +141,8 @@ namespace Ibex device().sourceAddress = CHERI::Capability{sourceAddress}.address(); device().targetAddress = CHERI::Capability{targetAddress}.address(); device().lengthInBytes = lengthInBytes; - device().sourceCapability = CHERI::Capability{sourceAddress}.base(); - device().targetCapability = CHERI::Capability{targetAddress}.base(); + device().sourceCapability = (void*) sourceAddress; + device().targetCapability = (void*) targetAddress; write_strides(sourceStrides, targetStrides); diff --git a/sdk/core/dma-v3/dma.h b/sdk/core/dma-v3/dma.h new file mode 100644 index 00000000..d5749584 --- /dev/null +++ b/sdk/core/dma-v3/dma.h @@ -0,0 +1,93 @@ +#include "dma_compartment.hh" +#include "futex.h" +#include "platform-dma.hh" +#include +#include +#include +#include +#include +#include + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, + dma, + true, + true); + +namespace DMA +{ + + template + concept IsDmaDevice = requires(T device, + uint32_t *sourceAddress, + uint32_t *targetAddress, + size_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) + { + { + device.write_conf_and_start(sourceAddress, + targetAddress, + lengthInBytes, + sourceStrides, + targetStrides, + byteSwapAmount) + } -> std::same_as; + { + device.reset_dma() + } -> std::same_as; + }; + + template + + requires IsDmaDevice + + class GenericDMA : public PlatformDMA + { + public: + int configure_and_launch(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) + { + /** + * Dma launch call: + * - checks for the dma ownership status, + * - for access rights, + * - creates claims for each source and destination addresses, + * - automatically resets the claims and the dma registers + * at the end of the transfer. + */ + Debug::log("before launch"); + + uint32_t dmaInterruptReturn = launch_dma(sourceAddress, + targetAddress, + lengthInBytes, + sourceStrides, + targetStrides, + byteSwapAmount); + + Debug::log("after launch: {}", dmaInterruptReturn); + + int freeStatus; + freeStatus = free(sourceAddress); + Debug::log("driver, freeStatus: {}", freeStatus); + + if (dmaInterruptReturn < 0) + { + return -EINVAL; + } + + wait_and_reset_dma(dmaInterruptReturn); + + return 0; + } + }; + + using Device = GenericDMA; +} // namespace DMA diff --git a/sdk/core/dma-v3/dma_compartment.cc b/sdk/core/dma-v3/dma_compartment.cc new file mode 100644 index 00000000..bd7a322a --- /dev/null +++ b/sdk/core/dma-v3/dma_compartment.cc @@ -0,0 +1,181 @@ +#include "futex.h" + +#include "dma_compartment.hh" +#include +#include +#include + +#include "platform-dma.hh" +#include +#include +#include +#include +#include +#include + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +// Import some useful things from the CHERI namespace. +using namespace CHERI; + +Ibex::PlatformDMA platformDma; + +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, + dma, + true, + true); + +namespace +{ + /** + * Flag lock to control ownership + * over the dma controller + */ + FlagLock dmaOwnershipLock; + + uint32_t expectedValue = 0; + + bool alreadyReset = 0; + + +} // namespace + +void internal_wait_and_reset_dma(uint32_t interruptNumber); + +int launch_dma(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) +{ + /** + * Lock this compartment via LockGuard, + * to prevent data race. + * + * This lock automatically unlocks at the end of this function. + */ + LockGuard g{dmaOwnershipLock}; + + /** + * If dma is already launched, we need to check for the interrupt status. + * No need for validity and permissions checks though for the scheduler + * once futex is created + */ + + static const uint32_t *dmaFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + uint32_t currentInterruptCounter = *dmaFutex; + + /** + * If dma is already running, check for the + * expected and current interrupt values. + * If they do not match, wait for the interrupt. + * + * Expected Value is expected to be incremented only per thread, + * assuming that every thread enters the launch_dma() + * only once per each transfer + */ + + if (expectedValue != currentInterruptCounter) + { + internal_wait_and_reset_dma(currentInterruptCounter); + } + + Debug::Assert( + expectedValue == *dmaFutex, + "ExpectedValue is not equal to the current interrupt counter!"); + + /** + * return if sufficient permissions are not present + * and if not long enough + */ + + if (!check_pointer( + sourceAddress, lengthInBytes) || + !check_pointer( + targetAddress, lengthInBytes)) + { + return -EINVAL; + } + + /** + * After passing all the checks, + * we can reset the alreadyReset value + */ + alreadyReset = 0; + + platformDma.write_conf_and_start(sourceAddress, + targetAddress, + lengthInBytes, + sourceStrides, + targetStrides, + byteSwapAmount); + + /** + * Increment the expected value only when + * dma has started to avoid the potential deadlock + * of this function is returned with failure earlier + */ + expectedValue++; + + /** + * return here, if all operations + * were successful. + */ + + return currentInterruptCounter; +} + +void internal_wait_and_reset_dma(uint32_t interruptNumber) +{ + /** + * Handle the interrupt here, once dmaFutex woke up via scheduler. + * DMA interrupt means that the dma operation is finished + * and it is time to reset and clear the dma configuration registers. + */ + + /** + * However, clear only if the addresses are not reset yet. + * Because this function can be called from two different points + */ + + static const uint32_t *dmaFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + Debug::log("before futex wait"); + + Timeout t{10}; + futex_timed_wait(&t, dmaFutex, interruptNumber); + + if (!alreadyReset) + { + /** + * Resetting the dma registers + * todo: we need some check here to avoid double checks + */ + Debug::log("inside the reset condition"); + + platformDma.reset_dma(); + + alreadyReset = 1; + + /** + * Acknowledging interrupt here irrespective of the reset status + */ + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + } + +} + +void wait_and_reset_dma(uint32_t interruptNumber) +{ + /** + * This lock is to avoid the data race as well + */ + LockGuard g{dmaOwnershipLock}; + + internal_wait_and_reset_dma(interruptNumber); +} \ No newline at end of file diff --git a/sdk/core/dma-v3/dma_compartment.hh b/sdk/core/dma-v3/dma_compartment.hh new file mode 100644 index 00000000..07f928c0 --- /dev/null +++ b/sdk/core/dma-v3/dma_compartment.hh @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +/** + * A function below claims the source and target addresses of the DMA interface. + * While, DMA is in progress, these addresses will be claimed by the DMA + * compartment and so the memory will not be freed. + */ + +int __cheri_compartment("dma") launch_dma(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount); + +void __cheri_compartment("dma") wait_and_reset_dma(uint32_t interruptNumber); \ No newline at end of file diff --git a/sdk/core/dma-v3/platform-dma.hh b/sdk/core/dma-v3/platform-dma.hh new file mode 100644 index 00000000..0019ab08 --- /dev/null +++ b/sdk/core/dma-v3/platform-dma.hh @@ -0,0 +1,173 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Ibex +{ + class PlatformDMA + { + private: + struct DMAInterface + { + /** + * Below is DMA source capability register address: + * - here source address is where FROM the DMA should transfer the + * data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any + * peripheral or accelerator) + * - this capability also includes the both address and the access bits + * - regs 0 and 1 at hw + */ + void* sourceCapability; + /** + * Below is DMA target capability register address: + * - here target address is where TO the DMA should transfer the + * data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any + * peripheral or accelerator) + * - this capability also includes the both address and the access bits + * - regs 2 and 3 at hw + */ + void* targetCapability; + /** + * Below is the amount of data IN BYTES that DMA should transfer + * between the source and a target + * - regs 4 at hw + */ + uint32_t lengthInBytes; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a source + * address. Strides is the jump amount between each data retrieval + * address during the DMA operation. Minimum width of the stride is + * 4 byte words. + * - 0 stride is default and points to the address of the next word + * - 1 is for 1 word skippal, and 2 for 2 before the next address + * - todo: more fine grain stride configurability (in term of + * bytes) for the future? + * - regs 5 at hw + */ + uint32_t sourceStrides; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a target + * address. The same as above but just for a target address. So that + * we can fetch and transfer the data at different stride rates + * - regs 6 at hw + */ + uint32_t targetStrides; + /** + * Below is DMA control register address: + * - includes start('b0), endianness conversion('b1 and 2) and + * reset bits('b3) + * - bit of index 1 and 2 are for enabling 2 and 4 byte swaps + * respectively + * - start bit is meant to be set at the end while programming + * - regs 7 at hw + */ + uint32_t control; + /** + * Below is DMA status register address + * - first bit refers to halted status + * - 0 is for idle, and 1 for running + * - regs 8 at hw + */ + uint32_t status; + /** + * Below is the MMIO interface to tell the DMA that free() + * call occurred at the allocator compartment + * - regs 9 at hw + */ + uint32_t callFromMalloc; + }; + + __always_inline volatile DMAInterface &device() + { + return *MMIO_CAPABILITY(DMAInterface, dma); + } + + void write_strides(uint32_t sourceStrides, uint32_t targetStrides) + { + /** + * Setting source and target strides + */ + device().sourceStrides = sourceStrides; + device().targetStrides = targetStrides; + } + + int swap_bytes_and_start_dma(uint32_t swapAmount) + { + /** + * Setting byte swaps + * and start bit. + * + * Swap amount can be equal to + * only either 2 or 4. + */ + + if (swapAmount != 2) + { + if (swapAmount != 4) + { + swapAmount = 0; + } + } + + uint32_t controlConfiguration = swapAmount | 0x1; + + device().control = controlConfiguration; + + return 0; + } + + public: + uint32_t read_status() + { + /** + * this statement returns the less signifance bit + * to show the halted status + */ + return device().status & 0x1; + } + + void write_conf_and_start(uint32_t *sourceAddress, + uint32_t *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) + { + /** + * Setting source and target addresses, and length fields + */ + device().sourceCapability = (void*) sourceAddress; + device().targetCapability = (void*) targetAddress; + device().lengthInBytes = lengthInBytes; + + write_strides(sourceStrides, targetStrides); + + swap_bytes_and_start_dma(byteSwapAmount); + } + + void notify_the_dma() + { + device().callFromMalloc = 1; + } + + void reset_dma() + { + /** + * Setting a reset bit, which is bit 3. + * this clears all the registers, + * but do not transfer the current transfer status anywhere + */ + device().control = 0x8; + } + }; +} // namespace Ibex + +// template +using PlatformDMA = Ibex::PlatformDMA; \ No newline at end of file From 980dca2f3b23b654962c2e80e816880da63be3a1 Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 6 Sep 2023 10:55:58 +0100 Subject: [PATCH 41/42] v4 dma sw stack - not tested yet --- examples/10.dma_test/dma-test-v4.cc | 54 ++++++++++ sdk/core/dma-v3/dma_compartment.cc | 18 +--- sdk/core/dma-v4/dma.h | 96 +++++++++++++++++ sdk/core/dma-v4/dma_compartment.cc | 123 +++++++++++++++++++++ sdk/core/dma-v4/dma_compartment.hh | 23 ++++ sdk/core/dma-v4/platform-dma.hh | 160 ++++++++++++++++++++++++++++ 6 files changed, 460 insertions(+), 14 deletions(-) create mode 100644 examples/10.dma_test/dma-test-v4.cc create mode 100644 sdk/core/dma-v4/dma.h create mode 100644 sdk/core/dma-v4/dma_compartment.cc create mode 100644 sdk/core/dma-v4/dma_compartment.hh create mode 100644 sdk/core/dma-v4/platform-dma.hh diff --git a/examples/10.dma_test/dma-test-v4.cc b/examples/10.dma_test/dma-test-v4.cc new file mode 100644 index 00000000..eade3174 --- /dev/null +++ b/examples/10.dma_test/dma-test-v4.cc @@ -0,0 +1,54 @@ +// Copyright Microsoft and CHERIoT Contributors. +// SPDX-License-Identifier: MIT +#define MALLOC_QUOTA 0x10000000 + +#include +#include +#include +#include +#include + +#include <../../sdk/core/dma-v4/dma.h> + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +// Thread entry point. +void __cheri_compartment("dma_test") test_dma() +{ + Debug::log("DMA app entered, v4.0!"); + + // This is a dma process between two different memory addresses + uint32_t bytes = 8096; + uint32_t words = bytes / 4; + uint32_t byteSwap = 0; + + uint32_t *sourceAddress = (uint32_t *)malloc(bytes); + uint32_t *targetAddress = (uint32_t *)malloc(bytes); + + for (int i = 0; i < words; i++) + { + *(sourceAddress + i) = i + 100; + *(targetAddress + i) = 0; + } + + Debug::log("M:Ind: 0 and last, Source values BEFORE dma: {}, {}", + *(sourceAddress), + *(sourceAddress + words - 1)); + Debug::log("M: Ind: 0 and last, Dest-n values BEFORE dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + + static DMA::Device dmaDevice; + + int ret = dmaDevice.configure_and_launch( + sourceAddress, targetAddress, bytes, 0, 0, byteSwap); + + Debug::log("Main, ret: {}", ret); + + Debug::log("M: Ind: 0 and last, Dest-n values AFTER dma: {}, {}", + *(targetAddress), + *(targetAddress + words - 1)); + + Debug::log("M: End of test"); +} diff --git a/sdk/core/dma-v3/dma_compartment.cc b/sdk/core/dma-v3/dma_compartment.cc index bd7a322a..5af64651 100644 --- a/sdk/core/dma-v3/dma_compartment.cc +++ b/sdk/core/dma-v3/dma_compartment.cc @@ -89,22 +89,12 @@ int launch_dma(uint32_t *sourceAddress, "ExpectedValue is not equal to the current interrupt counter!"); /** - * return if sufficient permissions are not present - * and if not long enough + * No checks at the driver at this version. + * + * So, once returned after interrupt and reset, if any, + * we write to the DMA and start the transfer */ - if (!check_pointer( - sourceAddress, lengthInBytes) || - !check_pointer( - targetAddress, lengthInBytes)) - { - return -EINVAL; - } - - /** - * After passing all the checks, - * we can reset the alreadyReset value - */ alreadyReset = 0; platformDma.write_conf_and_start(sourceAddress, diff --git a/sdk/core/dma-v4/dma.h b/sdk/core/dma-v4/dma.h new file mode 100644 index 00000000..227337dd --- /dev/null +++ b/sdk/core/dma-v4/dma.h @@ -0,0 +1,96 @@ +#include "dma_compartment.hh" +#include "futex.h" +#include "platform-dma.hh" +#include +#include +#include +#include +#include +#include + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, + dma, + true, + true); + +using namespace Ibex; + +namespace DMA +{ + + template + concept IsDmaDevice = requires(T device, + DMADescriptor *dmaDescriptorPointer) + { + { + device.write_conf_and_start(dmaDescriptorPointer) + } -> std::same_as; + { + device.reset_dma(dmaDescriptorPointer) + } -> std::same_as; + }; + + template + + requires IsDmaDevice + + class GenericDMA : public PlatformDMA + { + public: + int configure_and_launch(void *sourceAddress, + void *targetAddress, + uint32_t lengthInBytes, + uint32_t sourceStrides, + uint32_t targetStrides, + uint32_t byteSwapAmount) + { + /** + * Dma launch call: + * - checks for the dma ownership status, + * - for access rights, + * - creates claims for each source and destination addresses, + * - automatically resets the claims and the dma registers + * at the end of the transfer. + */ + Debug::log("before launch"); + + DMADescriptor *dmaDescriptorPointer; + + /** + * Set the configurations here, + * before sending the descriptor to the DMA + */ + dmaDescriptorPointer->sourceCapability = sourceAddress; + dmaDescriptorPointer->targetCapability = targetAddress; + dmaDescriptorPointer->lengthInBytes = lengthInBytes; + dmaDescriptorPointer->sourceStrides = sourceStrides; + dmaDescriptorPointer->targetStrides = targetStrides; + dmaDescriptorPointer->byteSwaps = byteSwapAmount; + + int dmaInterruptReturn = launch_dma(dmaDescriptorPointer); + + Debug::log("after launch: {}", dmaInterruptReturn); + + int freeStatus; + freeStatus = free(sourceAddress); + Debug::log("driver, freeStatus: {}", freeStatus); + + if (dmaInterruptReturn < 0) + { + return -EINVAL; + } + + // todo: implement a do-while loop later in case! + int restartReturn = wait_and_reset_dma(dmaInterruptReturn, dmaDescriptorPointer); + + Debug::log("after restart: {}", restartReturn); + + return 0; + } + }; + + using Device = GenericDMA; +} // namespace DMA diff --git a/sdk/core/dma-v4/dma_compartment.cc b/sdk/core/dma-v4/dma_compartment.cc new file mode 100644 index 00000000..5efe2ac2 --- /dev/null +++ b/sdk/core/dma-v4/dma_compartment.cc @@ -0,0 +1,123 @@ +#include "futex.h" + +#include "dma_compartment.hh" +#include +#include +#include + +#include "platform-dma.hh" +#include +#include +#include +#include +#include +#include + +// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug; + +// Import some useful things from the CHERI namespace. +using namespace CHERI; +using namespace Ibex; + +Ibex::PlatformDMA platformDma; + +DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, + dma, + true, + true); + +int launch_dma(DMADescriptor *dmaDescriptorPointer) +{ + /** + * No lock necessary due to single write point + * and automatic reset + */ + + /** + * No checks at the driver at this version. + * + * So, once returned after interrupt and reset, if any, + * we write to the DMA and start the transfer + */ + + /** + * Control register are set here for the start bit. + * Even it is set before at the drive, it is reset here + */ + dmaDescriptorPointer->control = 1; + platformDma.write_conf_and_start(dmaDescriptorPointer); + + static const uint32_t *dmaFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + uint32_t currentInterruptCounter = *dmaFutex; + + /** + * return the interrupt, + * so that initiator can wait for it + */ + + return currentInterruptCounter; +} + +int wait_and_reset_dma(uint32_t interruptNumber, + DMADescriptor *originalDescriptorPointer) +{ + /** + * Handle the interrupt here, once dmaFutex woke up via scheduler. + * DMA interrupt means that the dma operation is finished + * and it is time to reset and clear the dma configuration registers. + */ + + /** + * However, clear only if the addresses are not reset yet. + * Because this function can be called from two different points + */ + + static const uint32_t *dmaFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + Debug::log("before futex wait"); + + Timeout t{10}; + futex_timed_wait(&t, dmaFutex, interruptNumber); + + int startedStatus = originalDescriptorPointer->status; + + if (startedStatus == 1) + { + /** + * Resetting the dma registers + * todo: we need some check here to avoid double checks + */ + Debug::log("inside the reset condition"); + + originalDescriptorPointer->control = 8; + + platformDma.reset_dma(originalDescriptorPointer); + + /** + * Acknowledging interrupt here irrespective of the reset status + */ + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + return 0; + } + + return 1; +} + +void force_stop_dma(DMADescriptor *originalDescriptorPointer) +{ + /** + * Calling the platform function to forcefully reset the DMA + * with updated ptr. + * + * However, DMA controller will still conduct checks to make sure + * that the right descriptor only can abort the transfer + */ + + originalDescriptorPointer->control = 8; + platformDma.reset_dma(originalDescriptorPointer); +} \ No newline at end of file diff --git a/sdk/core/dma-v4/dma_compartment.hh b/sdk/core/dma-v4/dma_compartment.hh new file mode 100644 index 00000000..a71d0a9f --- /dev/null +++ b/sdk/core/dma-v4/dma_compartment.hh @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include "platform-dma.hh" + +using namespace Ibex; + +/** + * A function below claims the source and target addresses of the DMA interface. + * While, DMA is in progress, these addresses will be claimed by the DMA + * compartment and so the memory will not be freed. + */ + +int __cheri_compartment("dma") launch_dma(DMADescriptor *dmaDescriptorPointer); + +int __cheri_compartment("dma") + wait_and_reset_dma(uint32_t interruptNumber, + DMADescriptor *originalDescriptorPointer); + +void __cheri_compartment("dma") + force_stop_dma(DMADescriptor *originalDescriptorPointer); \ No newline at end of file diff --git a/sdk/core/dma-v4/platform-dma.hh b/sdk/core/dma-v4/platform-dma.hh new file mode 100644 index 00000000..fa55812f --- /dev/null +++ b/sdk/core/dma-v4/platform-dma.hh @@ -0,0 +1,160 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Ibex +{ + /** + * Drivers should obey this DMADescriptor structure + * + * If they do not obey the right structure and the addresses, + * bounds and permissions are wrong, DMA does not start. + */ + struct DMADescriptor + { + /** + * Below is DMA source capability register address: + * - here source address is where FROM the DMA should transfer the + * data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any + * peripheral or accelerator) + * - this capability also includes the both address and the access bits + * - regs 0 and 1 at hw + */ + void* sourceCapability; + /** + * Below is DMA target capability register address: + * - here target address is where TO the DMA should transfer the + * data + * - it can be either memory buffer or MMAP-ed I/O device (i.e. any + * peripheral or accelerator) + * - this capability also includes the both address and the access bits + * - regs 2 and 3 at hw + */ + void* targetCapability; + /** + * Below is the amount of data IN BYTES that DMA should transfer + * between the source and a target + * - regs 4 at hw + */ + uint32_t lengthInBytes; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a source + * address. Strides is the jump amount between each data retrieval + * address during the DMA operation. Minimum width of the stride is + * 4 byte words. + * - 0 stride is default and points to the address of the next word + * - 1 is for 1 word skippal, and 2 for 2 before the next address + * - todo: more fine grain stride configurability (in term of + * bytes) for the future? + * - regs 5 at hw + */ + uint32_t sourceStrides; + /** + * Below is the amount of strides IN 4 BYTE WORDS for a target + * address. The same as above but just for a target address. So that + * we can fetch and transfer the data at different stride rates + * - regs 6 at hw + */ + uint32_t targetStrides; + /** + * Below is the register to show the number of byte swaps to be 2 or 4. + * This will be checked for the right value at the hw again. + * - regs 7 at hw + */ + uint32_t byteSwaps; + /** + * Ideally the following two registers should be set only + * at the compartment! + */ + + /** + * Below is DMA control register address: + * - includes start('b0), endianness conversion('b1 and 2) and + * reset bits('b3) + * - bit of index 1 and 2 are for enabling 2 and 4 byte swaps + * respectively + * - start bit is meant to be set at the end while programming + * - regs 7 at hw + */ + uint32_t control; + /** + * Below is DMA status register address + * - first bit refers to success status + * - regs 8 at hw + */ + uint32_t status; + }; + + class PlatformDMA + { + private: + + struct DMAInterface + { + /** + * All configurations would be written to the hw via + * setting the dmaDescriptorPointer to the right descriptor + * + * The structure of the descriptor is written here. + * This driver does not use tne reset register and resets + * automatically. + */ + + DMADescriptor *dmaDescriptorPointer; + /** + * Below is the MMIO interface to tell the DMA that free() + * call occurred at the allocator compartment + * - regs 9 at hw + */ + uint32_t callFromMalloc; + }; + + __always_inline volatile DMAInterface &device() + { + return *MMIO_CAPABILITY(DMAInterface, dma); + } + + public: + + void write_conf_and_start(DMADescriptor *dmaDescriptorPointer) + { + /** + * Setting configurations via pointing + * to a descriptor adddress. + * + * DMA controller should be able to fetch the data on its own + */ + device().dmaDescriptorPointer = dmaDescriptorPointer; + } + + void notify_the_dma() + { + device().callFromMalloc = 1; + } + + void reset_dma(DMADescriptor *updatedDescriptorPointer) + { + /** + * Setting a reset bit, which is bit 3. + * this clears all the registers, + * but do not transfer the current transfer status anywhere. + * + * DMA driver should send the pointer with updated control + * field to enable this change. + * + * HW will check whether initiator and reset descriptor pointer + * are similar to guarantee the correctness + */ + device().dmaDescriptorPointer = updatedDescriptorPointer; + } + }; +} // namespace Ibex + +// template +using PlatformDMA = Ibex::PlatformDMA; \ No newline at end of file From 903d15921804a8a210e604a32527cfad91ed140c Mon Sep 17 00:00:00 2001 From: Nazerke Turtayeva Date: Wed, 6 Sep 2023 11:36:59 +0100 Subject: [PATCH 42/42] modified v4, compartment call removed --- examples/10.dma_test/dma-test-v4.cc | 21 +++++++-- sdk/core/dma-v4/dma.h | 67 ++++++++++++++++------------- sdk/core/dma-v4/platform-dma.hh | 6 ++- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/examples/10.dma_test/dma-test-v4.cc b/examples/10.dma_test/dma-test-v4.cc index eade3174..7b39379a 100644 --- a/examples/10.dma_test/dma-test-v4.cc +++ b/examples/10.dma_test/dma-test-v4.cc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT #define MALLOC_QUOTA 0x10000000 +#include <../../sdk/core/dma-v4/platform-dma.hh> #include #include #include @@ -11,7 +12,9 @@ #include <../../sdk/core/dma-v4/dma.h> // Expose debugging features unconditionally for this compartment. -using Debug = ConditionalDebug; +using Debug = ConditionalDebug; + +using namespace Ibex; // Thread entry point. void __cheri_compartment("dma_test") test_dma() @@ -32,6 +35,19 @@ void __cheri_compartment("dma_test") test_dma() *(targetAddress + i) = 0; } + DMADescriptor *dmaDescriptorPointer = (DMADescriptor *) malloc(sizeof(DMADescriptor)); + + /** + * Set the configurations here, + * before sending the descriptor to the DMA + */ + dmaDescriptorPointer->sourceCapability = sourceAddress; + dmaDescriptorPointer->targetCapability = targetAddress; + dmaDescriptorPointer->lengthInBytes = bytes; + dmaDescriptorPointer->sourceStrides = 0; + dmaDescriptorPointer->targetStrides = 0; + dmaDescriptorPointer->byteSwaps = byteSwap; + Debug::log("M:Ind: 0 and last, Source values BEFORE dma: {}, {}", *(sourceAddress), *(sourceAddress + words - 1)); @@ -41,8 +57,7 @@ void __cheri_compartment("dma_test") test_dma() static DMA::Device dmaDevice; - int ret = dmaDevice.configure_and_launch( - sourceAddress, targetAddress, bytes, 0, 0, byteSwap); + int ret = dmaDevice.configure_and_launch(dmaDescriptorPointer); Debug::log("Main, ret: {}", ret); diff --git a/sdk/core/dma-v4/dma.h b/sdk/core/dma-v4/dma.h index 227337dd..d0fbf72d 100644 --- a/sdk/core/dma-v4/dma.h +++ b/sdk/core/dma-v4/dma.h @@ -1,4 +1,3 @@ -#include "dma_compartment.hh" #include "futex.h" #include "platform-dma.hh" #include @@ -9,7 +8,7 @@ #include // Expose debugging features unconditionally for this compartment. -using Debug = ConditionalDebug; +using Debug = ConditionalDebug; DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, dma, @@ -18,6 +17,8 @@ DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY(dmaInterruptCapability, using namespace Ibex; +Ibex::PlatformDMA platformDma; + namespace DMA { @@ -40,12 +41,7 @@ namespace DMA class GenericDMA : public PlatformDMA { public: - int configure_and_launch(void *sourceAddress, - void *targetAddress, - uint32_t lengthInBytes, - uint32_t sourceStrides, - uint32_t targetStrides, - uint32_t byteSwapAmount) + int configure_and_launch(DMADescriptor *dmaDescriptorPointer) { /** * Dma launch call: @@ -56,37 +52,46 @@ namespace DMA * at the end of the transfer. */ Debug::log("before launch"); - - DMADescriptor *dmaDescriptorPointer; - /** - * Set the configurations here, - * before sending the descriptor to the DMA - */ - dmaDescriptorPointer->sourceCapability = sourceAddress; - dmaDescriptorPointer->targetCapability = targetAddress; - dmaDescriptorPointer->lengthInBytes = lengthInBytes; - dmaDescriptorPointer->sourceStrides = sourceStrides; - dmaDescriptorPointer->targetStrides = targetStrides; - dmaDescriptorPointer->byteSwaps = byteSwapAmount; + platformDma.write_conf_and_start(dmaDescriptorPointer); + + static const uint32_t *dmaFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + uint32_t currentInterruptCounter = *dmaFutex; + + Debug::log("after launch: {}", currentInterruptCounter); + + // int freeStatus; + // freeStatus = free(sourceAddress); + // Debug::log("driver, freeStatus: {}", freeStatus); - int dmaInterruptReturn = launch_dma(dmaDescriptorPointer); + // todo: implement a do-while loop later in case! - Debug::log("after launch: {}", dmaInterruptReturn); + Timeout t{10}; + futex_timed_wait(&t, dmaFutex, currentInterruptCounter); - int freeStatus; - freeStatus = free(sourceAddress); - Debug::log("driver, freeStatus: {}", freeStatus); + int startedStatus = dmaDescriptorPointer->status; - if (dmaInterruptReturn < 0) + if (startedStatus == 1) { - return -EINVAL; - } + /** + * Resetting the dma registers + * todo: we need some check here to avoid double checks + */ + Debug::log("inside the reset condition"); - // todo: implement a do-while loop later in case! - int restartReturn = wait_and_reset_dma(dmaInterruptReturn, dmaDescriptorPointer); + platformDma.reset_dma(dmaDescriptorPointer); + + /** + * Acknowledging interrupt here irrespective of the reset status + */ + interrupt_complete(STATIC_SEALED_VALUE(dmaInterruptCapability)); + + return 0; + } - Debug::log("after restart: {}", restartReturn); + Debug::log("after restart"); return 0; } diff --git a/sdk/core/dma-v4/platform-dma.hh b/sdk/core/dma-v4/platform-dma.hh index fa55812f..7bab6cee 100644 --- a/sdk/core/dma-v4/platform-dma.hh +++ b/sdk/core/dma-v4/platform-dma.hh @@ -130,6 +130,7 @@ namespace Ibex * * DMA controller should be able to fetch the data on its own */ + dmaDescriptorPointer->control = 1; device().dmaDescriptorPointer = dmaDescriptorPointer; } @@ -138,7 +139,7 @@ namespace Ibex device().callFromMalloc = 1; } - void reset_dma(DMADescriptor *updatedDescriptorPointer) + void reset_dma(DMADescriptor *originalDescriptorPointer) { /** * Setting a reset bit, which is bit 3. @@ -151,7 +152,8 @@ namespace Ibex * HW will check whether initiator and reset descriptor pointer * are similar to guarantee the correctness */ - device().dmaDescriptorPointer = updatedDescriptorPointer; + originalDescriptorPointer->control = 8; + device().dmaDescriptorPointer = originalDescriptorPointer; } }; } // namespace Ibex