From 45bb4f34d8806ff8108010294b75989959e9fc13 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Mon, 1 Dec 2025 16:30:33 +0800 Subject: [PATCH 001/133] init to sync with shm branch --- README.md | 11 +++++++++++ kernel/include/shared_memory.h | 6 +++--- kernel/src/object/cnode.c | 2 +- .../overlay-reserve-vm-memory.dts | 6 +++++- kernel/src/shared_memory.c | 6 +++--- .../libsel4test/include/sel4test/test.h | 3 ++- projects/sel4test/apps/hello-world/src/main.c | 12 ++++++++++++ .../sel4test/apps/sel4test-driver/src/main.c | 18 +++++++++++------- .../apps/sel4test-tests/src/tests/vspace.c | 15 ++++++++++++++- 9 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..9259de4 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# This repo for user-level function developement on seL4test + +Quick start on Qemu: + +``` +mkdir build +cd build +../init-build.sh -DPLATFORM=qemu-arm-virt -DSIMULATION=TRUE -DSel4testApp=hello-world +ninja +./simulate +``` diff --git a/kernel/include/shared_memory.h b/kernel/include/shared_memory.h index 90df305..fb7fd2d 100755 --- a/kernel/include/shared_memory.h +++ b/kernel/include/shared_memory.h @@ -9,10 +9,10 @@ #include #include // 共享内存物理地址定义 (与hvisor配置一致) -#define SHM_PADDR_DATA 0xDE000000UL +#define SHM_PADDR_DATA 0x59000000UL #define SHM_SIZE_DATA 0x00400000UL /* 4MB */ -#define SHM_PADDR_ROOT_Q 0xDE400000UL /* Root Linux队列 */ -#define SHM_PADDR_SEL4_Q 0xDE410000UL /* seL4队列 */ +#define SHM_PADDR_ROOT_Q 0x59400000UL /* Root Linux队列 */ +#define SHM_PADDR_SEL4_Q 0x59410000UL /* seL4队列 */ #define SHM_VADDR_DATA SHM_PADDR_DATA + PPTR_BASE_OFFSET #define SHM_VADDR_ROOT_Q SHM_PADDR_ROOT_Q + PPTR_BASE_OFFSET /* Root Linux队列 */ #define SHM_VADDR_SEL4_Q SHM_PADDR_SEL4_Q + PPTR_BASE_OFFSET diff --git a/kernel/src/object/cnode.c b/kernel/src/object/cnode.c index 8b8540b..50f323b 100755 --- a/kernel/src/object/cnode.c +++ b/kernel/src/object/cnode.c @@ -51,7 +51,7 @@ exception_t decodeCNodeInvocation(word_t invLabel, word_t length, cap_t cap, assert(cap_get_capType(cap) == cap_cnode_cap); if (invLabel < CNodeRevoke || invLabel > CNODE_LAST_INVOCATION) { - userError("CNodeCap: Illegal Operation attempted."); + // userError("CNodeCap: Illegal Operation attempted."); current_syscall_error.type = seL4_IllegalOperation; return EXCEPTION_SYSCALL_ERROR; } diff --git a/kernel/src/plat/qemu-arm-virt/overlay-reserve-vm-memory.dts b/kernel/src/plat/qemu-arm-virt/overlay-reserve-vm-memory.dts index f4c18af..95658ca 100755 --- a/kernel/src/plat/qemu-arm-virt/overlay-reserve-vm-memory.dts +++ b/kernel/src/plat/qemu-arm-virt/overlay-reserve-vm-memory.dts @@ -15,8 +15,12 @@ vm-memory@40000000 { reg = <0x0 0x40000000 0x0 0x20000000>; no-map; + + vm-memory@ea000000 { + no-map; + reg = <0x00 0xea000000 0x00 0x00420000>; // 4.125MB共享内存 }; - }; +}; }; diff --git a/kernel/src/shared_memory.c b/kernel/src/shared_memory.c index 4611725..fd48fcf 100755 --- a/kernel/src/shared_memory.c +++ b/kernel/src/shared_memory.c @@ -19,10 +19,10 @@ #include // 共享内存物理地址定义 (与hvisor配置一致) -#define SHM_PADDR_DATA 0xDE000000UL +#define SHM_PADDR_DATA 0x59000000UL #define SHM_SIZE_DATA 0x00400000UL /* 4MB */ -#define SHM_PADDR_ROOT_Q 0xDE400000UL /* Root Linux队列 */ -#define SHM_PADDR_SEL4_Q 0xDE410000UL /* seL4队列 */ +#define SHM_PADDR_ROOT_Q 0x59400000UL /* Root Linux队列 */ +#define SHM_PADDR_SEL4_Q 0x59410000UL /* seL4队列 */ #define SHM_PAGE_SIZE 0x1000UL // 消息队列初始化标记 diff --git a/projects/seL4_libs/libsel4test/include/sel4test/test.h b/projects/seL4_libs/libsel4test/include/sel4test/test.h index bfa51cc..3589fdd 100755 --- a/projects/seL4_libs/libsel4test/include/sel4test/test.h +++ b/projects/seL4_libs/libsel4test/include/sel4test/test.h @@ -150,7 +150,8 @@ typedef struct testcase ALIGN(sizeof(struct testcase)) testcase_t; _enabled, \ }; -#define DEFINE_TEST(_name, _description, _function, _enabled) DEFINE_TEST_WITH_TYPE(_name, _description, _function, BASIC, _enabled) +#define DEFINE_TEST(_name, _description, _function, _enabled) ; +#define DEFINE_TESTQ(_name, _description, _function, _enabled) DEFINE_TEST_WITH_TYPE(_name, _description, _function, BASIC, _enabled) #define DEFINE_TEST_BOOTSTRAP(_name, _description, _function, _enabled) DEFINE_TEST_WITH_TYPE(_name, _description, _function, BOOTSTRAP, _enabled) /**/ diff --git a/projects/sel4test/apps/hello-world/src/main.c b/projects/sel4test/apps/hello-world/src/main.c index e419060..6cf7050 100755 --- a/projects/sel4test/apps/hello-world/src/main.c +++ b/projects/sel4test/apps/hello-world/src/main.c @@ -8,6 +8,7 @@ #include #include #include +#include void __plat_putchar(int c); static size_t write_buf(void *data, size_t count) @@ -69,6 +70,17 @@ int main(void) { printf("DATA boundary test: OK\n"); // === 边界测试结束 === printString((char*)(0x53f000)); + + // test malloc free here + void *ptr = malloc(1024); + if (ptr) { + printf("Malloc 1024 bytes successful: %p\n", ptr); + free(ptr); + printf("Free successful\n"); + } else { + printf("Malloc failed\n"); + } + while (1) { /* code */ diff --git a/projects/sel4test/apps/sel4test-driver/src/main.c b/projects/sel4test/apps/sel4test-driver/src/main.c index d671851..7beb1a8 100755 --- a/projects/sel4test/apps/sel4test-driver/src/main.c +++ b/projects/sel4test/apps/sel4test-driver/src/main.c @@ -594,13 +594,17 @@ void *main_continued(void *arg UNUSED) memcpy(env.init->untyped_size_bits_list, untyped_size_bits_list, sizeof(uint8_t) * env.num_untypeds); /* parse elf region data about the test image to pass to the tests app */ - num_elf_regions = sel4utils_elf_num_regions(&tests_elf); - assert(num_elf_regions <= MAX_REGIONS); - sel4utils_elf_reserve(NULL, &tests_elf, elf_regions); - - /* copy the region list for the process to clone itself */ - memcpy(env.init->elf_regions, elf_regions, sizeof(sel4utils_elf_region_t) * num_elf_regions); - env.init->num_elf_regions = num_elf_regions; + // printf("About to parse ELF regions...\n"); + // num_elf_regions = sel4utils_elf_num_regions(&tests_elf); + // assert(num_elf_regions <= MAX_REGIONS); + // printf("TEST 0001\n"); + // sel4utils_elf_reserve(NULL, &tests_elf, elf_regions); + // printf("TEST 0002\n"); + + // /* copy the region list for the process to clone itself */ + // memcpy(env.init->elf_regions, elf_regions, sizeof(sel4utils_elf_region_t) * num_elf_regions); + // env.init->num_elf_regions = num_elf_regions; + // printf("env num_elf_regions: %d\n", num_elf_regions); /* setup init data that won't change test-to-test */ env.init->priority = seL4_MaxPrio - 1; diff --git a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c index f69a631..ed90645 100755 --- a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c +++ b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c @@ -125,9 +125,22 @@ test_unmap_after_delete(env_t env) /* unmap the frame */ seL4_ARM_Page_Unmap(frame); + printf("----------------ROOTSERVER--------------------\n"); + seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); + printf("Shmem Comm Buff Pptr: 0x%lx\n", (seL4_Word)Ptr2ShmemCommBuff); + unsigned long long *vaddrs=(unsigned long long *)*Ptr2ShmemCommBuff; + printf("Shmem Comm VAddrs ptr: 0x%llx\n", (seL4_Word)vaddrs); + + // 打印三个共享内存区域的虚拟地址 + // printf("Shmem Comm VAddrs:\n"); + // printf(" ROOT_Q VADDR: 0x%llx\n", vaddrs[0]); // [0] = ROOT_Q + // printf(" SEL4_Q VADDR: 0x%llx\n", vaddrs[1]); // [1] = SEL4_Q + // printf(" DATA VADDR: 0x%llx\n", vaddrs[2]); // [2] = DATA + // printf("--------------------------------------------\n"); + return sel4test_get_result(); } -DEFINE_TEST(VSPACE0001, "Test unmapping a page after deleting the PD", test_unmap_after_delete, true) +DEFINE_TESTQ(VSPACE0001, "Test unmapping a page after deleting the PD", test_unmap_after_delete, true) #endif /* CONFIG_ARCH_AARCHxx */ static int -- Gitee From 2c1918e18af9ee5b53eb5f87148eafabc30fabe4 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Thu, 20 Nov 2025 02:21:01 +0800 Subject: [PATCH 002/133] filter only vspace test --- projects/sel4test/apps/sel4test-tests/src/tests/vspace.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c index ed90645..a7b5c8f 100755 --- a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c +++ b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c @@ -33,7 +33,7 @@ static int test_interas_diffcspace(env_t env) return sel4test_get_result(); } -DEFINE_TEST(VSPACE0000, "Test threads in different cspace/vspace", test_interas_diffcspace, true) +DEFINE_TESTQ(VSPACE0000, "Test threads in different cspace/vspace", test_interas_diffcspace, true) #if defined(CONFIG_ARCH_AARCH32) static int @@ -118,6 +118,13 @@ test_unmap_after_delete(env_t env) error = seL4_ARM_Page_Map(frame, vspace, map_addr, seL4_AllRights, seL4_ARM_Default_VMAttributes); test_error_eq(error, seL4_NoError); + // test set and get on map_addr address + uint32_t *p = (uint32_t*)0x10000000; + *p = 0xCAFEBABE; + printf("Value = %u\n", *p); + assert(*p == 0xCAFEBABE); + + /* delete the page directory */ vka_cspace_make_path(&env->vka, vspace, &path); seL4_CNode_Delete(path.root, path.capPtr, path.capDepth); -- Gitee From c0e392df3bc33ce0987ef49d45273bf5e9ee3df8 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Thu, 27 Nov 2025 11:05:19 +0800 Subject: [PATCH 003/133] add and test xmalloc locally --- .../apps/sel4test-tests/CMakeLists.txt | 3 + .../apps/sel4test-tests/src/malloc_stub.c | 150 ++++++++++++++++++ .../apps/sel4test-tests/src/tests/vspace.c | 59 +++++++ 3 files changed, 212 insertions(+) create mode 100644 projects/sel4test/apps/sel4test-tests/src/malloc_stub.c diff --git a/projects/sel4test/apps/sel4test-tests/CMakeLists.txt b/projects/sel4test/apps/sel4test-tests/CMakeLists.txt index cb3b677..b6cac44 100755 --- a/projects/sel4test/apps/sel4test-tests/CMakeLists.txt +++ b/projects/sel4test/apps/sel4test-tests/CMakeLists.txt @@ -27,6 +27,9 @@ file( src/arch/${KernelArch}/tests/*.S ) +# Ensure malloc stub is included even if added after initial configure +list(APPEND deps src/malloc_stub.c) + add_executable(sel4test-tests EXCLUDE_FROM_ALL ${deps}) # special handling for "arm_hyp", it's really "aarch32" set(_inc_folder_KernelSel4Arch "${KernelSel4Arch}") diff --git a/projects/sel4test/apps/sel4test-tests/src/malloc_stub.c b/projects/sel4test/apps/sel4test-tests/src/malloc_stub.c new file mode 100644 index 0000000..5fd9fb6 --- /dev/null +++ b/projects/sel4test/apps/sel4test-tests/src/malloc_stub.c @@ -0,0 +1,150 @@ +/* +/* Mixed allocator: small allocations served from local static slab pools + * (array-based), large allocations served by mapping pages using the vspace + * (via vspace_new_pages). This implementation keeps slab storage in static + * arrays (no dynamic memory) and records page-backed allocations so they + * can be unmapped on free. */ + +#include +#include +#include +#include + +#include "helpers.h" /* env_t */ +#include + +#define MAX_SMALL_ALLOC 256 +#define SLOTS_PER_CLASS 64 + +/* slab classes */ +static const size_t slab_sizes[] = {16, 32, 64, 128, 256}; +#define NSLAB (sizeof(slab_sizes) / sizeof(slab_sizes[0])) + +/* static slab storage: NSLAB classes x SLOTS_PER_CLASS slots x MAX_SMALL_ALLOC bytes */ +static uint8_t slab_storage[NSLAB][SLOTS_PER_CLASS][MAX_SMALL_ALLOC]; +/* per-class free stacks (indices) */ +static int16_t slab_free_stack_top[NSLAB]; +static int16_t slab_free_stack[NSLAB][SLOTS_PER_CLASS]; + +/* page-backed allocation records */ +#define MAX_PAGE_ALLOCS 128 +typedef struct page_alloc_rec { + void *vaddr; + size_t num_pages; +} page_alloc_rec_t; +static page_alloc_rec_t page_allocs[MAX_PAGE_ALLOCS]; +static int page_allocs_count = 0; + +/* allocator env */ +static env_t alloc_env = NULL; +static int alloc_initted = 0; + +/* Initialize allocator (must be called from test) */ +void xmalloc_init(env_t env) +{ + if (alloc_initted || env == NULL) return; + alloc_env = env; + + /* initialise slab free stacks */ + for (int c = 0; c < (int)NSLAB; c++) { + slab_free_stack_top[c] = 0; + for (int i = 0; i < SLOTS_PER_CLASS; i++) { + slab_free_stack[c][i] = SLOTS_PER_CLASS - 1 - i; /* fill with indices */ + } + } + + page_allocs_count = 0; + alloc_initted = 1; +} + +/* helper: pick slab class for size, or -1 if too large */ +static int slab_class_for_size(size_t size) +{ + for (int i = 0; i < (int)NSLAB; i++) { + if (size <= slab_sizes[i]) return i; + } + return -1; +} + +/* allocate pages via vspace_new_pages; record for free */ +static void *alloc_pages(size_t num_pages) +{ + if (!alloc_initted) return NULL; + if (num_pages == 0) return NULL; + void *vaddr = vspace_new_pages(&alloc_env->vspace, seL4_AllRights, num_pages, seL4_PageBits); + if (!vaddr) return NULL; + if (page_allocs_count < MAX_PAGE_ALLOCS) { + page_allocs[page_allocs_count].vaddr = vaddr; + page_allocs[page_allocs_count].num_pages = num_pages; + page_allocs_count++; + } + return vaddr; +} + +/* free page-backed region (unmap + let vspace free the frames) */ +static void free_pages(void *ptr) +{ + if (!alloc_initted || ptr == NULL) return; + for (int i = 0; i < page_allocs_count; i++) { + if (page_allocs[i].vaddr == ptr) { + vspace_unmap_pages(&alloc_env->vspace, ptr, page_allocs[i].num_pages, seL4_PageBits, VSPACE_FREE); + /* compact array */ + page_allocs[i] = page_allocs[page_allocs_count - 1]; + page_allocs_count--; + return; + } + } +} + +/* xmalloc: slab for small sizes, pages for large sizes */ +void *xmalloc(size_t size) +{ + if (size == 0) return NULL; + if (!alloc_initted) return NULL; + + if (size <= MAX_SMALL_ALLOC) { + int cls = slab_class_for_size(size); + if (cls >= 0) { + /* have a slab class */ + if (slab_free_stack_top[cls] < SLOTS_PER_CLASS) { + int idx = slab_free_stack[cls][slab_free_stack_top[cls]++]; + return (void *)&slab_storage[cls][idx][0]; + } + /* no free slot in this class; fallthrough to page alloc */ + } + } + + /* page-based allocation */ + size_t page_size = (size_t)1 << seL4_PageBits; + size_t num_pages = (size + page_size - 1) / page_size; + void *v = alloc_pages(num_pages); + return v; +} + +/* xfree: return to slab pool if pointer is within slab arrays, otherwise free pages */ +void xfree(void *ptr) +{ + if (!ptr || !alloc_initted) return; + + /* check slab pools */ + for (int c = 0; c < (int)NSLAB; c++) { + uint8_t *base = (uint8_t *)&slab_storage[c][0][0]; + size_t class_block = SLOTS_PER_CLASS * MAX_SMALL_ALLOC; + if ((uint8_t *)ptr >= base && (uint8_t *)ptr < base + class_block) { + size_t offset = (uint8_t *)ptr - base; + int idx = offset / MAX_SMALL_ALLOC; + /* push back */ + if (slab_free_stack_top[c] > 0) { + slab_free_stack[c][--slab_free_stack_top[c]] = idx; + } else { + /* stack is full, shouldn't happen */ + } + return; + } + } + + /* otherwise must be page-backed allocation; free if we recorded it */ + free_pages(ptr); +} + + diff --git a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c index a7b5c8f..f95008f 100755 --- a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c +++ b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c @@ -15,6 +15,12 @@ #include "../helpers.h" +/* xmalloc_init provided by malloc_stub.c */ +void xmalloc_init(env_t env); +/* test allocator API */ +void *xmalloc(size_t size); +void xfree(void *ptr); + static int remote_function(void) { return 42; @@ -285,6 +291,58 @@ static int test_create_asid_pools_and_touch(env_t env) } DEFINE_TEST(VSPACE0006, "Test touching all available ASID pools", test_create_asid_pools_and_touch, true) +#if 1 +static int +test_malloc_free(env_t env) +{ + /* initialise allocator heap backed by vspace pages */ + xmalloc_init(env); + + /* 1) Exercise slab allocations: allocate many small blocks until slabs + * are exhausted and some allocations fall back to page-backed. */ + const int NUM_SMALL = 70; /* should exceed per-class slab slots (default 64) */ + void *small_ptrs[NUM_SMALL]; + for (int i = 0; i < NUM_SMALL; i++) { + small_ptrs[i] = xmalloc(16); + test_assert(small_ptrs[i] != NULL); + memset(small_ptrs[i], 0xA5, 16); + for (int j = 0; j < 16; j++) { + test_assert(((unsigned char *)small_ptrs[i])[j] == 0xA5); + } + } + + /* free a couple of small slots and reallocate to test reuse */ + xfree(small_ptrs[10]); + xfree(small_ptrs[20]); + void *r1 = xmalloc(16); + test_assert(r1 != NULL); + void *r2 = xmalloc(16); + test_assert(r2 != NULL); + /* free them */ + xfree(r1); + xfree(r2); + + /* 2) Allocate a large, page-backed allocation and test read/write */ + void *big = xmalloc(4096); + test_assert(big != NULL); + memset(big, 0x5A, 4096); + for (int i = 0; i < 4096; i++) { + test_assert(((unsigned char *)big)[i] == 0x5A); + } + xfree(big); + + /* 3) Clean up remaining small allocations */ + for (int i = 0; i < NUM_SMALL; i++) { + if (small_ptrs[i]) { + xfree(small_ptrs[i]); + } + } + + return sel4test_get_result(); +} +DEFINE_TESTQ(XMALLOC0001, "Test malloc/free basic free-list allocator", test_malloc_free, true) +#endif + #ifdef CONFIG_ARCH_IA32 static int test_dirty_accessed_bits(env_t env) @@ -336,4 +394,5 @@ test_dirty_accessed_bits(env_t env) return sel4test_get_result(); } DEFINE_TEST(VSPACE0010, "Test dirty and accessed bits on mappings", test_dirty_accessed_bits, true) + #endif -- Gitee From 115a68a1d8871274d800c04f7c451ed0c6928420 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Mon, 1 Dec 2025 01:26:36 +0800 Subject: [PATCH 004/133] add local kv store function test --- .../apps/sel4test-tests/CMakeLists.txt | 2 + .../sel4test/apps/sel4test-tests/src/minikv.c | 120 ++++++++++++++++++ .../sel4test/apps/sel4test-tests/src/minikv.h | 15 +++ .../apps/sel4test-tests/src/tests/vspace.c | 68 +++++++++- 4 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 projects/sel4test/apps/sel4test-tests/src/minikv.c create mode 100644 projects/sel4test/apps/sel4test-tests/src/minikv.h diff --git a/projects/sel4test/apps/sel4test-tests/CMakeLists.txt b/projects/sel4test/apps/sel4test-tests/CMakeLists.txt index b6cac44..b3fc66a 100755 --- a/projects/sel4test/apps/sel4test-tests/CMakeLists.txt +++ b/projects/sel4test/apps/sel4test-tests/CMakeLists.txt @@ -29,6 +29,8 @@ file( # Ensure malloc stub is included even if added after initial configure list(APPEND deps src/malloc_stub.c) +# Ensure minikv is included even if added after initial configure +list(APPEND deps src/minikv.c) add_executable(sel4test-tests EXCLUDE_FROM_ALL ${deps}) # special handling for "arm_hyp", it's really "aarch32" diff --git a/projects/sel4test/apps/sel4test-tests/src/minikv.c b/projects/sel4test/apps/sel4test-tests/src/minikv.c new file mode 100644 index 0000000..d748e4a --- /dev/null +++ b/projects/sel4test/apps/sel4test-tests/src/minikv.c @@ -0,0 +1,120 @@ +/* Minimal key-value store implementation using chained hash table. + * Uses xmalloc/xfree for allocation; call minikv_init(env) before use + * so the underlying allocator (xmalloc) can be initialised. + */ +#include "minikv.h" +#include +#include + +/* forward declarations of test allocator functions (implemented in malloc_stub.c) */ +void xmalloc_init(env_t env); +void *xmalloc(size_t size); +void xfree(void *ptr); + +/* simple djb2 string hash */ +static unsigned long hash_str(const char *str) +{ + unsigned long hash = 5381; + int c; + while ((c = *str++)) + hash = ((hash << 5) + hash) + (unsigned char)c; + return hash; +} + +/* table parameters */ +#define MINIKV_BUCKETS 1024 + +typedef struct kv_node { + char *key; + void *value; + struct kv_node *next; +} kv_node_t; + +static kv_node_t *buckets[MINIKV_BUCKETS]; +static int initted = 0; + +int minikv_init(env_t env) +{ + if (initted) return 0; + if (env) { + xmalloc_init(env); + } + for (int i = 0; i < MINIKV_BUCKETS; i++) buckets[i] = NULL; + initted = 1; + return 0; +} + +int minikv_set(const char *key, void *value) +{ + if (!initted || !key) return -1; + unsigned long h = hash_str(key) % MINIKV_BUCKETS; + kv_node_t *n = buckets[h]; + while (n) { + if (strcmp(n->key, key) == 0) { + n->value = value; /* replace */ + return 0; + } + n = n->next; + } + /* not found -> insert */ + kv_node_t *node = (kv_node_t *)xmalloc(sizeof(kv_node_t)); + if (!node) return -1; + size_t klen = strlen(key) + 1; + node->key = (char *)xmalloc(klen); + if (!node->key) { + xfree(node); + return -1; + } + memcpy(node->key, key, klen); + node->value = value; + node->next = buckets[h]; + buckets[h] = node; + return 0; +} + +void *minikv_get(const char *key) +{ + if (!initted || !key) return NULL; + unsigned long h = hash_str(key) % MINIKV_BUCKETS; + kv_node_t *n = buckets[h]; + while (n) { + if (strcmp(n->key, key) == 0) return n->value; + n = n->next; + } + return NULL; +} + +int minikv_delete(const char *key) +{ + if (!initted || !key) return -1; + unsigned long h = hash_str(key) % MINIKV_BUCKETS; + kv_node_t *n = buckets[h]; + kv_node_t *prev = NULL; + while (n) { + if (strcmp(n->key, key) == 0) { + if (prev) prev->next = n->next; else buckets[h] = n->next; + xfree(n->key); + xfree(n); + return 0; + } + prev = n; + n = n->next; + } + return -1; +} + +void minikv_destroy(void) +{ + if (!initted) return; + for (int i = 0; i < MINIKV_BUCKETS; i++) { + kv_node_t *n = buckets[i]; + while (n) { + kv_node_t *next = n->next; + xfree(n->key); + xfree(n); + n = next; + } + buckets[i] = NULL; + } + initted = 0; +} diff --git a/projects/sel4test/apps/sel4test-tests/src/minikv.h b/projects/sel4test/apps/sel4test-tests/src/minikv.h new file mode 100644 index 0000000..fe629cc --- /dev/null +++ b/projects/sel4test/apps/sel4test-tests/src/minikv.h @@ -0,0 +1,15 @@ +/* Minimal hashtable-based key-value store (minikv) + * Keys: null-terminated strings (copied) + * Values: void* (opaque pointer stored as-is) + */ +#pragma once + +#include +#include +#include "helpers.h" + +int minikv_init(env_t env); +int minikv_set(const char *key, void *value); +void *minikv_get(const char *key); +int minikv_delete(const char *key); +void minikv_destroy(void); diff --git a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c index f95008f..cfe213e 100755 --- a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c +++ b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c @@ -20,6 +20,12 @@ void xmalloc_init(env_t env); /* test allocator API */ void *xmalloc(size_t size); void xfree(void *ptr); +/* minikv API */ +int minikv_init(env_t env); +int minikv_set(const char *key, void *value); +void *minikv_get(const char *key); +int minikv_delete(const char *key); +void minikv_destroy(void); static int remote_function(void) { @@ -39,7 +45,7 @@ static int test_interas_diffcspace(env_t env) return sel4test_get_result(); } -DEFINE_TESTQ(VSPACE0000, "Test threads in different cspace/vspace", test_interas_diffcspace, true) +DEFINE_TEST(VSPACE0000, "Test threads in different cspace/vspace", test_interas_diffcspace, true) #if defined(CONFIG_ARCH_AARCH32) static int @@ -340,9 +346,67 @@ test_malloc_free(env_t env) return sel4test_get_result(); } -DEFINE_TESTQ(XMALLOC0001, "Test malloc/free basic free-list allocator", test_malloc_free, true) +DEFINE_TESTQ(XMALLOC0001, "Test basic free-list allocator: xmalloc", test_malloc_free, true) #endif +static int +test_minikv(env_t env) +{ + /* Ensure allocator is initialised for minikv */ + xmalloc_init(env); + + int rc = minikv_init(env); + test_eq(rc, 0); + + /* allocate some values via the test allocator */ + int *v1 = xmalloc(sizeof(int)); + int *v2 = xmalloc(sizeof(int)); + int *v3 = xmalloc(sizeof(int)); + test_assert(v1 && v2 && v3); + *v1 = 11; + *v2 = 22; + *v3 = 33; + + test_eq(minikv_set("one", v1), 0); + test_eq(minikv_set("two", v2), 0); + test_eq(minikv_set("three", v3), 0); + + int *r = minikv_get("one"); + test_assert(r != NULL); + test_eq(*r, 11); + + r = minikv_get("two"); + test_assert(r != NULL); + test_eq(*r, 22); + + /* Replace an existing key's value and verify replacement */ + int *v2b = xmalloc(sizeof(int)); + test_assert(v2b); + *v2b = 99; + test_eq(minikv_set("two", v2b), 0); + r = minikv_get("two"); + test_assert(r != NULL); + test_eq(*r, 99); + + /* Delete a key and ensure it's gone */ + test_eq(minikv_delete("one"), 0); + test_assert(minikv_get("one") == NULL); + + /* Cleanup: remove remaining keys and free allocated values */ + test_eq(minikv_delete("two"), 0); + test_eq(minikv_delete("three"), 0); + + xfree(v1); + xfree(v2); + xfree(v3); + xfree(v2b); + + minikv_destroy(); + + return sel4test_get_result(); +} +DEFINE_TESTQ(MINIKV0001, "Test minimal key-value store: minikv", test_minikv, true) + #ifdef CONFIG_ARCH_IA32 static int test_dirty_accessed_bits(env_t env) -- Gitee From fc4b8a2203e8454fe7f24d219a8e2d1688104f1b Mon Sep 17 00:00:00 2001 From: Flower Black Date: Mon, 1 Dec 2025 23:26:12 +0800 Subject: [PATCH 005/133] export compile_commands.json on init. --- projects/sel4test/easy-settings.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/projects/sel4test/easy-settings.cmake b/projects/sel4test/easy-settings.cmake index 1aabca1..43619e2 100755 --- a/projects/sel4test/easy-settings.cmake +++ b/projects/sel4test/easy-settings.cmake @@ -37,3 +37,7 @@ set(LibSel4TestPrinterHaltOnTestFailure OFF CACHE BOOL "Halt on the first test f set(KernelRootCNodeSizeBits "16" CACHE STRING "Root CNode size bits (default 13)") mark_as_advanced(CLEAR LibSel4TestPrinterRegex LibSel4TestPrinterHaltOnTestFailure) + + +# Let CMake export compile_commands.json for clangd. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -- Gitee From f2507e7eae23d80a7840988462379c4bcfb5421f Mon Sep 17 00:00:00 2001 From: Flower Black Date: Mon, 1 Dec 2025 23:33:46 +0800 Subject: [PATCH 006/133] ignore /.cache/ --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1812c34..8468763 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,6 @@ build-*/ .Spotlight-V100 .Trashes ehthumbs.db -Thumbs.db \ No newline at end of file +Thumbs.db + +.cache/ -- Gitee From 3887c8aa27b1bae41c5762d9afe6de8a36011d51 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Tue, 2 Dec 2025 14:31:46 +0800 Subject: [PATCH 007/133] update readme for sel4libs --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9259de4..d5ebdca 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,18 @@ -# This repo for user-level function developement on seL4test +# User-level functions for seL4test +Developing libs that run on seL4test environment, that provides: + +- Memory allocation function +- Key-value store function +- File management function + +etc. + +## Intro + +We currently develope with the assumption of aarch64 environment, with hvisor and linux as its root system. + +## Run and test Quick start on Qemu: ``` -- Gitee From 21a0d7df64259b4a2dfb6b6547846e8a8585ad6a Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Tue, 2 Dec 2025 21:33:37 +0800 Subject: [PATCH 008/133] add mem alloc and kv functions --- .../sel4test/apps/hello-world/CMakeLists.txt | 7 + projects/sel4test/apps/hello-world/src/main.c | 10 +- .../apps/hello-world/src/mem_helpers.c | 181 ++++++++++++++++++ .../apps/hello-world/src/mem_helpers.h | 15 ++ .../sel4test/apps/hello-world/src/mem_test.c | 48 +++++ .../sel4test/apps/hello-world/src/mem_test.h | 4 + .../apps/sel4test-tests/src/malloc_stub.c | 8 +- 7 files changed, 269 insertions(+), 4 deletions(-) create mode 100644 projects/sel4test/apps/hello-world/src/mem_helpers.c create mode 100644 projects/sel4test/apps/hello-world/src/mem_helpers.h create mode 100644 projects/sel4test/apps/hello-world/src/mem_test.c create mode 100644 projects/sel4test/apps/hello-world/src/mem_test.h diff --git a/projects/sel4test/apps/hello-world/CMakeLists.txt b/projects/sel4test/apps/hello-world/CMakeLists.txt index 8defc04..49a90b1 100755 --- a/projects/sel4test/apps/hello-world/CMakeLists.txt +++ b/projects/sel4test/apps/hello-world/CMakeLists.txt @@ -22,6 +22,13 @@ sel4_libs_import_libraries() file(GLOB deps src/*.c) list(SORT deps) +# Ensure mem_helpers.c is always included (some CMake setups do not re-run +# globbing on new files). Append the specific file path and remove duplicates. +list(APPEND deps "${CMAKE_CURRENT_LIST_DIR}/src/mem_helpers.c") +list(APPEND deps "${CMAKE_CURRENT_LIST_DIR}/src/mem_test.c") +list(REMOVE_DUPLICATES deps) +list(SORT deps) + add_executable(hello-world EXCLUDE_FROM_ALL ${deps}) target_link_libraries(hello-world PUBLIC diff --git a/projects/sel4test/apps/hello-world/src/main.c b/projects/sel4test/apps/hello-world/src/main.c index 6cf7050..b4bc068 100755 --- a/projects/sel4test/apps/hello-world/src/main.c +++ b/projects/sel4test/apps/hello-world/src/main.c @@ -9,6 +9,10 @@ #include #include #include +#include + +/* memory helper implementations are in mem_helpers.c */ +#include "mem_helpers.h" void __plat_putchar(int c); static size_t write_buf(void *data, size_t count) @@ -69,7 +73,7 @@ int main(void) { data[0x3FFFFF] = 'd'; // 4MB - 1 = 0x400000 - 1 = 0x3FFFFF printf("DATA boundary test: OK\n"); // === 边界测试结束 === - printString((char*)(0x53f000)); + printString((char*)(vaddrs[1])); // test malloc free here void *ptr = malloc(1024); @@ -81,6 +85,10 @@ int main(void) { printf("Malloc failed\n"); } + /* Run memory tests (moved out of main to keep main.c minimal) */ + #include "mem_test.h" + run_mem_tests(); + while (1) { /* code */ diff --git a/projects/sel4test/apps/hello-world/src/mem_helpers.c b/projects/sel4test/apps/hello-world/src/mem_helpers.c new file mode 100644 index 0000000..14e8dd9 --- /dev/null +++ b/projects/sel4test/apps/hello-world/src/mem_helpers.c @@ -0,0 +1,181 @@ +/* Implementation of memory helpers used by hello-world. This is intentionally + * minimal and independent of sel4test-tests so the hello-world binary can + * link the helpers directly. */ + +#include "mem_helpers.h" +#include +#include + +/* Simple slab-backed allocator for small allocations and malloc for large ones */ +#include + +#define MAX_SMALL_ALLOC 256 +#define SLOTS_PER_CLASS 64 + +/* slab classes (must be sorted ascending) */ +static const size_t slab_sizes[] = {16, 32, 64, 128, 256}; +#define NSLAB (sizeof(slab_sizes) / sizeof(slab_sizes[0])) + +/* static slab storage: NSLAB classes x SLOTS_PER_CLASS slots x MAX_SMALL_ALLOC bytes */ +static uint8_t slab_storage[NSLAB][SLOTS_PER_CLASS][MAX_SMALL_ALLOC]; +/* per-class free stacks (indices) */ +static int16_t slab_free_stack_top[NSLAB]; +static int16_t slab_free_stack[NSLAB][SLOTS_PER_CLASS]; +static int slab_initted = 0; + +static void slab_init(void) +{ + if (slab_initted) return; + for (int c = 0; c < (int)NSLAB; c++) { + slab_free_stack_top[c] = 0; + for (int i = 0; i < SLOTS_PER_CLASS; i++) { + slab_free_stack[c][i] = SLOTS_PER_CLASS - 1 - i; /* fill with indices */ + } + } + slab_initted = 1; +} + +static int slab_class_for_size(size_t size) +{ + for (int i = 0; i < (int)NSLAB; i++) { + if (size <= slab_sizes[i]) return i; + } + return -1; +} + +void xmalloc_init(void *env) +{ + (void)env; + slab_init(); +} + +void *xmalloc(size_t size) +{ + if (size == 0) return NULL; + slab_init(); + + if (size <= MAX_SMALL_ALLOC) { + int cls = slab_class_for_size(size); + if (cls >= 0) { + if (slab_free_stack_top[cls] < SLOTS_PER_CLASS) { + int idx = slab_free_stack[cls][slab_free_stack_top[cls]++]; + return (void *)&slab_storage[cls][idx][0]; + } + /* no free slot in this class; fall through to malloc */ + } + } + + /* large allocation -> heap */ + return malloc(size); +} + +void xfree(void *ptr) +{ + if (!ptr) return; + slab_init(); + + /* check slab pools */ + for (int c = 0; c < (int)NSLAB; c++) { + uint8_t *base = (uint8_t *)&slab_storage[c][0][0]; + size_t class_block = SLOTS_PER_CLASS * MAX_SMALL_ALLOC; + if ((uint8_t *)ptr >= base && (uint8_t *)ptr < base + class_block) { + size_t offset = (uint8_t *)ptr - base; + int idx = offset / MAX_SMALL_ALLOC; + /* push back */ + if (slab_free_stack_top[c] > 0) { + slab_free_stack[c][--slab_free_stack_top[c]] = idx; + } else { + /* stack is full, shouldn't happen; leak */ + } + return; + } + } + + /* otherwise was allocated via malloc */ + free(ptr); +} + +/* tiny minikv implementation */ +#define HW_MINIKV_BUCKETS 64 +typedef struct hw_kv_node { + char *key; + void *value; + struct hw_kv_node *next; +} hw_kv_node_t; +static hw_kv_node_t *hw_buckets[HW_MINIKV_BUCKETS]; +static int hw_minikv_initted = 0; + +static unsigned long hw_hash_str(const char *str) +{ + unsigned long hash = 5381; + int c; + while ((c = (unsigned char)*str++)) + hash = ((hash << 5) + hash) + c; + return hash; +} + +int minikv_init(void *env) +{ + (void)env; + if (hw_minikv_initted) return 0; + for (int i = 0; i < HW_MINIKV_BUCKETS; i++) hw_buckets[i] = NULL; + hw_minikv_initted = 1; + return 0; +} + +int minikv_set(const char *key, void *value) +{ + if (!hw_minikv_initted || !key) return -1; + unsigned long h = hw_hash_str(key) % HW_MINIKV_BUCKETS; + hw_kv_node_t *n = hw_buckets[h]; + while (n) { + if (strcmp(n->key, key) == 0) { + n->value = value; + return 0; + } + n = n->next; + } + hw_kv_node_t *node = xmalloc(sizeof(*node)); + if (!node) return -1; + size_t klen = strlen(key) + 1; + node->key = xmalloc(klen); + if (!node->key) { xfree(node); return -1; } + memcpy(node->key, key, klen); + node->value = value; + node->next = hw_buckets[h]; + hw_buckets[h] = node; + return 0; +} + +void *minikv_get(const char *key) +{ + if (!hw_minikv_initted || !key) return NULL; + unsigned long h = hw_hash_str(key) % HW_MINIKV_BUCKETS; + hw_kv_node_t *n = hw_buckets[h]; + while (n) { + if (strcmp(n->key, key) == 0) return n->value; + n = n->next; + } + return NULL; +} + +int minikv_delete(const char *key) +{ + if (!hw_minikv_initted || !key) return -1; + unsigned long h = hw_hash_str(key) % HW_MINIKV_BUCKETS; + hw_kv_node_t *n = hw_buckets[h]; + hw_kv_node_t *prev = NULL; + while (n) { + if (strcmp(n->key, key) == 0) { + if (prev) prev->next = n->next; else hw_buckets[h] = n->next; + xfree(n->key); + xfree(n); + return 0; + } + prev = n; + n = n->next; + } + return -1; + + } + diff --git a/projects/sel4test/apps/hello-world/src/mem_helpers.h b/projects/sel4test/apps/hello-world/src/mem_helpers.h new file mode 100644 index 0000000..36d3bf2 --- /dev/null +++ b/projects/sel4test/apps/hello-world/src/mem_helpers.h @@ -0,0 +1,15 @@ +/* Simple memory helpers for hello-world: xmalloc/xfree and a tiny minikv + * Placed in a separate file so the implementations don't live in main.c. + */ +#pragma once + +#include + +void xmalloc_init(void *env); +void *xmalloc(size_t size); +void xfree(void *ptr); + +int minikv_init(void *env); +int minikv_set(const char *key, void *value); +void *minikv_get(const char *key); +int minikv_delete(const char *key); diff --git a/projects/sel4test/apps/hello-world/src/mem_test.c b/projects/sel4test/apps/hello-world/src/mem_test.c new file mode 100644 index 0000000..ebe595b --- /dev/null +++ b/projects/sel4test/apps/hello-world/src/mem_test.c @@ -0,0 +1,48 @@ +/* mem_test.c - exercises xmalloc/xfree and minikv from mem_helpers.c + * Keeps test logic out of main.c to minimize changes to main. */ + +#include +#include "mem_helpers.h" +#include "mem_test.h" + +void run_mem_tests(void) +{ + /* ----- Test xmalloc/xfree (slab-only) ----- */ + xmalloc_init(NULL); /* init slab-only mode */ + void *s1 = xmalloc(32); + if (s1) { + printf("xmalloc small (32) OK: %p\n", s1); + xfree(s1); + printf("xfree small OK\n"); + } else { + printf("xmalloc small failed\n"); + } + + void *s2 = xmalloc(4096); /* larger than slab classes -> heap */ + if (s2) { + printf("xmalloc large (4096) returned %p\n", s2); + xfree(s2); + } else { + printf("xmalloc large returned NULL (expected if no heap)\n"); + } + + /* ----- Test minikv using slab allocator ----- */ + if (minikv_init(NULL) == 0) { + const char *k = "hello"; + void *val = (void *)0x1234; + if (minikv_set(k, val) == 0) { + printf("minikv set OK\n"); + void *g = minikv_get(k); + printf("minikv get: %p\n", g); + if (minikv_delete(k) == 0) { + printf("minikv delete OK\n"); + } else { + printf("minikv delete failed\n"); + } + } else { + printf("minikv set failed\n"); + } + } else { + printf("minikv_init failed\n"); + } +} diff --git a/projects/sel4test/apps/hello-world/src/mem_test.h b/projects/sel4test/apps/hello-world/src/mem_test.h new file mode 100644 index 0000000..2ee043f --- /dev/null +++ b/projects/sel4test/apps/hello-world/src/mem_test.h @@ -0,0 +1,4 @@ +/* mem_test.h - small harness to exercise mem_helpers in hello-world */ +#pragma once + +void run_mem_tests(void); diff --git a/projects/sel4test/apps/sel4test-tests/src/malloc_stub.c b/projects/sel4test/apps/sel4test-tests/src/malloc_stub.c index 5fd9fb6..eacc431 100644 --- a/projects/sel4test/apps/sel4test-tests/src/malloc_stub.c +++ b/projects/sel4test/apps/sel4test-tests/src/malloc_stub.c @@ -42,7 +42,8 @@ static int alloc_initted = 0; /* Initialize allocator (must be called from test) */ void xmalloc_init(env_t env) { - if (alloc_initted || env == NULL) return; + if (alloc_initted) return; + /* allow slab-only mode when env == NULL (no page-backed allocations) */ alloc_env = env; /* initialise slab free stacks */ @@ -69,7 +70,8 @@ static int slab_class_for_size(size_t size) /* allocate pages via vspace_new_pages; record for free */ static void *alloc_pages(size_t num_pages) { - if (!alloc_initted) return NULL; + /* page allocation requires a valid env; return NULL if not available */ + if (!alloc_initted || !alloc_env) return NULL; if (num_pages == 0) return NULL; void *vaddr = vspace_new_pages(&alloc_env->vspace, seL4_AllRights, num_pages, seL4_PageBits); if (!vaddr) return NULL; @@ -84,7 +86,7 @@ static void *alloc_pages(size_t num_pages) /* free page-backed region (unmap + let vspace free the frames) */ static void free_pages(void *ptr) { - if (!alloc_initted || ptr == NULL) return; + if (!alloc_initted || !alloc_env || ptr == NULL) return; for (int i = 0; i < page_allocs_count; i++) { if (page_allocs[i].vaddr == ptr) { vspace_unmap_pages(&alloc_env->vspace, ptr, page_allocs[i].num_pages, seL4_PageBits, VSPACE_FREE); -- Gitee From b9aa8b2cbe662a6006c029ed37155e96a50ec0d3 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Tue, 2 Dec 2025 21:58:44 +0800 Subject: [PATCH 009/133] add basis for file system management --- .../sel4test/apps/hello-world/CMakeLists.txt | 1 + .../sel4test/apps/hello-world/src/immfs.c | 206 ++++++++++++++++++ .../sel4test/apps/hello-world/src/immfs.h | 21 ++ .../apps/hello-world/src/mem_helpers.c | 18 +- .../sel4test/apps/hello-world/src/mem_test.c | 41 ++++ 5 files changed, 279 insertions(+), 8 deletions(-) create mode 100644 projects/sel4test/apps/hello-world/src/immfs.c create mode 100644 projects/sel4test/apps/hello-world/src/immfs.h diff --git a/projects/sel4test/apps/hello-world/CMakeLists.txt b/projects/sel4test/apps/hello-world/CMakeLists.txt index 49a90b1..49549bc 100755 --- a/projects/sel4test/apps/hello-world/CMakeLists.txt +++ b/projects/sel4test/apps/hello-world/CMakeLists.txt @@ -26,6 +26,7 @@ list(SORT deps) # globbing on new files). Append the specific file path and remove duplicates. list(APPEND deps "${CMAKE_CURRENT_LIST_DIR}/src/mem_helpers.c") list(APPEND deps "${CMAKE_CURRENT_LIST_DIR}/src/mem_test.c") +list(APPEND deps "${CMAKE_CURRENT_LIST_DIR}/src/immfs.c") list(REMOVE_DUPLICATES deps) list(SORT deps) diff --git a/projects/sel4test/apps/hello-world/src/immfs.c b/projects/sel4test/apps/hello-world/src/immfs.c new file mode 100644 index 0000000..513f3e3 --- /dev/null +++ b/projects/sel4test/apps/hello-world/src/immfs.c @@ -0,0 +1,206 @@ +/* immfs.c - minimal in-memory file system for tests + * Supports: open, close, creat, unlink, read, write + */ + +#include "immfs.h" +#include +#include +#include +#include "mem_helpers.h" + +#define IMMFS_MAX_FILES 128 +#define IMMFS_MAX_FDS 64 +#define IMMFS_PATH_MAX 128 + +typedef struct immfs_file { + char path[IMMFS_PATH_MAX]; + unsigned char *data; + size_t size; + size_t capacity; + int in_use; +} immfs_file_t; + +typedef struct immfs_fd { + int file_idx; + size_t offset; + int flags; + int used; +} immfs_fd_t; + +static immfs_file_t *files = NULL; +static immfs_fd_t *fds = NULL; +static int initted = 0; + +void immfs_init(void) +{ + if (initted) return; + /* Ensure slab allocator is initialised so small allocations use slabs */ + xmalloc_init(NULL); + files = xmalloc(sizeof(immfs_file_t) * IMMFS_MAX_FILES); + if (!files) { errno = ENOMEM; return; } + fds = xmalloc(sizeof(immfs_fd_t) * IMMFS_MAX_FDS); + if (!fds) { xfree(files); files = NULL; errno = ENOMEM; return; } + memset(files, 0, sizeof(immfs_file_t) * IMMFS_MAX_FILES); + memset(fds, 0, sizeof(immfs_fd_t) * IMMFS_MAX_FDS); + initted = 1; +} + +/* helpers */ +static int find_file_index(const char *path) +{ + for (int i = 0; i < IMMFS_MAX_FILES; i++) { + if (files[i].in_use && strcmp(files[i].path, path) == 0) return i; + } + return -1; +} + +static int alloc_file_slot(const char *path) +{ + for (int i = 0; i < IMMFS_MAX_FILES; i++) { + if (!files[i].in_use) { + files[i].in_use = 1; + strncpy(files[i].path, path, IMMFS_PATH_MAX - 1); + files[i].path[IMMFS_PATH_MAX - 1] = '\0'; + files[i].data = NULL; + files[i].size = 0; + files[i].capacity = 0; + return i; + } + } + return -1; +} + +static int alloc_fd_slot(void) +{ + for (int i = 0; i < IMMFS_MAX_FDS; i++) { + if (!fds[i].used) { + fds[i].used = 1; + fds[i].file_idx = -1; + fds[i].offset = 0; + fds[i].flags = 0; + return i; + } + } + return -1; +} + +static void free_file_slot(int idx) +{ + if (files[idx].data) xfree(files[idx].data); + files[idx].data = NULL; + files[idx].size = 0; + files[idx].capacity = 0; + files[idx].in_use = 0; + files[idx].path[0] = '\0'; +} + +int immfs_creat(const char *path, int mode) +{ + (void)mode; + immfs_init(); + if (find_file_index(path) != -1) { + errno = EEXIST; + return -1; + } + int idx = alloc_file_slot(path); + if (idx < 0) { errno = ENOSPC; return -1; } + /* return a descriptor like POSIX creat: open for write */ + int fd = alloc_fd_slot(); + if (fd < 0) { free_file_slot(idx); errno = EMFILE; return -1; } + fds[fd].file_idx = idx; + fds[fd].offset = 0; + fds[fd].flags = O_WRONLY | O_CREAT | O_TRUNC; + return fd; +} + +int immfs_open(const char *path, int flags, int mode) +{ + (void)mode; + immfs_init(); + int idx = find_file_index(path); + if (idx == -1) { + if (flags & O_CREAT) { + idx = alloc_file_slot(path); + if (idx < 0) { errno = ENOSPC; return -1; } + } else { + errno = ENOENT; return -1; + } + } + /* truncation */ + if ((flags & O_TRUNC) && (flags & O_WRONLY || flags & O_RDWR)) { + if (files[idx].data) { xfree(files[idx].data); files[idx].data = NULL; } + files[idx].size = 0; files[idx].capacity = 0; + } + int fd = alloc_fd_slot(); + if (fd < 0) { errno = EMFILE; return -1; } + fds[fd].file_idx = idx; + fds[fd].offset = 0; + fds[fd].flags = flags; + return fd; +} + +int immfs_close(int fd) +{ + immfs_init(); + if (fd < 0 || fd >= IMMFS_MAX_FDS || !fds[fd].used) { errno = EBADF; return -1; } + fds[fd].used = 0; + fds[fd].file_idx = -1; + fds[fd].offset = 0; + fds[fd].flags = 0; + return 0; +} + +int immfs_unlink(const char *path) +{ + immfs_init(); + int idx = find_file_index(path); + if (idx == -1) { errno = ENOENT; return -1; } + /* ensure no FD refers to this file */ + for (int i = 0; i < IMMFS_MAX_FDS; i++) { + if (fds[i].used && fds[i].file_idx == idx) { + errno = EBUSY; return -1; + } + } + free_file_slot(idx); + return 0; +} + +ssize_t immfs_read(int fd, void *buf, size_t count) +{ + immfs_init(); + if (fd < 0 || fd >= IMMFS_MAX_FDS || !fds[fd].used) { errno = EBADF; return -1; } + int idx = fds[fd].file_idx; + if (idx < 0 || !files[idx].in_use) { errno = EBADF; return -1; } + if ((fds[fd].flags & O_WRONLY) && !(fds[fd].flags & O_RDWR)) { errno = EBADF; return -1; } + size_t avail = files[idx].size > fds[fd].offset ? files[idx].size - fds[fd].offset : 0; + size_t toread = count < avail ? count : avail; + if (toread > 0) memcpy(buf, files[idx].data + fds[fd].offset, toread); + fds[fd].offset += toread; + return (ssize_t)toread; +} + +ssize_t immfs_write(int fd, const void *buf, size_t count) +{ + immfs_init(); + if (fd < 0 || fd >= IMMFS_MAX_FDS || !fds[fd].used) { errno = EBADF; return -1; } + int idx = fds[fd].file_idx; + if (idx < 0 || !files[idx].in_use) { errno = EBADF; return -1; } + if ((fds[fd].flags & O_RDONLY) && !(fds[fd].flags & O_RDWR)) { errno = EBADF; return -1; } + size_t need = fds[fd].offset + count; + if (need > files[idx].capacity) { + size_t newcap = files[idx].capacity ? files[idx].capacity : 1024; + while (newcap < need) newcap *= 2; + unsigned char *newdata = xmalloc(newcap); + if (!newdata) { errno = ENOMEM; return -1; } + if (files[idx].data) { + memcpy(newdata, files[idx].data, files[idx].size); + xfree(files[idx].data); + } + files[idx].data = newdata; + files[idx].capacity = newcap; + } + memcpy(files[idx].data + fds[fd].offset, buf, count); + fds[fd].offset += count; + if (fds[fd].offset > files[idx].size) files[idx].size = fds[fd].offset; + return (ssize_t)count; +} diff --git a/projects/sel4test/apps/hello-world/src/immfs.h b/projects/sel4test/apps/hello-world/src/immfs.h new file mode 100644 index 0000000..dad2a1d --- /dev/null +++ b/projects/sel4test/apps/hello-world/src/immfs.h @@ -0,0 +1,21 @@ +/* Simple in-memory file system (immfs) + * Provides minimal POSIX-like APIs: open, close, creat, unlink, read, write. + * This is intended for tests and demos only. + */ +#pragma once + +#include +#include + +/* open flags (subset) */ +#include + +int immfs_open(const char *path, int flags, int mode); +int immfs_close(int fd); +int immfs_creat(const char *path, int mode); +int immfs_unlink(const char *path); +ssize_t immfs_read(int fd, void *buf, size_t count); +ssize_t immfs_write(int fd, const void *buf, size_t count); + +/* Optional: initialize immfs (called automatically on first use) */ +void immfs_init(void); diff --git a/projects/sel4test/apps/hello-world/src/mem_helpers.c b/projects/sel4test/apps/hello-world/src/mem_helpers.c index 14e8dd9..de901e1 100644 --- a/projects/sel4test/apps/hello-world/src/mem_helpers.c +++ b/projects/sel4test/apps/hello-world/src/mem_helpers.c @@ -27,9 +27,10 @@ static void slab_init(void) { if (slab_initted) return; for (int c = 0; c < (int)NSLAB; c++) { - slab_free_stack_top[c] = 0; + /* initialize as fully-free: top holds the count of free slots */ + slab_free_stack_top[c] = SLOTS_PER_CLASS; for (int i = 0; i < SLOTS_PER_CLASS; i++) { - slab_free_stack[c][i] = SLOTS_PER_CLASS - 1 - i; /* fill with indices */ + slab_free_stack[c][i] = i; /* indices 0..SLOTS_PER_CLASS-1 */ } } slab_initted = 1; @@ -57,8 +58,9 @@ void *xmalloc(size_t size) if (size <= MAX_SMALL_ALLOC) { int cls = slab_class_for_size(size); if (cls >= 0) { - if (slab_free_stack_top[cls] < SLOTS_PER_CLASS) { - int idx = slab_free_stack[cls][slab_free_stack_top[cls]++]; + /* if there's a free slot in this class, pop and return it */ + if (slab_free_stack_top[cls] > 0) { + int idx = slab_free_stack[cls][--slab_free_stack_top[cls]]; return (void *)&slab_storage[cls][idx][0]; } /* no free slot in this class; fall through to malloc */ @@ -81,11 +83,11 @@ void xfree(void *ptr) if ((uint8_t *)ptr >= base && (uint8_t *)ptr < base + class_block) { size_t offset = (uint8_t *)ptr - base; int idx = offset / MAX_SMALL_ALLOC; - /* push back */ - if (slab_free_stack_top[c] > 0) { - slab_free_stack[c][--slab_free_stack_top[c]] = idx; + /* push back: only if there is room in the free stack */ + if (slab_free_stack_top[c] < SLOTS_PER_CLASS) { + slab_free_stack[c][slab_free_stack_top[c]++] = idx; } else { - /* stack is full, shouldn't happen; leak */ + /* free-stack overflow: shouldn't happen; leak to avoid crash */ } return; } diff --git a/projects/sel4test/apps/hello-world/src/mem_test.c b/projects/sel4test/apps/hello-world/src/mem_test.c index ebe595b..b4e7440 100644 --- a/projects/sel4test/apps/hello-world/src/mem_test.c +++ b/projects/sel4test/apps/hello-world/src/mem_test.c @@ -4,6 +4,9 @@ #include #include "mem_helpers.h" #include "mem_test.h" +#include "immfs.h" +#include +#include void run_mem_tests(void) { @@ -45,4 +48,42 @@ void run_mem_tests(void) } else { printf("minikv_init failed\n"); } + + /* ----- Test immfs (in-memory filesystem) ----- */ + immfs_init(); + const char *path = "/tmp/hello"; + int fd = immfs_creat(path, 0644); + if (fd < 0) { + printf("immfs_creat failed: %d\n", errno); + } else { + const char *msg = "hello immfs"; + ssize_t w = immfs_write(fd, msg, strlen(msg)); + if (w != (ssize_t)strlen(msg)) { + printf("immfs_write failed wrote=%zd errno=%d\n", w, errno); + } else { + printf("immfs_write OK wrote=%zd\n", w); + } + immfs_close(fd); + + int rfd = immfs_open(path, O_RDONLY, 0); + if (rfd < 0) { + printf("immfs_open for read failed: %d\n", errno); + } else { + char buf[64]; + ssize_t r = immfs_read(rfd, buf, sizeof(buf)); + if (r > 0) { + buf[r] = '\0'; + printf("immfs_read OK read=%zd data=\"%s\"\n", r, buf); + } else { + printf("immfs_read failed r=%zd errno=%d\n", r, errno); + } + immfs_close(rfd); + } + + if (immfs_unlink(path) == 0) { + printf("immfs_unlink OK\n"); + } else { + printf("immfs_unlink failed errno=%d\n", errno); + } + } } -- Gitee From ee14a75826cdb0bb456db06cb9eda0e017fccb1f Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Tue, 2 Dec 2025 22:43:00 +0800 Subject: [PATCH 010/133] enrich testcase --- .../apps/hello-world/src/mem_helpers.c | 107 ++++++++++++---- .../sel4test/apps/hello-world/src/mem_test.c | 114 +++++++++++++----- 2 files changed, 166 insertions(+), 55 deletions(-) diff --git a/projects/sel4test/apps/hello-world/src/mem_helpers.c b/projects/sel4test/apps/hello-world/src/mem_helpers.c index de901e1..37c3daf 100644 --- a/projects/sel4test/apps/hello-world/src/mem_helpers.c +++ b/projects/sel4test/apps/hello-world/src/mem_helpers.c @@ -10,28 +10,66 @@ #include #define MAX_SMALL_ALLOC 256 -#define SLOTS_PER_CLASS 64 +#define SLOTS_PER_CLASS 1024 /* slab classes (must be sorted ascending) */ static const size_t slab_sizes[] = {16, 32, 64, 128, 256}; #define NSLAB (sizeof(slab_sizes) / sizeof(slab_sizes[0])) -/* static slab storage: NSLAB classes x SLOTS_PER_CLASS slots x MAX_SMALL_ALLOC bytes */ -static uint8_t slab_storage[NSLAB][SLOTS_PER_CLASS][MAX_SMALL_ALLOC]; -/* per-class free stacks (indices) */ -static int16_t slab_free_stack_top[NSLAB]; -static int16_t slab_free_stack[NSLAB][SLOTS_PER_CLASS]; +/* per-class slab storage: allocate exactly the slot size for each class to avoid waste. + * Storage and bookkeeping are allocated at runtime so the pool can be resized. + */ +static uint8_t *slab_base_ptrs[NSLAB] = { NULL }; +static size_t slab_slots[NSLAB] = { 0 }; +/* per-class free bitmaps */ +static uint32_t *slab_bitmap_ptr[NSLAB] = { NULL }; +static size_t slab_bitmap_words[NSLAB] = { 0 }; +static size_t slab_free_count[NSLAB] = { 0 }; static int slab_initted = 0; +/* default slots per class; can be grown at runtime */ +#define DEFAULT_SLOTS_PER_CLASS 1024 + static void slab_init(void) { if (slab_initted) return; for (int c = 0; c < (int)NSLAB; c++) { - /* initialize as fully-free: top holds the count of free slots */ - slab_free_stack_top[c] = SLOTS_PER_CLASS; - for (int i = 0; i < SLOTS_PER_CLASS; i++) { - slab_free_stack[c][i] = i; /* indices 0..SLOTS_PER_CLASS-1 */ + size_t item_size = slab_sizes[c]; + size_t slots = DEFAULT_SLOTS_PER_CLASS; + /* allocate storage using malloc (can't use xmalloc here) */ + uint8_t *base = malloc(item_size * slots); + if (!base) { + slab_base_ptrs[c] = NULL; + slab_slots[c] = 0; + slab_bitmap_ptr[c] = NULL; + slab_bitmap_words[c] = 0; + slab_free_count[c] = 0; + continue; + } + slab_base_ptrs[c] = base; + slab_slots[c] = slots; + /* bitmap: one bit per slot */ + size_t words = (slots + 31) / 32; + uint32_t *bitmap = calloc(words, sizeof(uint32_t)); + if (!bitmap) { + free(base); + slab_base_ptrs[c] = NULL; + slab_slots[c] = 0; + slab_bitmap_ptr[c] = NULL; + slab_bitmap_words[c] = 0; + slab_free_count[c] = 0; + continue; + } + /* set all valid bits to 1 (free) */ + for (size_t w = 0; w < words; w++) bitmap[w] = 0xFFFFFFFFu; + /* clear bits beyond slots */ + if (slots % 32) { + uint32_t last_mask = (1u << (slots % 32)) - 1u; + bitmap[words - 1] = last_mask; } + slab_bitmap_ptr[c] = bitmap; + slab_bitmap_words[c] = words; + slab_free_count[c] = slots; } slab_initted = 1; } @@ -57,14 +95,25 @@ void *xmalloc(size_t size) if (size <= MAX_SMALL_ALLOC) { int cls = slab_class_for_size(size); - if (cls >= 0) { - /* if there's a free slot in this class, pop and return it */ - if (slab_free_stack_top[cls] > 0) { - int idx = slab_free_stack[cls][--slab_free_stack_top[cls]]; - return (void *)&slab_storage[cls][idx][0]; + if (cls >= 0 && slab_bitmap_ptr[cls] && slab_free_count[cls] > 0) { + /* find first set bit in bitmap */ + uint32_t *bitmap = slab_bitmap_ptr[cls]; + size_t words = slab_bitmap_words[cls]; + for (size_t w = 0; w < words; w++) { + uint32_t word = bitmap[w]; + if (word == 0) continue; + /* find least-significant set bit */ + unsigned bit = (unsigned)__builtin_ctz(word); + size_t idx = w * 32 + bit; + if (idx >= slab_slots[cls]) continue; + /* clear bit */ + bitmap[w] = word & ~(1u << bit); + slab_free_count[cls]--; + size_t item_size = slab_sizes[cls]; + return (void *)(slab_base_ptrs[cls] + idx * item_size); } - /* no free slot in this class; fall through to malloc */ } + /* no free slot in this class; fall through to malloc */ } /* large allocation -> heap */ @@ -78,16 +127,24 @@ void xfree(void *ptr) /* check slab pools */ for (int c = 0; c < (int)NSLAB; c++) { - uint8_t *base = (uint8_t *)&slab_storage[c][0][0]; - size_t class_block = SLOTS_PER_CLASS * MAX_SMALL_ALLOC; - if ((uint8_t *)ptr >= base && (uint8_t *)ptr < base + class_block) { + uint8_t *base = slab_base_ptrs[c]; + size_t item_size = slab_sizes[c]; + size_t class_block = (size_t)slab_slots[c] * item_size; + if (base && (uint8_t *)ptr >= base && (uint8_t *)ptr < base + class_block) { size_t offset = (uint8_t *)ptr - base; - int idx = offset / MAX_SMALL_ALLOC; - /* push back: only if there is room in the free stack */ - if (slab_free_stack_top[c] < SLOTS_PER_CLASS) { - slab_free_stack[c][slab_free_stack_top[c]++] = idx; - } else { - /* free-stack overflow: shouldn't happen; leak to avoid crash */ + size_t idx = offset / item_size; + /* set bit if it was not already free */ + if (slab_bitmap_ptr[c]) { + size_t w = idx / 32; + unsigned b = (unsigned)(idx % 32); + uint32_t mask = 1u << b; + if ((slab_bitmap_ptr[c][w] & mask) == 0) { + /* currently allocated -> mark free */ + slab_bitmap_ptr[c][w] |= mask; + slab_free_count[c]++; + } else { + /* double free or already free: ignore */ + } } return; } diff --git a/projects/sel4test/apps/hello-world/src/mem_test.c b/projects/sel4test/apps/hello-world/src/mem_test.c index b4e7440..7559ebe 100644 --- a/projects/sel4test/apps/hello-world/src/mem_test.c +++ b/projects/sel4test/apps/hello-world/src/mem_test.c @@ -5,51 +5,105 @@ #include "mem_helpers.h" #include "mem_test.h" #include "immfs.h" + #include #include +#include +#include void run_mem_tests(void) { - /* ----- Test xmalloc/xfree (slab-only) ----- */ - xmalloc_init(NULL); /* init slab-only mode */ - void *s1 = xmalloc(32); - if (s1) { - printf("xmalloc small (32) OK: %p\n", s1); - xfree(s1); - printf("xfree small OK\n"); - } else { - printf("xmalloc small failed\n"); - } + /* ----- Stress tests for slab allocator and minikv ----- */ + printf("\n=== Stress tests: xmalloc and minikv stress ===\n"); + xmalloc_init(NULL); - void *s2 = xmalloc(4096); /* larger than slab classes -> heap */ - if (s2) { - printf("xmalloc large (4096) returned %p\n", s2); - xfree(s2); + const size_t ALLOC_STRESS = 6000; /* try to allocate more than default slab pool */ + void **ptrs = malloc(sizeof(void*) * ALLOC_STRESS); + if (!ptrs) { + printf("alloc stress: cannot malloc ptr array\n"); } else { - printf("xmalloc large returned NULL (expected if no heap)\n"); + size_t success = 0; + for (size_t i = 0; i < ALLOC_STRESS; i++) { + /* alternate sizes: exact class sizes and off-by-one */ + size_t sz; + switch (i % 5) { + case 0: sz = 16; break; + case 1: sz = 17; break; + case 2: sz = 64; break; + case 3: sz = 65; break; + default: sz = 200; break; + } + ptrs[i] = xmalloc(sz); + if (ptrs[i]) success++; + } + printf("alloc stress: requested=%zu succeeded=%zu\n", ALLOC_STRESS, success); + + /* free half of them, then allocate again to test reuse */ + for (size_t i = 0; i < ALLOC_STRESS; i += 2) { + xfree(ptrs[i]); + ptrs[i] = NULL; + } + size_t success2 = 0; + for (size_t i = 0; i < ALLOC_STRESS; i++) { + if (!ptrs[i]) { + ptrs[i] = xmalloc(32); + if (ptrs[i]) success2++; + } + } + printf("alloc reuse: reallocated=%zu\n", success2); + + /* double-free some pointers to ensure allocator tolerates it */ + if (ALLOC_STRESS >= 4) { + xfree(ptrs[1]); + xfree(ptrs[1]); /* double free should be tolerated (ignored) */ + printf("double-free test executed (no crash expected)\n"); + } + + for (size_t i = 0; i < ALLOC_STRESS; i++) if (ptrs[i]) xfree(ptrs[i]); + free(ptrs); } - /* ----- Test minikv using slab allocator ----- */ + /* minikv stress: many inserts, updates, deletes */ if (minikv_init(NULL) == 0) { - const char *k = "hello"; - void *val = (void *)0x1234; - if (minikv_set(k, val) == 0) { - printf("minikv set OK\n"); - void *g = minikv_get(k); - printf("minikv get: %p\n", g); - if (minikv_delete(k) == 0) { - printf("minikv delete OK\n"); - } else { - printf("minikv delete failed\n"); - } - } else { - printf("minikv set failed\n"); + const int N = 2000; + char key[48]; + int errors = 0; + for (int i = 0; i < N; i++) { + snprintf(key, sizeof(key), "key-%04d", i); + if (minikv_set(key, (void*)(intptr_t)i) != 0) { errors++; if (i < 10) printf("minikv_set failed for %s\n", key); } } + printf("minikv stress: inserted=%d errors=%d\n", N, errors); + /* verify some random entries and update them */ + int mismatches = 0; + for (int i = 0; i < N; i += 37) { + snprintf(key, sizeof(key), "key-%04d", i); + void *v = minikv_get(key); + if ((intptr_t)v != i) { mismatches++; if (mismatches < 5) printf("minikv mismatch for %s got=%p expected=%d\n", key, v, i); } + /* update */ + if (minikv_set(key, (void*)(intptr_t)(i + 1)) != 0) printf("minikv update failed for %s\n", key); + void *v2 = minikv_get(key); + if ((intptr_t)v2 != i + 1) { printf("minikv update mismatch for %s got=%p expected=%d\n", key, v2, i+1); } + } + printf("minikv verify: mismatches=%d\n", mismatches); + + /* delete half */ + int dels = 0; + for (int i = 0; i < N; i += 2) { + snprintf(key, sizeof(key), "key-%04d", i); + if (minikv_delete(key) == 0) dels++; else if (i < 10) printf("minikv_delete failed for %s\n", key); + } + printf("minikv deleted=%d\n", dels); + + /* check non-existent */ + if (minikv_get("no-such-key") == NULL) printf("minikv correctly returns NULL for missing key\n"); + if (minikv_set(NULL, (void*)1) != 0) printf("minikv rejects NULL key (as expected)\n"); + } else { - printf("minikv_init failed\n"); + printf("minikv_init failed for stress tests\n"); } /* ----- Test immfs (in-memory filesystem) ----- */ + printf("\n=== immfs tests ===\n"); immfs_init(); const char *path = "/tmp/hello"; int fd = immfs_creat(path, 0644); -- Gitee From 36e76780cf3b2dd3ecc3f3b7f90479b4d65548ea Mon Sep 17 00:00:00 2001 From: Flower Black Date: Wed, 3 Dec 2025 19:55:14 +0800 Subject: [PATCH 011/133] CMakeLists.txt for monkey-mnemosyne --- projects/sel4test/CMakeLists.txt | 4 +- .../apps/monkey-mnemosyne/CMakeLists.txt | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt diff --git a/projects/sel4test/CMakeLists.txt b/projects/sel4test/CMakeLists.txt index 328247e..c2564a8 100755 --- a/projects/sel4test/CMakeLists.txt +++ b/projects/sel4test/CMakeLists.txt @@ -30,10 +30,12 @@ elfloader_import_project() # Allow choosing between different applications set(Sel4testApp "sel4test-driver" CACHE STRING "Application to build (sel4test-driver or hello-world)") -set_property(CACHE Sel4testApp PROPERTY STRINGS sel4test-driver hello-world) +set_property(CACHE Sel4testApp PROPERTY STRINGS sel4test-driver hello-world monkey-mnemosyne) if(Sel4testApp STREQUAL "hello-world") add_subdirectory(apps/hello-world) +elseif(Sel4testApp STREQUAL "monkey-mnemosyne") + add_subdirectory(apps/monkey-mnemosyne) else() add_subdirectory(apps/sel4test-driver) endif() diff --git a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt new file mode 100644 index 0000000..683825a --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.16.0) + +project(monkey-mnemosyne C) + +find_package(musllibc REQUIRED) +find_package(util_libs REQUIRED) +find_package(seL4_libs REQUIRED) + +# Setup build environment +musllibc_setup_build_environment_with_sel4runtime() +sel4_import_libsel4() +util_libs_import_libraries() +sel4_libs_import_libraries() + +# Build monkey mnemosyne application +file(GLOB deps src/*.c*) +list(SORT deps) +list(REMOVE_DUPLICATES deps) + +add_executable(monkey-mnemosyne EXCLUDE_FROM_ALL ${deps}) +target_link_libraries(monkey-mnemosyne + PUBLIC + sel4_autoconf + muslc + sel4 + sel4runtime + sel4utils + sel4platsupport + sel4muslcsys + sel4allocman + PRIVATE + utils +) + +# Set executable properties +target_compile_options(monkey-mnemosyne PRIVATE -Werror -g) +set_property(TARGET monkey-mnemosyne PROPERTY C_STANDARD 99) + +# Set this image as the rootserver +include(rootserver) +DeclareRootserver(monkey-mnemosyne) -- Gitee From 67f6b87e9a098dee1571b4688f66139ca1404145 Mon Sep 17 00:00:00 2001 From: Flower Black Date: Wed, 3 Dec 2025 21:09:55 +0800 Subject: [PATCH 012/133] adl. --- .../apps/monkey-mnemosyne/CMakeLists.txt | 25 +- .../apps/monkey-mnemosyne/src/adl/Allocator.h | 142 ++ .../apps/monkey-mnemosyne/src/adl/AsciiChar.h | 64 + .../apps/monkey-mnemosyne/src/adl/TString.cc | 826 ++++++++++ .../apps/monkey-mnemosyne/src/adl/TString.h | 116 ++ .../apps/monkey-mnemosyne/src/adl/arpa/inet.h | 61 + .../src/adl/collections/ArrayList.hpp | 357 +++++ .../src/adl/collections/HashMap.hpp | 22 + .../src/adl/collections/LinkedList.cpp | 315 ++++ .../src/adl/collections/LinkedList.h | 138 ++ .../src/adl/collections/RedBlackTree.hpp | 1407 +++++++++++++++++ .../apps/monkey-mnemosyne/src/adl/config.cpp | 48 + .../apps/monkey-mnemosyne/src/adl/config.h | 24 + .../apps/monkey-mnemosyne/src/adl/endian.h | 144 ++ .../monkey-mnemosyne/src/adl/recursive_mutex | 84 + .../apps/monkey-mnemosyne/src/adl/stdarg.h | 28 + .../apps/monkey-mnemosyne/src/adl/stdbool.h | 19 + .../apps/monkey-mnemosyne/src/adl/stddef.h | 22 + .../apps/monkey-mnemosyne/src/adl/stdint.h | 81 + .../apps/monkey-mnemosyne/src/adl/string.cpp | 366 +++++ .../apps/monkey-mnemosyne/src/adl/string.h | 44 + .../apps/monkey-mnemosyne/src/adl/sys/types.h | 61 + .../apps/monkey-mnemosyne/src/adl/type_traits | 27 + .../apps/monkey-mnemosyne/src/adl/utility | 43 + .../apps/monkey-mnemosyne/src/adl/wl_list.cpp | 97 ++ .../apps/monkey-mnemosyne/src/adl/wl_list.h | 324 ++++ 26 files changed, 4883 insertions(+), 2 deletions(-) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/Allocator.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/AsciiChar.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/TString.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/TString.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/arpa/inet.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/ArrayList.hpp create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/HashMap.hpp create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/LinkedList.cpp create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/LinkedList.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/config.cpp create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/config.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/endian.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/recursive_mutex create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/stdarg.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/stdbool.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/stddef.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/stdint.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/string.cpp create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/string.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/sys/types.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/type_traits create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/utility create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/wl_list.cpp create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/adl/wl_list.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt index 683825a..75e3008 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt +++ b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.0) -project(monkey-mnemosyne C) +project(monkey-mnemosyne) find_package(musllibc REQUIRED) find_package(util_libs REQUIRED) @@ -13,7 +13,7 @@ util_libs_import_libraries() sel4_libs_import_libraries() # Build monkey mnemosyne application -file(GLOB deps src/*.c*) +file(GLOB_RECURSE deps src/*.c*) list(SORT deps) list(REMOVE_DUPLICATES deps) @@ -32,6 +32,27 @@ target_link_libraries(monkey-mnemosyne utils ) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) + +# GCC(g++) 编译选项。 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostartfiles") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-pic") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-pie") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -no-pie") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-stack-protector") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-use-cxa-atexit") +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1") # 适当优化。 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-sized-deallocation") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") + + # Set executable properties target_compile_options(monkey-mnemosyne PRIVATE -Werror -g) set_property(TARGET monkey-mnemosyne PROPERTY C_STANDARD 99) diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/Allocator.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/Allocator.h new file mode 100644 index 0000000..39f96a3 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/Allocator.h @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * 分配器。 + * + * 创建于 2023年7月2日 上海市嘉定区安亭镇 + */ + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +// modified for Amkos + +#pragma once + +#include "./sys/types.h" + +namespace adl { struct Allocator; }; + + +// DO NOT call this outside adl::Allocator! +inline void* operator new(adl::size_t, adl::Allocator* addr) { + return addr; +} + + +namespace adl { + + +struct Allocator { +public: + typedef void* (* AllocFunc) (size_t, void*); + typedef void (* FreeFunc) (void*, size_t, void*); + +protected: + void* data = nullptr; + AllocFunc _alloc = nullptr; + FreeFunc _free = nullptr; + + struct Header { + size_t size; + size_t count; + }; + + +public: + + template + inline T* allocNoConstruct(size_t count = 1, size_t objSize = sizeof(T)) { + if (count == 0) + return nullptr; + + uintptr_t addr = (uintptr_t) this->_alloc(objSize * count + sizeof(Header), data); + + if (!addr) + return nullptr; + + auto header = (Header*) addr; + header->size = objSize; + header->count = count; + + auto objs = (T*) (addr + sizeof(Header)); + + return objs; + } + + + template + inline T* alloc(size_t count = 1, bool construct = true, size_t objSize = sizeof(T)) { + + auto objs = allocNoConstruct(count, objSize); + if (!objs) + return nullptr; + + if (construct) { + for (size_t i = 0; i < count; i++) + // redirect to fake allocation that doesn't give any extra memory. + new ((Allocator*) (objs + i)) T; + } + + return objs; + } + + + inline void free(void* addr) { + auto header = (Header*) (uintptr_t(addr) - sizeof(Header)); + this->_free(header, header->count * header->size + sizeof(Header), data); + } + + + template + inline void free(T* addr, bool destruct = true) { + auto header = (Header*) (uintptr_t(addr) - sizeof(Header)); + + if (destruct) { + size_t i = header->count - 1; + do { + uintptr_t objAddr = uintptr_t(addr) + i * header->size; + ((T*) objAddr)->~T(); + } while (i--); + } + + this->_free(header, header->count * header->size + sizeof(Header), data); + } + + + inline bool ready() { return !!_alloc && !!_free; } + inline bool notReady() { return !ready(); } + + inline void bindAlloc(AllocFunc func) { this->_alloc = func; }; + inline void bindFree(FreeFunc func) { this->_free = func; }; + inline void bindData(void* data) { this->data = data; }; + + + void init(AllocFunc alloc, FreeFunc free, void* data = nullptr) { + bindAlloc(alloc); + bindFree(free); + bindData(data); + } + + struct InitParams { + AllocFunc alloc; + FreeFunc free; + void* data = nullptr; + }; + + Allocator() = default; + + Allocator(AllocFunc alloc, FreeFunc free, void* data = nullptr) { init(alloc, free, data); } + Allocator(const InitParams& x) { init(x.alloc, x.free, x.data); } + + inline void init(InitParams x) { init(x.alloc, x.free, x.data); } + + inline Allocator operator = (InitParams x) { init(x); return *this; } + +}; + + + + +} // namespace adl + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/AsciiChar.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/AsciiChar.h new file mode 100644 index 0000000..3bf1058 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/AsciiChar.h @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * ASCII 特殊字符定义。 + * 创建于 2022年7月12日。 + * + * 参考: + * https://www.asciitable.com/ + */ + + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +#pragma once +#include "./sys/types.h" + +namespace adl { + +class AsciiChar { +public: + static const uint8_t NUL = 0; + static const uint8_t SOH = 1; + static const uint8_t STX = 2; + static const uint8_t ETX = 3; + static const uint8_t EOT = 4; + static const uint8_t ENQ = 5; + static const uint8_t ACK = 6; + static const uint8_t BEL = 7; + static const uint8_t BS = 8; + static const uint8_t TAB = 9; + + /** NL line feed, new line (n). */ + static const uint8_t LF = 10; + static const uint8_t VT = 11; + static const uint8_t FF = 12; + + /** carriage return (r). */ + static const uint8_t CR = 13; + static const uint8_t SO = 14; + static const uint8_t SI = 15; + static const uint8_t DLE = 16; + static const uint8_t DC1 = 17; + static const uint8_t DC2 = 18; + static const uint8_t DC3 = 19; + static const uint8_t DC4 = 20; + static const uint8_t NAK = 21; + static const uint8_t SYN = 22; + static const uint8_t ETB = 23; + static const uint8_t CAN = 24; + static const uint8_t EM = 25; + static const uint8_t SUB = 26; + static const uint8_t ESC = 27; + static const uint8_t FS = 28; + static const uint8_t GS = 29; + static const uint8_t RS = 30; + static const uint8_t US = 31; + + /** 可见字符起始位置。含空格。 */ + static const uint8_t START_OF_VISIBLE_CHARS = 32; + static const uint8_t END_OF_VISIBLE_CHARS = 126; +}; + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/TString.cc b/projects/sel4test/apps/monkey-mnemosyne/src/adl/TString.cc new file mode 100644 index 0000000..97a334b --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/TString.cc @@ -0,0 +1,826 @@ +/* 2051565 GTY Tongji CS */ +// Ported from TJ OOP HW. Modified for Amkos. + +#define _CRT_SECURE_NO_WARNINGS + + +#include + +#include +#include +#include + +#include + +using namespace std; + + +static void __die() { + printf("[FATAL] Panic. "); + printf(__FILE__); + printf("\n"); + while (1) + ; +} + + +namespace adl { + + +TString::TString(adl::Allocator* alloc) +{ + this->allocator = alloc; + content = nullptr; + len = 0; +} + +TString::TString(const char* str, adl::Allocator* alloc) +{ + this->allocator = alloc; + + if (str == nullptr || str[0] == '\0') { + content = nullptr; + len = 0; + } + else { + len = adl::strlen(str); + + content = alloc->alloc(len + 1, false); + + if (content == nullptr) { + __die(); + } + + + adl::strcpy(content, str); + } +} + +#if defined(__GNUC__) + +#else +TString::TString(const int x) +{ + if (x == 0) { // x == NULL + len = 0; + content = nullptr; + } + else { + len = 0; + content = nullptr; + } +} +#endif + +TString::TString(const TString& str, adl::Allocator* alloc) +{ + this->allocator = alloc; + + if (str.length() == 0) { + content = nullptr; + len = 0; + } + else { + len = str.length(); + content = alloc->alloc(len + 1, false); + if (content == nullptr) { + __die(); + } + + adl::strcpy(content, str.c_str()); + } +} + +TString::~TString() +{ + freeUp(); +} + +void TString::freeUp() +{ + if (content != nullptr) { + freeUpContent(); + len = 0; + } +} + + +void TString::clear() { + freeUp(); +} + + +void TString::freeUpContent() +{ + if (content != nullptr) { + allocator->free(content); + content = nullptr; + } +} + +size_t TString::length() const +{ + return len; +} + +const char* TString::c_str() const +{ + return content; +} + +TString& TString::operator=(const TString& str) +{ + if (&str == this) { + return *this; + } + + freeUp(); + + if (str.length() == 0) { + return *this; + } + else { + content = allocator->alloc(str.length() + 1, false); + if (content == nullptr) { + __die(); + } + + adl::strcpy(content, str.c_str()); + len = str.length(); + return *this; + } +} + +TString& TString::operator=(const char* str) +{ + if (str == this->content) { + return *this; + } + + freeUp(); + + if (str == nullptr || str[0] == '\0') { + return *this; + } + else { + len = strlen(str); + content = allocator->alloc(len + 1, false); + if (content == nullptr) { + __die(); + } + + strcpy(content, str); + return *this; + } +} + +const TString TString::operator+(const TString& str) const +{ + TString ret; + + ret.len = this->len + str.len; + + if (ret.len == 0) { + return ret; + } + + ret.content = allocator->alloc(ret.len + 1, false); + if (ret.content == nullptr) { + __die(); + } + + + if (this->content != nullptr) { + adl::memcpy(ret.content, this->content, this->len * sizeof(char)); + } + if (str.c_str() != nullptr) { + adl::memcpy(ret.content + this->len, str.c_str(), str.length() * sizeof(char)); + } + ret.content[ret.len] = '\0'; + + return ret; +} + +const TString TString::operator+(const char* str) const +{ + TString ret; + + auto lengthOfStr = (str == nullptr ? 0 : adl::strlen(str)); + + ret.len = this->len + lengthOfStr; + + if (ret.len == 0) { + return ret; + } + + ret.content = allocator->alloc(ret.len + 1, false); + if (ret.content == nullptr) { + __die(); + } + + if (this->content != nullptr) { + adl::memcpy(ret.content, this->content, this->len * sizeof(char)); + } + + adl::memcpy(ret.content + this->len, str, lengthOfStr * sizeof(char)); + ret.content[ret.len] = '\0'; + + return ret; +} + +const TString operator+(const char* strA, const TString& strB) +{ + TString ret {strB.allocator}; + + auto lengthOfStrA = (strA == nullptr ? 0 : adl::strlen(strA)); + ret.len = lengthOfStrA + strB.length(); + + if (ret.len == 0) { + return ret; + } + + ret.content = ret.allocator->alloc(ret.len + 1, false); + if (ret.content == nullptr) { + __die(); + } + + adl::memcpy(ret.content, strA, lengthOfStrA * sizeof(char)); + adl::memcpy(ret.content + lengthOfStrA, strB.c_str(), strB.length() * sizeof(char)); + ret.content[ret.len] = '\0'; + + return ret; +} + +const TString TString::operator+(const char c) const +{ + char tmp[2] = { c, '\0' }; + return *this + tmp; +} + +const TString TString::operator!() const +{ + TString ret = *this; + if (ret.length() >= 2) { + char c; + for (size_t i = 0; i < ret.length() / 2; i++) { + c = ret[i]; + ret[i] = ret[ret.length() - 1 - i]; + ret[ret.length() - 1 - i] = c; + } + } + + return ret; + +} + +const TString TString::operator-(const TString& str) const +{ + if (str.len == 0 || this->len == 0) { + return *this; + } + else { + char* occurenceLocation = adl::strstr(content, str.content); + if (occurenceLocation == nullptr) { + return *this; + } + else if (len == str.length()) { + return TString(this->allocator); + } + else { + char* p = allocator->alloc(len - str.length() + 1, false); + if (p == nullptr) { + __die(); + } + TString ret {this->allocator}; + ret.len = len - str.length(); + ret.content = p; + + adl::memcpy(p, content, adl::uint64_t(occurenceLocation - content) * sizeof(char)); + adl::memcpy( + p + (occurenceLocation - content), + occurenceLocation + str.length(), + (len - adl::uint64_t(occurenceLocation - content) - str.length()) * sizeof(char) + ); + p[ret.len] = '\0'; + + return ret; + } + } +} + +const TString TString::operator-(const char* str) const +{ + if (str == nullptr || str[0] == '\0' || this->len == 0) { + return *this; + } + else { + auto lengthOfStr = adl::strlen(str); + + char* occurenceLocation = adl::strstr(content, str); + if (occurenceLocation == nullptr) { + return *this; + } + else if (len == lengthOfStr) { + return TString(this->allocator); + } + else { + char* p = allocator->alloc(len - lengthOfStr + 1); + if (p == nullptr) { + __die(); + } + TString ret; + ret.len = len - lengthOfStr; + ret.content = p; + + adl::memcpy(p, content, adl::uint64_t(occurenceLocation - content) * sizeof(char)); + adl::memcpy( + p + (occurenceLocation - content), + occurenceLocation + lengthOfStr, + (len - adl::uint64_t(occurenceLocation - content) - lengthOfStr) * sizeof(char) + ); + p[ret.len] = '\0'; + + return ret; + } + } +} + +const TString TString::operator - (const char c) const +{ + if (c == '\0' || len == 0) { + return *this; + } + else { + for (size_t i = 0; i < len; i++) { + if (c == content[i]) { + TString ret{this->allocator}; + ret.len = len - 1; + ret.content = allocator->alloc(len, false); + if (ret.content == nullptr) { + __die(); + } + + adl::memcpy(ret.content, content, i * sizeof(char)); + adl::memcpy(ret.content + i, content + i + 1, (len - i - 1) * sizeof(char)); + ret[ret.len] = '\0'; + return ret; + } + } + + return *this; + } +} + +TString& TString::operator+=(const TString& str) +{ + if (str.len == 0) { + return *this; + } + else { + char* p; + auto resLen = len + str.len; + + p = allocator->alloc(resLen + 1, false); + if (p == nullptr) { + __die(); + } + if (content != nullptr) { + adl::memcpy(p, content, len * sizeof(char)); + + this->freeUpContent(); + } + + adl::memcpy(p + len, str.content, str.len * sizeof(char)); + p[resLen] = '\0'; + + this->len = resLen; + + this->content = p; + return *this; + } +} +TString& TString::operator+=(const char* str) +{ + if (str == nullptr || str[0] == '\0') { + return *this; + } + else { + auto lengthOfStr = adl::strlen(str); + auto resLen = len + lengthOfStr; + + char* p; + + p = allocator->alloc(resLen + 1, false); + if (p == nullptr) { + __die(); + } + if (content != nullptr) { + adl::memcpy(p, content, len * sizeof(char)); + this->freeUpContent(); + } + + adl::memcpy(p + len, str, lengthOfStr * sizeof(char)); + p[resLen] = '\0'; + + this->len = resLen; + + this->content = p; + return *this; + } +} +TString& TString::operator+=(const char c) +{ + if (c == '\0') { + return *this; + } + else { + auto resLen = len + 1; + char* p; + + p = allocator->alloc(resLen + 1, false); + if (p == nullptr) { + __die(); + } + + if (this->content != nullptr) { + adl::memcpy(p, content, len * sizeof(char)); + this->freeUpContent(); + } + + p[resLen - 1] = c; + p[resLen] = '\0'; + + this->len = resLen; + this->content = p; + return *this; + } +} + +TString& TString::operator-=(const TString& str) +{ + return *this = *this - str; +} +TString& TString::operator-=(const char* str) +{ + return *this = *this - str; +} +TString& TString::operator-=(const char c) +{ + return *this = *this - c; +} + +char& TString::operator[](size_t i) +{ + return content[i]; +} + +const char& TString::operator[](size_t i) const +{ + return content[i]; +} + + +const TString TString::operator*(int x) const +{ + TString ret; + + if (this->len == 0 || x <= 0) { + return ret; + } + else { + // x >= 1 + char* p = allocator->alloc(len * adl::size_t(x) + 1, false); + if (p == nullptr) { + __die(); + } + + for (adl::size_t i = 0; i < adl::size_t(x); i++) { + adl::memcpy(p + i * len, content, len * sizeof(char)); + } + + ret.content = p; + ret.len = this->len * adl::size_t(x); + ret.content[ret.len] = '\0'; + + return ret; + } +} + +const TString operator*(const int x, const TString& str) +{ + return str * x; +} + +TString& TString::operator*=(int x) +{ + return *this = *this * x; +} + +const TString operator+(const char c, const TString& str) +{ + char tmp[2] = { c, '\0' }; + return tmp + str; +} + +bool TString::operator==(const TString& str) const +{ + if (this->content == str.content) { + return true; + } + else if (this->len != str.len) { + return false; + } + else if (this->content == nullptr || str.content == nullptr) { + return false; + } + else { + for (size_t i = 0; i < len; i++) { + if (this->content[i] != str[i]) { + return false; + } + } + return true; + } +} + +bool TString::operator==(const char* str) const +{ + if (str == content) { + return true; + } + else if (str == nullptr || str[0] == '\0') { + return len == 0; + } + else if (len == 0) { + return false; + } + + auto lenOfStr = adl::strlen(str); + if (lenOfStr != len) { + return false; + } + for (size_t i = 0; i < lenOfStr; i++) { + if (str[i] != content[i]) { + return false; + } + } + return true; +} + +bool TString::operator!=(const char* str) const +{ + return !(*this == str); +} + +bool TString::operator!=(const TString& str) const +{ + return !(*this == str); +} + +bool TString::operator>(const TString& str) const +{ + if (this->len + str.len == 0 || this->len == 0) { + return false; + } + else if (str.len == 0) { + return true; + } + else { + auto minLen = len < str.len ? len : str.len; + for (size_t i = 0; i < minLen; i++) { + if (content[i] > str[i]) { + return true; + } + else if (content[i] < str[i]) { + return false; + } + } + + return len > str.len; + } +} + +bool TString::operator>(const char* str) const +{ + auto lengthOfStr = (str == nullptr ? 0 : adl::strlen(str)); + if (this->len + lengthOfStr == 0 || this->len == 0) { + return false; + } + else if (lengthOfStr == 0) { + return true; + } + else { + auto minLen = len < lengthOfStr ? len : lengthOfStr; + for (size_t i = 0; i < minLen; i++) { + if (content[i] > str[i]) { + return true; + } + else if (content[i] < str[i]) { + return false; + } + } + + return len > lengthOfStr; + } +} + + +bool TString::operator<(const TString& str) const +{ + if (this->len + str.len == 0 || str.len == 0) { + return false; + } + else if (len == 0) { + return true; + } + else { + auto minLen = len < str.len ? len : str.len; + for (size_t i = 0; i < minLen; i++) { + if (content[i] < str[i]) { + return true; + } + else if (content[i] > str[i]) { + return false; + } + } + + return len < str.len; + } +} + +bool TString::operator<(const char* str) const +{ + auto lengthOfStr = (str == nullptr ? 0 : adl::strlen(str)); + if (this->len + lengthOfStr == 0 || lengthOfStr == 0) { + return false; + } + else if (len == 0) { + return true; + } + else { + auto minLen = len < lengthOfStr ? len : lengthOfStr; + for (size_t i = 0; i < minLen; i++) { + if (content[i] < str[i]) { + return true; + } + else if (content[i] > str[i]) { + return false; + } + } + + return len < lengthOfStr; + } +} + +bool TString::operator>=(const TString& str) const +{ + return !(*this < str); +} +bool TString::operator>=(const char* str) const +{ + return !(*this < str); +} + +bool TString::operator<=(const TString& str) const +{ + return !(*this > str); +} +bool TString::operator<=(const char* str) const +{ + return !(*this > str); +} + + +TString TString::substr(const size_t pos, const size_t len) const { + TString ret; + + if (len == 0 || pos >= this->len) + return ret; + + size_t endIdx = pos + len; // exclusive + if (endIdx > this->len) { + endIdx = this->len; + } + + ret.len = endIdx - pos; + ret.content = allocator->alloc(ret.len + 1, false); + if (ret.content == nullptr) { + __die(); + } + + adl::memcpy(ret.content, this->content + pos, ret.len); + ret.content[ret.len] = '\0'; + + return ret; +} + + + +void TString::split(const TString& key, ArrayList& out) const { + out.clear(); + if (key.length() == 0) { + out.append(key); + return; + } + + adl::TString tmp{this->allocator}; + adl::size_t i = 0; + while (i < length()) { + if (adl::strncmp(key.c_str(), content + i, key.length()) == 0) { + if (tmp.length()) { + out.append(tmp); + tmp.clear(); + } + + i += key.length(); + continue; + } + + tmp += content[i++]; + } + + if (tmp.length()) { + out.append(tmp); + } +} + + +bool operator>(const char* strA, const TString& strB) +{ + return strB < strA; +} +bool operator<(const char* strA, const TString& strB) +{ + return strB > strA; +} +bool operator>=(const char* strA, const TString& strB) +{ + return strB <= strA; +} +bool operator<=(const char* strA, const TString& strB) +{ + return strB >= strA; +} + + +bool operator==(const char* strA, const TString& strB) +{ + return strB == strA; +} + +bool operator!=(const char* strA, const TString& strB) +{ + return strB != strA; +} + + +int64_t TString::toInt64() const { + if (len == 0) + return 0; + + bool negative = false; + int64_t res = 0; + size_t idx = 0; + if (content[0] == '-') { + negative = true; + idx++; + } + + while (idx < len) { + char ch = content[idx]; + + if (ch < '0' || ch > '9') + break; + + res *= 10; + res += ch - '0'; + + idx++; + } + + return negative ? (-res) : res; +} + + +#define TSTRING_IMPL_TO_STRING(Type) \ + TString TString::to_string(const Type x) { \ + TString res; \ + Type value = x; \ + while (value) { \ + res = char((value % 10) + '0') + res; \ + value /= 10; \ + } \ +\ + return res.length() ? res : "0"; \ + } + + +TSTRING_IMPL_TO_STRING(adl::uint32_t) +TSTRING_IMPL_TO_STRING(adl::uint64_t) +TSTRING_IMPL_TO_STRING(adl::size_t) + + +#undef TSTRING_IMPL_TO_STRING + + +} // namespace adl diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/TString.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/TString.h new file mode 100644 index 0000000..6446c05 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/TString.h @@ -0,0 +1,116 @@ +/* 2051565 GTY Tongji CS */ +// Ported from TJ OOP HW. Modified for Amkos. + +#pragma once + +#include "./Allocator.h" +#include "./config.h" + +namespace adl { + + +template class ArrayList; + + +class TString { +protected: + char* content; + size_t len; + adl::Allocator* allocator = nullptr; + + inline void freeUpContent(); + + +public: + static const size_t npos = -1; + + TString(adl::Allocator* alloc = &defaultAllocator); + TString(const char* str, adl::Allocator* alloc = &defaultAllocator); + TString(const TString& str, adl::Allocator* alloc = &defaultAllocator); + +#if defined(__GNUC__) + +#else + TString(const int x); +#endif + + virtual ~TString(); + + + void freeUp(); + void clear(); + + size_t length() const; + const char* c_str() const; + inline char* data() { return this->content; } + inline const char* data() const { return this->content; } + + TString& operator = (const TString& str); + TString& operator = (const char* str); + + const TString operator + (const TString& str) const; + const TString operator + (const char* str) const; + const TString operator + (const char c) const; + + const TString operator - (const TString& str) const; + const TString operator - (const char* str) const; + const TString operator - (const char c) const; + + const TString operator * (const int x) const; + + const TString operator ! () const; + + TString& operator *= (int x); + TString& operator += (const TString& str); + TString& operator += (const char* str); + TString& operator += (const char c); + + TString& operator -= (const TString& str); + TString& operator -= (const char* str); + TString& operator -= (const char c); + + bool operator == (const TString& str) const; + bool operator == (const char* str) const; + bool operator != (const TString& str) const; + bool operator != (const char* str) const; + bool operator > (const TString& str) const; + bool operator > (const char* str) const; + bool operator < (const TString& str) const; + bool operator < (const char* str) const; + bool operator >= (const TString& str) const; + bool operator >= (const char* str) const; + bool operator <= (const TString& str) const; + bool operator <= (const char* str) const; + + TString substr(const size_t pos, const size_t len = npos) const; + char& at(const int n); + const char& at(const int n) const; + + void split(const adl::TString&, adl::ArrayList& out) const; + + char& operator[] (size_t i); + const char& operator[] (size_t i) const; + + friend const TString operator + (const char* strA, const TString& strB); + + friend const TString operator + (const char c, const TString& str); + friend const TString operator * (const int x, const TString& str); + + friend bool operator == (const char* strA, const TString& strB); + friend bool operator != (const char* strA, const TString& strB); + friend bool operator > (const char* strA, const TString& strB); + friend bool operator < (const char* strA, const TString& strB); + friend bool operator >= (const char* strA, const TString& strB); + friend bool operator <= (const char* strA, const TString& strB); + + int64_t toInt64() const; + + static TString to_string(const adl::uint32_t); + static TString to_string(const adl::uint64_t); + static TString to_string(const unsigned long); +}; + + + +} // namespace MtsysKv + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/arpa/inet.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/arpa/inet.h new file mode 100644 index 0000000..c1fbe0d --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/arpa/inet.h @@ -0,0 +1,61 @@ +// 124033910070 GTY +// gongty [at] tongji [dot] edu [dot] cn + +// created on 2024.11.25 +// at Jiangchuan, Minhang, Shanghai + +// implements: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html + +#pragma once + +#include "../endian.h" + +namespace adl { + + +inline int32_t htonl(int32_t x) { + return htobe32(x); +} + +inline uint32_t htonl(uint32_t x) { + return htobe32(x); +} + + +inline uint16_t htons(uint16_t x) { + return htobe16(x); +} + + +inline int64_t htonq(int64_t x) { + return htobe64(x); +} + + +inline uint64_t htonq(uint64_t x) { + return htobe64(x); +} + + +inline int32_t ntohl(int32_t x) { + return htonl(x); +} + +inline uint32_t ntohl(uint32_t x) { + return htonl(x); +} + +inline uint16_t ntohs(uint16_t x) { + return htons(x); +} + +inline int64_t ntohq(int64_t x) { + return htonq(x); +} + +inline uint64_t ntohq(uint64_t x) { + return htonq(x); +} + + +} // namespace adl diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/ArrayList.hpp b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/ArrayList.hpp new file mode 100644 index 0000000..50ca155 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/ArrayList.hpp @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * Array List Structure + * by gongty [AT] tongji [DOT] edu [DOT] cn + * + * Warning!! Heavy Data Structure: + * This structure heavily depends on template, each usage could make your kernel larger. + * + * + * created on 2023.7.3 at Anting Town, Jiading, Shanghai + */ + +/* + * This data structure is forked from YurongOS, modified for Amkos. + * + * https://github.com/FlowerBlackG/YurongOS/blob/master/src/lib/collections/ArrayList.hpp + */ + +#pragma once + + +#include "../string.h" +#include "../config.h" +#include "../stdint.h" +#include + +#include + +namespace adl { + + +template +class ArrayListIterator { + typedef ArrayListIterator Self; + +protected: + size_t size; + size_t curr; + DataType* data; + +public: + ArrayListIterator(DataType* data, size_t size, size_t curr) { + this->size = size; + this->data = data; + this->curr = curr; + } + + DataType& operator * () const { return data[curr]; }; + Self& operator ++ () { + curr++; + return *this; + } + + Self operator ++ (int) { + auto tmp = *this; + curr++; + return tmp; + } + + friend bool operator == (const Self& a, const Self& b) { + return a.curr == b.curr + && a.size == b.size + && a.data == b.data; + } + + friend bool operator != (const Self& a, const Self& b) { + return a.curr != b.curr + || a.size != b.size + || a.data != b.data; + } + +}; + + +template +class ArrayList { + +protected: + size_t _size = 0; + size_t _capacity = 0; + DataType* _data = nullptr; + Allocator* allocator; + +public: + ArrayList() { + this->allocator = &defaultAllocator; + } + + ArrayList(Allocator& allocator) { + this->allocator = &allocator; + } + + + ArrayList(const ArrayList& other) { + this->allocator = other.allocator; + if (!this->resize(other.size())) { + printf("failed to copy arraylist!\n"); + return; + } + + adl::memcpy(this->_data, other._data, other._size * sizeof(DataType)); + } + + + const ArrayList& operator = (const ArrayList& other) { + if (this == &other) { + return *this; + } + + if (_capacity) + allocator->free(_data); + + this->allocator = other.allocator; + if (!this->resize(other.size())) { + printf("failed to copy arraylist!\n"); + return *this; + } + + adl::memcpy(this->_data, other._data, other._size * sizeof(DataType)); + return *this; + } + + + void clear() { + _size = 0; + } + + virtual ~ArrayList() { + if (_capacity) + allocator->free(_data); + } + + inline size_t size() const { + return this->_size; + } + + inline size_t capacity() const { + return this->_capacity; + } + + inline DataType* data() { + return this->_data; + } + + inline const DataType* data() const { + return this->_data; + } + + void reserve(size_t new_capacity) { + if (new_capacity <= _capacity) { + // Do nothing + return; + } + if (!this->allocator) { // Check null ptr + printf("[CRITICAL] Allocator is null!\n"); + return; + } + // Allocate new memory + auto newAddr = allocator->alloc(new_capacity); + if (!newAddr) { + printf("[CRITICAL] ArrayList reserve failed: insufficient memory.\n"); + return; + } + // Copy the original data to new memory + for (size_t i = 0; i < _size; i++) + newAddr[i] = _data[i]; + // Free old memory + if (_capacity) { + allocator->free(_data); + } + // Update ptr and capacity + _data = newAddr; + _capacity = new_capacity; + } + + + bool resize(size_t newSize) { + if (newSize > _size) { + reserve(newSize); + _size = _capacity; + return _size == newSize; + } + else { + _size = newSize; + return true; + } + } + + int append(const DataType& data) { + + if (_size == _capacity) { + // alloc more memory + size_t newCapacity = _capacity; + if (newCapacity == 0) { + newCapacity = 16; + } else { + newCapacity += newCapacity / 2; + } + + auto newAddr = allocator->alloc(newCapacity); + if (!newAddr) { + return 1; // error + } + + for (size_t i = 0; i < _size; i++) + newAddr[i] = _data[i]; + + if (_capacity) + allocator->free(_data); + + _data = newAddr; + _capacity = newCapacity; + } + + _data[_size++] = data; + + return 0; // inserted successfully + } + + inline int push(const DataType& data) { + return this->append(data); + } + + DataType pop() { + if (_size) { + return _data[--_size]; // todo: should call destructor + } + + return * ( DataType* ) 0; + } + + + adl::size_t count(const DataType& data) const { + adl::size_t res = 0; + for (const auto& it : *this) { + if (it == data) { + res++; + } + } + return res; + } + + + bool contains(const DataType& data) const { + return count(data); + } + + + DataType& operator [] (size_t idx) { + return _data[idx]; + } + + const DataType& operator [] (size_t idx) const { + return _data[idx]; + } + + + bool operator == (const ArrayList& other) const { + if (_size != other._size) + return false; + for (adl::size_t i = 0; i < _size; i++) { + if (_data[i] != other[i]) + return false; + } + return true; + } + + + DataType& back() { return _data[_size - 1]; } + DataType& front() { return _data[0]; } + + + bool isEmpty() const { return _size == 0; } + bool isNotEmpty() const { return _size > 0; } + + + // ------ iteration related ------ + + ArrayListIterator begin() const { + return ArrayListIterator(_data, _size, 0); + } + + ArrayListIterator end() const { + return ArrayListIterator(_data, _size, _size); + } + +}; + + +class ByteArray : public ArrayList { +protected: + int construct(const void* data, adl::size_t dataLen, adl::Allocator& alloc) { + this->allocator = &alloc; + if (!resize(dataLen)) { + // failed to reserve. + printf("Failed to initialize ByteArray! No memory.\n"); + return -1; + } + adl::memcpy(this->_data, data, dataLen); + return 0; + } + +public: + ByteArray() : adl::ArrayList() {} + + ByteArray(adl::Allocator& alloc) : adl::ArrayList(alloc) {} + + + ByteArray(const void* data, adl::size_t dataLen, adl::Allocator& alloc = defaultAllocator) + : adl::ArrayList(alloc) + { + construct(data, dataLen, alloc); + } + + + ByteArray(const char* str, adl::Allocator& alloc = defaultAllocator) + : adl::ArrayList(alloc) + { + construct(str, (str ? strlen(str) : 0), alloc); + } + + + ByteArray(const TString& str, adl::Allocator& alloc = defaultAllocator) + : adl::ArrayList(alloc) + { + construct(str.data(), str.length(), alloc); + } + + + TString toString() const { + TString str; + for (auto& ch : *this) { + str += char(ch); + } + + return str; + } + + + int append(const void* data, adl::size_t len) { + if (len == 0) + return 0; + + if (!resize(_size + len)) + return 1; // out of resource. + + adl::memcpy(_data + _size - len, data, len); + + return 0; + } + +}; + + + +} // namespace adl diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/HashMap.hpp b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/HashMap.hpp new file mode 100644 index 0000000..33c0dbf --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/HashMap.hpp @@ -0,0 +1,22 @@ +/* + HashMap + + Created on 2025.1.19 at Zhuhai, Guangdong + + gongty [at] tongji [dot] edu [dot] cn + +*/ + + +#pragma once + +#include "./RedBlackTree.hpp" + +namespace adl { + +template +using HashMap = RedBlackTree; + + +} + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/LinkedList.cpp b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/LinkedList.cpp new file mode 100644 index 0000000..cf42152 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/LinkedList.cpp @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + + 链表 + + 创建于2023年2月5日 江西省上饶市玉山县 + +*/ + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +// modified for Amkos + +#include + +namespace adl { + +LinkedList& LinkedList::append(LinkedListNode* node) { + if (tail) { + + tail->next = node; + node->prev = tail; + node->next = nullptr; + + tail = node; + + } else { + + node->prev = node->next = nullptr; + head = tail = node; + + } + + length++; + + return *this; + +} + + +LinkedList& LinkedList::insert(LinkedListNode* node, int index) { + + if (index >= this->length) { + pushBack(node); + } else if (index <= 0) { + if (this->head) { + node->next = this->head->next; + this->head->next->prev = node; + } + + node->prev = nullptr; + this->head = node; + this->length++; + } else { + + auto prevNode = this->get(index); + node->prev = prevNode; + node->next = prevNode->next; + prevNode->next->prev = node; + prevNode->next = node; + this->length++; + + } + + + return *this; +} + +LinkedList& LinkedList::insertWhen( + LinkedListNode& node, + const int64_t cargo, + int (* judge) (const int64_t cargo, LinkedListNode* before, LinkedListNode* after) +) { + + + LinkedListNode* prev = nullptr; + LinkedListNode* next = this->head; + + do { + + int judgement = judge(cargo, prev, next); + if (judgement == 0) { // accept + + this->length++; + + if (prev && next) { + + // ... -> prev -> (new) -> next -> ... + + prev->next = &node; + next->prev = &node; + node.prev = prev; + node.next = next; + + } else if (prev) { + + // ... -> prev -> (new) -> tail + + this->tail = prev->next = &node; + node.next = nullptr; + node.prev = prev; + + } else if (next) { + + // head -> (new) -> next -> ... + + this->head = next->prev = &node; + node.next = next; + node.prev = nullptr; + + } else { + + // head -> (new) -> tail + this->head = this->tail = &node; + node.prev = node.next = nullptr; + + } + + break; + + } else if (judgement < 0) { // cancel + + break; + + } else { // continue + + prev = next; + if (next) { + next = next->next; + } + + } + + } while (prev || next); + + return *this; +} + +LinkedList& LinkedList::insertWhen( + LinkedListNode* node, + const int64_t cargo, + int (* judge) (const int64_t cargo, LinkedListNode* before, LinkedListNode* after) +) { + return this->insertWhen(*node, cargo, judge); +} + +LinkedList& LinkedList::pushBack(LinkedListNode* node) { + + return this->append(node); + +} + + +LinkedList& LinkedList::remove(LinkedListNode* node) { + + if (node == this->head && node == this->tail) { + this->head = this->tail = nullptr; + + } else if (node == this->head) { + this->head->next->prev = nullptr; + this->head = this->head->next; + + } else if (node == this->tail) { + this->tail->prev->next = nullptr; + this->tail = this->tail->prev; + + } else { + node->prev->next = node->next; + node->next->prev = node->prev; + } + + this->length --; + + return *this; +} + + +LinkedListNode* LinkedList::removeAt(int index) { + auto node = this->get(index); + + this->remove(node); + + return node; +} + +LinkedListNode* LinkedList::removeFirst() { + + LinkedListNode* ret = nullptr; + + if (head) { + + length--; + ret = head; + if (this->head == this->tail) { + this->head = this->tail = nullptr; + } else { + this->head->next->prev = nullptr; + this->head = this->head->next; + } + + } + + return ret; +} + +LinkedListNode* LinkedList::removeLast() { + + auto res = this->tail; + + if (res) { + + this->tail = res->prev; + res->prev = nullptr; + + if (this->tail) { + this->tail->next = nullptr; + } else { + this->head = nullptr; + } + + length--; + + } + + res->next = nullptr; + res->prev = nullptr; + + return res; +} + +LinkedListNode* LinkedList::popBack() { + return this->removeLast(); +} + + +bool LinkedList::contains(LinkedListNode* node) { + return this->indexOf(node) >= 0; +} + + +LinkedListNode* LinkedList::getFirst() { + return this->head; +} + +LinkedListNode* LinkedList::getLast() { + return this->tail; +} + +LinkedListNode* LinkedList::get(int index) { + + if (index < length) { + + LinkedListNode* res; + + if (index < length / 2) { + + int counter = index; + res = this->head; + while (counter--) { + res = res->next; + } + + } else { + + int counter = length - index - 1; + res = this->tail; + while (counter--) { + res = res->prev; + } + + } + + return res; + + } else { + return nullptr; + } +} + + +int LinkedList::indexOf(LinkedListNode* node) { + auto curr = this->head; + int counter = 0; + + while (curr && curr != node) { + counter++; + curr = curr->next; + } + + return curr ? counter : -1; +} + +bool LinkedList::isEmpty() { + return this->length == 0; +} + +bool LinkedList::isNotEmpty() { + return this->length != 0; +} + +void LinkedList::clear() { + this->head = this->tail = nullptr; +} + +void LinkedList::forEach( void (* callable) (LinkedListNode*) ) { + auto curr = this->head; + while (curr) { + callable(curr); + curr = curr->next; + } +} + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/LinkedList.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/LinkedList.h new file mode 100644 index 0000000..753b9d0 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/LinkedList.h @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + + 链表 + + 创建于2023年2月5日 江西省上饶市玉山县 + +*/ + +#pragma once + +#include "../sys/types.h" + +namespace adl { + +/** + * 链表节点。 + * 将该结构嵌入需要用链表连接的对象结构,通过偏移方式得到数据结构本体。 + */ +struct LinkedListNode { + LinkedListNode* prev; + LinkedListNode* next; +}; + + +class LinkedListIterator { + + typedef LinkedListIterator Self; + typedef LinkedListNode ElementType; + +public: + LinkedListIterator(ElementType* node) { this->node = node;} + + ElementType& operator * () const { return *node; }; + + Self& operator ++ () { + node = node->next; + return *this; + }; + + Self operator ++ (int) { + auto tmp = *this; + node = node->next; + return tmp; + } + + Self& operator -- () { + node = node->prev; + return *this; + } + + Self operator -- (int) { + auto tmp = *this; + node = node->prev; + return tmp; + } + + friend bool operator == (const Self& a, const Self& b) { + return a.node == b.node; + } + + friend bool operator != (const Self& a, const Self& b) { + return a.node != b.node; + } + + +protected: + ElementType* node; +}; + +struct LinkedList { + LinkedListNode* head = nullptr; + LinkedListNode* tail = nullptr; + + int length = 0; + + LinkedList& append(LinkedListNode* node); + LinkedList& insert(LinkedListNode* node, int index); + + /** + * 逐位置判断是否可插入。 + * + * @param judge 返回 0 表示可以插入。返回正数表示继续查找。返回负数表示取消操作。 + */ + LinkedList& insertWhen( + LinkedListNode& node, + const adl::int64_t cargo, + int (* judge) (const adl::int64_t cargo, LinkedListNode* before, LinkedListNode* after) + ); + + LinkedList& insertWhen( + LinkedListNode* node, + const adl::int64_t cargo, + int (* judge) (const adl::int64_t cargo, LinkedListNode* before, LinkedListNode* after) + ); + + LinkedList& pushBack(LinkedListNode* node); + + LinkedList& remove(LinkedListNode* node); + + LinkedListNode* removeAt(int index); + LinkedListNode* removeFirst(); + LinkedListNode* removeLast(); + LinkedListNode* popBack(); + + bool contains(LinkedListNode* node); + + LinkedListNode* getFirst(); + LinkedListNode* getLast(); + LinkedListNode* get(int index); + + int indexOf(LinkedListNode* node); + + bool isEmpty(); + bool isNotEmpty(); + + void clear(); + + void forEach( + void (* callable) ( + LinkedListNode* node + ) + ); + + // ------ 迭代相关 ------ + + inline LinkedListIterator begin() const { + return LinkedListIterator(this->head); + } + + inline LinkedListIterator end() const { + return LinkedListIterator(nullptr); + } +}; + + +} // namespace adl diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp new file mode 100644 index 0000000..b0f5ccf --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp @@ -0,0 +1,1407 @@ +/** + * Red Black Tree Hpp + * by GTY + * 2022.1 + * at Yushan County, Shangrao, Jiangxi + */ + +// https://blog.csdn.net/m0_62405272/article/details/122612246 +// https://blog.csdn.net/m0_62405272/article/details/122631653 + +// 2024.11.6: modified for Amkos + + +#include +#include "../Allocator.h" +#include "../config.h" +#include "../sys/types.h" +#include +#include + +namespace adl { + + +template +class RedBlackTreeIterator; + + +template +class RedBlackTree { + friend RedBlackTreeIterator; + +public: + /** Object's life-management methods */ + RedBlackTree(adl::Allocator* = &adl::defaultAllocator); + ~RedBlackTree(); + + /** + * Clear all elements in tree. + */ + void clear(); + +public: + /** Basic query methods */ + + /** + * Determine whether a key is in the tree. + * + * @param queryKey + */ + bool hasKey(const KeyType&); + + bool contains(const KeyType& key) { + return hasKey(key); + } + + /** + * Get data (ref) by key. + * + * @param key + */ + DataType& getData(const KeyType&); + + DataType& operator [] (const KeyType&); + + /** + * Get data (clone) by key. + */ + DataType copyData(const KeyType&, const DataType* fallback = nullptr); + + /** + * Set data. If data with same key already exists, it would be overwritten. + * + * @param key + * @param data + */ + RedBlackTree& setData(const KeyType&, const DataType&); + + /** + * Delete key. + * + * @param key + */ + RedBlackTree& removeKey(const KeyType&, bool noExcept = true); + + adl::size_t size(); + + /** + * Range scan. This method will search for keys inside [lhs, rhs], + * and calls collector with data for each node discovered. + * + * @param lhs inclusive + * @param rhs inclusive + * @param data to be passed to collector + * @param collector + * @return How many elements collected. + */ + adl::size_t rangeScan( + const KeyType& lhs, + const KeyType& rhs, + void* data, + bool (*collector) (void* data, const KeyType&, const DataType&) + ); + + + /* ------ iteration ------ */ + + RedBlackTreeIterator begin() const { + return RedBlackTreeIterator(this->root); + } + + + RedBlackTreeIterator end() const { + return RedBlackTreeIterator(nullptr); + } + + +public: + struct RuntimeError : Genode::Exception {}; + + +protected: + enum class NodeColor { + RED, BLACK + }; + enum class ChildSide { + LEFT, RIGHT + }; + struct Node { + KeyType key; + DataType data; + NodeColor color = NodeColor::RED; + Node* parent = nullptr; + Node* leftChild = nullptr; + Node* rightChild = nullptr; + }; + +protected: + /** + * Release one node with all of its children, recursively. + * + * @param node first node to be released recursively + */ + void cleanup(Node* node); + + + Node* locateNode(const KeyType&); + + + /** + * Non-locked version of range scan. Designed to be called by public rangeScan method. + */ + adl::size_t doRangeScan( + const Node* node, + const KeyType& lhs, + const KeyType& rhs, + void* data, + bool (*collector) (void* data, const KeyType&, const DataType&) + ); + + + /** + * Rotate left. + * + * @param node + * @exception If `node` dosen't have right subtree, undefined behaviour occurs. + */ + void rotateLeft(Node* node); + + /** + * Rotate right + * + * @param node + * @exception If `node` dosen't have left subtree, undefined behaviour occurs + */ + void rotateRight(Node* node); + + /** + * + * + * @param node Child node of the two connected red nodes. + * @exception If node is nullptr, UB would occur. + */ + void rebalanceRedNode(Node* node); + + /** + * + * + * @param node Lighter node. + * @exception UB if node is not the lighter one. + */ + void rebalanceChildren(Node* node); + + + enum class LockType { + READ, WRITE + }; + + void lock(LockType); + void unlock(LockType); + + +protected: + /** + * Root node. + */ + Node* root = nullptr; + + adl::Allocator* allocator = nullptr; + + + /** + * Locking for multi-thread access. + */ + struct { + Genode::Mutex mutex; + + int readerCount = 0; + + Genode::Semaphore access {1}; + Genode::Semaphore write {1}; + } locking; + + + struct ReadGuard { + RedBlackTree* tree; + ReadGuard(RedBlackTree* t) : tree(t) { tree->lock(LockType::READ); } + ~ReadGuard() { tree->unlock(LockType::READ); } + }; + + + struct WriteGuard { + RedBlackTree* tree; + WriteGuard(RedBlackTree* t) : tree(t) { tree->lock(LockType::WRITE); } + ~WriteGuard() { tree->unlock(LockType::WRITE); } + }; + + +}; + + +/* ---------------- Iterator ---------------- */ + + +template +class RedBlackTreeIterator { + typedef RedBlackTreeIterator Self; + +protected: + RedBlackTree::Node* root; // Set to nullptr to disable this iterator. + RedBlackTree::Node* curr; + +protected: + void doOperatorPlusPlus() { + if (root == nullptr) + return; + + if (curr->rightChild) { + curr = curr->rightChild; + while (curr->leftChild) + curr = curr->leftChild; + return; + } + + + + while (true) { + + if (curr->parent == nullptr) { + root = nullptr; + return; + } + + if (curr->parent->rightChild == curr) { + curr = curr->parent; + continue; + } + + curr = curr->parent; + return; + + } + + + } // void doOperatorPlusPlus() + +public: + RedBlackTreeIterator(RedBlackTree::Node* root) { + this->root = this->curr = root; + + if (this->root == nullptr) + return; + + while (this->curr->leftChild) + this->curr = this->curr->leftChild; + } + + adl::ref_pair operator * () const { + return make_ref_pair(curr->key, curr->data); + } + + Self& operator ++ () { + doOperatorPlusPlus(); + return *this; + } + + Self operator ++ (int) { + auto tmp = *this; + doOperatorPlusPlus(); + return tmp; + } + + friend bool operator == (const Self& a, const Self& b) { + return (!a.root && !b.root) || (a.root == b.root && a.curr == b.curr); + } + + friend bool operator != (const Self& a, const Self& b) { + return !(a == b); + } + + +}; + + + +/* ---------------- Impl ---------------- */ + +template +RedBlackTree::RedBlackTree(adl::Allocator* allocator) +{ + this->allocator = allocator; +} + + +template +RedBlackTree::~RedBlackTree() +{ + this->clear(); +} + + +template +void RedBlackTree::clear() +{ + WriteGuard _g {this}; + if (this->root != nullptr) { + this->cleanup(this->root); + this->root = nullptr; + } +} + + +template +bool RedBlackTree::hasKey(const KeyType& queryKey) +{ + ReadGuard _g {this}; + return !!locateNode(queryKey); +} + + +template +DataType& RedBlackTree::getData(const KeyType& key) +{ + ReadGuard _g {this}; + auto node = locateNode(key); + + if (node) { + return node->data; // Warning: Race condition here. + } + + +#if 0 + throw std::runtime_error("could not find your key in the object."); // key not found. +#else + throw RuntimeError {}; +#endif + +} + + + +template +DataType& RedBlackTree::operator [] (const KeyType& key) +{ + + { + ReadGuard _g {this}; + auto node = locateNode(key); + + if (node) { + return node->data; // Warning: Race condition here. + } + } + + + this->setData(key, DataType {}); + + ReadGuard _g {this}; + return locateNode(key)->data; + +} + + + + + +template +DataType RedBlackTree::copyData(const KeyType& key, const DataType* fallback) { + ReadGuard _g {this}; + auto node = locateNode(key); + + if (node) { + return node->data; + } else if (fallback) { + return *fallback; + } + +#if 0 + throw std::runtime_error("could not find your key in the object."); // key not found. +#else + throw RuntimeError {}; +#endif +} + + +template +RedBlackTree& RedBlackTree::setData( + const KeyType& key, + const DataType& data +) +{ + WriteGuard _g {this}; + + + Node* currentNode = root; + Node* currentParent = nullptr; + + while (currentNode != nullptr) { + if (key == currentNode->key) { // key found + currentNode->data = data; + return *this; // data updated. exit. + } + else { + currentParent = currentNode; + currentNode = (currentNode->key > key ? currentNode->leftChild : currentNode->rightChild); + } + } + + + // now, we should create new node for data. + + /* + now, currentNode points to nullptr, currentParent points to the last visited node, probably nullptr. + new node is red, inserted to the end. + */ + + // create node + currentNode = allocator->alloc(); + currentNode->parent = currentParent; + currentNode->leftChild = nullptr; + currentNode->rightChild = nullptr; + currentNode->key = key; + currentNode->data = data; + + // If tree is empty, just set new node as root. + if (currentParent == nullptr) { + currentNode->color = NodeColor::BLACK; + this->root = currentNode; + return *this; + } + + + // now, we should handle when tree is not empty. + + currentNode->color = NodeColor::RED; + // bind new node to parent. + if (currentParent->key > key) { + currentParent->leftChild = currentNode; + } + else { + currentParent->rightChild = currentNode; + } + + this->rebalanceRedNode(currentNode); + return *this; +} + + +template +RedBlackTree& RedBlackTree::removeKey( + const KeyType& key, + bool noExcept +) +{ + WriteGuard _g {this}; + + + Node* currentNode = root; + + while (currentNode != nullptr) { + if (key == currentNode->key) { + break; // key found. matched. + } + else { + currentNode = (currentNode->key > key ? currentNode->leftChild : currentNode->rightChild); + } + } + + if (currentNode == nullptr) { + + if (noExcept) + return *this; + else { +#if 0 + throw std::runtime_error("could not find your key in the object."); // key not found. +#else + throw RuntimeError {}; +#endif + } + } + + + // Otherwise, find an alternative node. + // As long as there is at least one child, continue to search for an alternative node. + while (currentNode->leftChild != nullptr || currentNode->rightChild != nullptr) { + if (currentNode->rightChild != nullptr) { + // current node has right child. + // replace the original deleted node with its successor. + // note that successor must has no left child. + Node* replacementNode = currentNode->rightChild; + while (replacementNode->leftChild != nullptr) { + replacementNode = replacementNode->leftChild; + } + + struct { + Node* leftChild; + Node* rightChild; + NodeColor color; + Node* parent; + } currentNodeInfo = { + currentNode->leftChild, currentNode->rightChild, + currentNode->color, currentNode->parent + }, replacementNodeInfo = { + replacementNode->leftChild, replacementNode->rightChild, + replacementNode->color, replacementNode->parent + }; + + // exchange color. + currentNode->color = replacementNodeInfo.color; + replacementNode->color = currentNodeInfo.color; + + // edit parent node. + if (currentNodeInfo.parent != nullptr) { + if (currentNodeInfo.parent->leftChild == currentNode) { + currentNodeInfo.parent->leftChild = replacementNode; + } + else { + currentNodeInfo.parent->rightChild = replacementNode; + } + } + else { + this->root = replacementNode; + } + + // edit currentNode's left child's data. + if (currentNodeInfo.leftChild != nullptr) { + currentNodeInfo.leftChild->parent = replacementNode; + } + + // edit replacementNode's right child's data (if has) + if (replacementNodeInfo.rightChild != nullptr) { + replacementNodeInfo.rightChild->parent = currentNode; + } + + currentNode->leftChild = nullptr; + currentNode->rightChild = replacementNodeInfo.rightChild; + if (replacementNodeInfo.parent == currentNode) { + currentNode->parent = replacementNode; + } + else { + currentNode->parent = replacementNodeInfo.parent; + } + + replacementNode->leftChild = currentNodeInfo.leftChild; + replacementNode->parent = currentNodeInfo.parent; + if (currentNodeInfo.rightChild == replacementNode) { + replacementNode->rightChild = currentNode; + } + else { + replacementNode->rightChild = currentNodeInfo.rightChild; + } + + if (currentNodeInfo.rightChild != replacementNode) { + currentNodeInfo.rightChild->parent = replacementNode; + replacementNodeInfo.parent->leftChild = currentNode; + } + } // if (currnode has right child) + else { // curr child only have left child + + // note that prev node's right child must be nullptr. + Node* replacementNode = currentNode->leftChild; + while (replacementNode->rightChild != nullptr) { + replacementNode = replacementNode->rightChild; + } + + struct { + Node* leftChild; + Node* rightChild; + NodeColor color; + Node* parent; + } currentNodeInfo = { + currentNode->leftChild, currentNode->rightChild, + currentNode->color, currentNode->parent + }, replacementNodeInfo = { + replacementNode->leftChild, replacementNode->rightChild, + replacementNode->color, replacementNode->parent + }; + + // exchange color + currentNode->color = replacementNodeInfo.color; + replacementNode->color = currentNodeInfo.color; + + // edit parent node + if (currentNodeInfo.parent != nullptr) { + if (currentNodeInfo.parent->leftChild == currentNode) { + currentNodeInfo.parent->leftChild = replacementNode; + } + else { + currentNodeInfo.parent->rightChild = replacementNode; + } + } + else { + this->root = replacementNode; + } + + + if (currentNodeInfo.rightChild != nullptr) { + currentNodeInfo.rightChild->parent = replacementNode; + } + + if (replacementNodeInfo.leftChild != nullptr) { + replacementNodeInfo.leftChild->parent = currentNode; + } + + currentNode->rightChild = nullptr; + currentNode->leftChild = replacementNodeInfo.leftChild; + if (replacementNodeInfo.parent == currentNode) { + currentNode->parent = replacementNode; + } + else { + currentNode->parent = replacementNodeInfo.parent; + } + + replacementNode->rightChild = currentNodeInfo.rightChild; + replacementNode->parent = currentNodeInfo.parent; + if (currentNodeInfo.leftChild == replacementNode) { + replacementNode->leftChild = currentNode; + } + else { + replacementNode->leftChild = currentNodeInfo.leftChild; + } + + if (currentNodeInfo.leftChild != replacementNode) { + currentNodeInfo.leftChild->parent = replacementNode; + replacementNodeInfo.parent->rightChild = currentNode; + } + } + } // while (currentNode->leftChild != nullptr || currentNode->rightChild != nullptr) + + + // now, node to be deleted has no child. + + if (currentNode == this->root) { // if deleting root + this->root = nullptr; + allocator->free(currentNode); + } + else if (currentNode->color == NodeColor::RED) { + if (currentNode == currentNode->parent->leftChild) { + currentNode->parent->leftChild = nullptr; + } + else { + currentNode->parent->rightChild = nullptr; + } + allocator->free(currentNode); + } + else { + // target node's parent must exists. + // because target is black, it must have sibling. + Node* sibling = (currentNode->parent->leftChild != currentNode ? + currentNode->parent->leftChild : currentNode->parent->rightChild); + + Node* currentparentParent = currentNode->parent; + + ChildSide siblingSideToparentParent = + (sibling == currentparentParent->leftChild ? ChildSide::LEFT : ChildSide::RIGHT); + + // now we can delete the node. + if (currentparentParent->leftChild == currentNode) { + currentparentParent->leftChild = nullptr; + } + else { + currentparentParent->rightChild = nullptr; + } + + allocator->free(currentNode); + + + // Next, let's consider different scenarios. + + if (currentparentParent->color == NodeColor::RED) { + // when parent is red, sibling must be red. + + if (sibling->leftChild != nullptr && sibling->rightChild != nullptr) + { + /* + X: Remove; R: Red; B: Black + R + / \ + X B + / \ + R R Or her symmetrical counterpart + */ + // sibling's children are red + // note: If her black uncle has child, his child must be red + // operation: rotate sibling, and rotate parent. set parent to black, sibling to red. + sibling->color = NodeColor::RED; + currentparentParent->color = NodeColor::BLACK; + + if (siblingSideToparentParent == ChildSide::RIGHT) { + sibling->rightChild->color = NodeColor::BLACK; + this->rotateLeft(currentparentParent); + } + else { + sibling->leftChild->color = NodeColor::BLACK; + this->rotateRight(currentparentParent); + } + } + else if (siblingSideToparentParent == ChildSide::RIGHT && sibling->leftChild != nullptr) + { + /* + X: Remove; R: Red; B: Black + R + / \ + X B + / + R + */ + + currentparentParent->color = NodeColor::BLACK; + this->rotateRight(sibling); + this->rotateLeft(currentparentParent); + } + else if (siblingSideToparentParent == ChildSide::LEFT && sibling->rightChild != nullptr) + { + /* + X: Remove; R: Red; B: Black + R + / \ + B X + \ + R + */ + + currentparentParent->color = NodeColor::BLACK; + this->rotateLeft(sibling); + this->rotateRight(currentparentParent); + } + else if (siblingSideToparentParent == ChildSide::RIGHT && sibling->rightChild != nullptr) { + /* + X: Remove; R: Red; B: Black + R + / \ + X B + \ + R + */ + currentparentParent->color = NodeColor::BLACK; + sibling->color = NodeColor::RED; + sibling->rightChild->color = NodeColor::BLACK; + this->rotateLeft(currentparentParent); + } + else if (siblingSideToparentParent == ChildSide::LEFT && sibling->leftChild != nullptr) + { + /* + X: Remove; R: Red; B: Black + R + / \ + B X + / + R + */ + currentparentParent->color = NodeColor::BLACK; + sibling->color = NodeColor::RED; + sibling->leftChild->color = NodeColor::BLACK; + this->rotateRight(currentparentParent); + } + else { // sibling has no child + sibling->color = NodeColor::RED; + currentparentParent->color = NodeColor::BLACK; + } + + } // currentparentParent->color == NodeColor::RED + else { // currentparentParent->color == NodeColor::BLACK + if (sibling->color == NodeColor::BLACK + && sibling->leftChild != nullptr && sibling->rightChild != nullptr) + { + // sibling is black, and it has two children. then these children must be red. + if (siblingSideToparentParent == ChildSide::RIGHT) { + /* + X: Remove; R: Red; B: Black + B + / \ + X B + / \ + R R + */ + sibling->leftChild->color = NodeColor::BLACK; + this->rotateRight(sibling); + this->rotateLeft(currentparentParent); + } + else { + /* + X: Remove; R: Red; B: Black + B + / \ + B X + / \ + R R + */ + sibling->rightChild->color = NodeColor::BLACK; + this->rotateLeft(sibling); + this->rotateRight(currentparentParent); + } + } + else if (sibling->color == NodeColor::BLACK && + (sibling->leftChild != nullptr || sibling->rightChild != nullptr)) + { + // sibling is black, and has one red child. + if (siblingSideToparentParent == ChildSide::RIGHT && sibling->rightChild != nullptr) { + /* + X: Remove; R: Red; B: Black + B + / \ + X B + \ + R + */ + sibling->rightChild->color = NodeColor::BLACK; + this->rotateLeft(currentparentParent); + } + else if (siblingSideToparentParent == ChildSide::RIGHT && sibling->leftChild != nullptr) + { + /* + X: Remove; R: Red; B: Black + B + / \ + X B + / + R + */ + sibling->leftChild->color = NodeColor::BLACK; + this->rotateRight(sibling); + this->rotateLeft(currentparentParent); + } + else if (siblingSideToparentParent == ChildSide::LEFT && sibling->leftChild != nullptr) + { + /* + X: Remove; R: Red; B: Black + B + / \ + B X + / + R + */ + sibling->leftChild->color = NodeColor::BLACK; + this->rotateRight(currentparentParent); + } + else { + /* + X: Remove; R: Red; B: Black + B + / \ + B X + \ + R + */ + sibling->rightChild->color = NodeColor::BLACK; + this->rotateLeft(sibling); + this->rotateRight(currentparentParent); + } + } + else if (sibling->color == NodeColor::BLACK) { + // black sibling without child + /* + B B + / \ -> / \ + X B X R + */ + if (currentparentParent->leftChild == nullptr) { + currentparentParent->rightChild->color = NodeColor::RED; + this->rebalanceChildren(currentparentParent); + } + else { + currentparentParent->leftChild->color = NodeColor::RED; + this->rebalanceChildren(currentparentParent); + } + } + else { + // sibling is red. then it must has two black children. + sibling->color = NodeColor::BLACK; + currentparentParent->color = NodeColor::RED; + if (siblingSideToparentParent == ChildSide::RIGHT) { + /* + B + / \ + X R + / \ + B B + */ + this->rotateLeft(currentparentParent); + this->rotateLeft(currentparentParent); + if (currentparentParent->rightChild != nullptr) { + this->rebalanceRedNode(currentparentParent->rightChild); + } + } + else { + /* + B + / \ + R X + / \ + B B + */ + this->rotateRight(currentparentParent); + this->rotateRight(currentparentParent); + if (currentparentParent->leftChild != nullptr) { + this->rebalanceRedNode(currentparentParent->leftChild); + } + } + } + } + } + + return *this; +} + + + +template +adl::size_t RedBlackTree::size() { + ReadGuard _g {this}; + + adl::size_t count = 0; + for (const auto& it : *this) { + count++; + } + + return count; +} + + +template +adl::size_t RedBlackTree::rangeScan( + const KeyType& lhs, + const KeyType& rhs, + void* data, + bool (*collector) (void* data, const KeyType&, const DataType&) +) { + ReadGuard _g {this}; + return this->root ? doRangeScan(this->root, lhs, rhs, data, collector) : 0; +} + + + +template +void RedBlackTree::cleanup(Node* node) +{ + if (node->leftChild != nullptr) { + cleanup(node->leftChild); + } + if (node->rightChild != nullptr) { + cleanup(node->rightChild); + } + allocator->free(node); +} + + +template +RedBlackTree::Node* RedBlackTree::locateNode(const KeyType& key) { + Node* currentNode = root; + while (currentNode != nullptr) { + if (key == currentNode->key) { + return currentNode; // key found + } + else if (currentNode->key > key) { + currentNode = currentNode->leftChild; // target is less than curr. search from left. + } + else { // key > currentNode->key + currentNode = currentNode->rightChild; // target is more than curr. search from right. + } + } + + return nullptr; // key not found. +} + + +template +adl::size_t RedBlackTree::doRangeScan( + const Node* node, + const KeyType& lhs, + const KeyType& rhs, + void* data, + bool (*collector) (void* data, const KeyType&, const DataType&) +) { + adl::size_t count = 0; + + auto left = node->leftChild; + auto right = node->rightChild; + + if (left && node->key >= lhs) + count += doRangeScan(left, lhs, rhs, data, collector); + + if (node->key >= lhs && rhs >= node->key) { + count++; + if (collector) { + collector(data, node->key, node->data); + } + } + + if (right && rhs >= node->key) + count += doRangeScan(right, lhs, rhs, data, collector); + + return count; +} + + +template +void RedBlackTree::rotateLeft(Node* node) +{ + Node* parent = node->parent; + Node* targetRoot = node->rightChild; + + // rebind root. + if (parent == nullptr) { + this->root = targetRoot; + } + else { + if (node == parent->leftChild) { + parent->leftChild = targetRoot; + } + else { + parent->rightChild = targetRoot; + } + } + targetRoot->parent = parent; + + node->rightChild = targetRoot->leftChild; // child might be nullptr + if (node->rightChild != nullptr) { // only when child is not null then we can bind parent node. + node->rightChild->parent = node; + } + targetRoot->leftChild = node; + node->parent = targetRoot; +} + + +template +void RedBlackTree::rotateRight(Node* node) +{ + Node* parent = node->parent; + Node* targetRoot = node->leftChild; + + if (parent == nullptr) { + this->root = targetRoot; + } + else { + if (node == parent->leftChild) { + parent->leftChild = targetRoot; + } + else { + parent->rightChild = targetRoot; + } + } + targetRoot->parent = parent; + + node->leftChild = targetRoot->rightChild; // child might be nullptr + if (node->leftChild != nullptr) { + node->leftChild->parent = node; + } + targetRoot->rightChild = node; + node->parent = targetRoot; +} + + +template +void RedBlackTree::rebalanceRedNode(Node* node) +{ + Node* currentNode = node; + + // Only when the current node is red is it possible to violate the red-black tree rule + // when the parent node is also red, thus requiring a repair operation. + while (currentNode->color == NodeColor::RED) { + Node* currentparentParent = currentNode->parent; + if (currentparentParent == nullptr) { + // currentNode is root + currentNode->color = NodeColor::BLACK; + break; + } + else if (currentparentParent->color == NodeColor::BLACK) { + // parent is black. no problem! + break; + } + + + // Arriving here indicates that both the parent and target nodes are colored red. + + // If a parent node is red, then it cannot be the root, so a grandparent must exist. + + Node* currentGrandpa = currentparentParent->parent; + + // find uncle (might be null) + Node* uncle = + (currentGrandpa->leftChild != currentparentParent ? + currentGrandpa->leftChild : currentGrandpa->rightChild); + + if (uncle != nullptr && uncle->color == NodeColor::RED) { + + uncle->color = NodeColor::BLACK; + currentparentParent->color = NodeColor::BLACK; + currentGrandpa->color = NodeColor::RED; + + currentNode = currentGrandpa; + } + else { + + if (currentparentParent == currentGrandpa->leftChild) { + if (currentNode == currentparentParent->leftChild) { + this->rotateRight(currentGrandpa); + // recolor + currentparentParent->color = NodeColor::BLACK; + currentGrandpa->color = NodeColor::RED; + } + else { + this->rotateLeft(currentparentParent); + this->rotateRight(currentGrandpa); + // recolor + currentGrandpa->color = NodeColor::RED; + currentNode->color = NodeColor::BLACK; + } + } // if (currentparentParent == currentGrandpa->leftChild) + else { + + if (currentNode == currentparentParent->rightChild) { + this->rotateLeft(currentGrandpa); + // recolor + currentparentParent->color = NodeColor::BLACK; + currentGrandpa->color = NodeColor::RED; + } + else { + this->rotateRight(currentparentParent); + this->rotateLeft(currentGrandpa); + // recolor + currentGrandpa->color = NodeColor::RED; + currentNode->color = NodeColor::BLACK; + } + } // if (currentparentParent != currentGrandpa->leftChild) + + return; + } + } +} + + +template +void RedBlackTree::rebalanceChildren(Node* node) +{ + Node* currentNode = node; + + while (currentNode != this->root) { + Node* currentparentParent = currentNode->parent; + Node* sibling = (currentparentParent->leftChild != currentNode ? + currentparentParent->leftChild : currentparentParent->rightChild); + ChildSide siblingSideToparentParent = + (sibling == currentparentParent->leftChild ? ChildSide::LEFT : ChildSide::RIGHT); + + if (currentparentParent->color == NodeColor::RED) { + + + if (siblingSideToparentParent == ChildSide::RIGHT + && sibling->leftChild->color == NodeColor::BLACK) + { + /* + R + / \ + X B + / \ + B ? + */ + this->rotateLeft(currentparentParent); + break; + } + else if (siblingSideToparentParent == ChildSide::LEFT + && sibling->rightChild->color == NodeColor::BLACK) + { + /* + R + / \ + B X + / \ + ? B + */ + this->rotateRight(currentparentParent); + break; + } + else if (siblingSideToparentParent == ChildSide::RIGHT + && sibling->rightChild->color == NodeColor::BLACK) + { + /* + R + / \ + X B + / \ + R B + */ + currentparentParent->color = NodeColor::BLACK; + sibling->color = NodeColor::RED; + this->rebalanceRedNode(sibling->leftChild); + break; + } + else if (siblingSideToparentParent == ChildSide::LEFT + && sibling->leftChild->color == NodeColor::BLACK) + { + /* + R + / \ + B X + / \ + B R + */ + currentparentParent->color = NodeColor::BLACK; + sibling->color = NodeColor::RED; + this->rebalanceRedNode(sibling->rightChild); + break; + } + else { // sibling has two red child + currentparentParent->color = NodeColor::BLACK; + sibling->color = NodeColor::RED; + if (siblingSideToparentParent == ChildSide::RIGHT) { + sibling->rightChild->color = NodeColor::BLACK; + this->rotateLeft(currentparentParent); + } + else { + sibling->leftChild->color = NodeColor::BLACK; + this->rotateRight(currentparentParent); + } + break; + } + } + else { // parent is black + if (sibling->color == NodeColor::RED) { // sibling is red + /* + B + / \ + X R + / \ + B B + + or her symmatrical counterpart + */ + currentparentParent->color = NodeColor::RED; + sibling->color = NodeColor::BLACK; + if (siblingSideToparentParent == ChildSide::RIGHT) { + + this->rotateLeft(currentparentParent); + } + else { + this->rotateRight(currentparentParent); + } + } + else { // sibling is black + if (sibling->leftChild->color == NodeColor::BLACK + && sibling->rightChild->color == NodeColor::BLACK) + { + /* + B + / \ + X B + / \ + B B + + or her symmatrical counterpart + */ + sibling->color = NodeColor::RED; + currentNode = currentparentParent; + } + else if (siblingSideToparentParent == ChildSide::RIGHT + && sibling->rightChild->color == NodeColor::RED) + { + /* + B + / \ + X B + / \ + ? R + + */ + sibling->rightChild->color = NodeColor::BLACK; + this->rotateLeft(currentparentParent); + break; + } + else if (siblingSideToparentParent == ChildSide::LEFT + && sibling->leftChild->color == NodeColor::RED) + { + /* + B + / \ + B X + / \ + R ? + + */ + sibling->leftChild->color = NodeColor::BLACK; + this->rotateRight(currentparentParent); + break; + } + else if (siblingSideToparentParent == ChildSide::RIGHT) + { + /* + B + / \ + X B + / \ + R B + + */ + sibling->leftChild->color = NodeColor::BLACK; + this->rotateRight(sibling); + this->rotateLeft(currentparentParent); + break; + } + else + { + /* + B + / \ + B X + / \ + B R + + */ + sibling->rightChild->color = NodeColor::BLACK; + this->rotateLeft(sibling); + this->rotateRight(currentparentParent); + break; + } + } + } + } +} + + +template +void RedBlackTree::lock(LockType lockType) { + + locking.access.down(); + + if (lockType == LockType::READ) { + + Genode::Mutex::Guard _g { locking.mutex }; + + if (locking.readerCount++ == 0) { + locking.write.down(); + } + + locking.access.up(); + + } else { // lockType is WRITE + + locking.write.down(); + + } +} + + +template +void RedBlackTree::unlock(LockType lockType) { + + if (lockType == LockType::READ) { + + Genode::Mutex::Guard _g { locking.mutex }; + + if (--locking.readerCount == 0) { + locking.write.up(); + } + + } else { // lockType is WRITE + + locking.access.up(); + locking.write.up(); + + } + +} + + + + +} // namespace adl + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/config.cpp b/projects/sel4test/apps/monkey-mnemosyne/src/adl/config.cpp new file mode 100644 index 0000000..90c09a5 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/config.cpp @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * 设置。 + * + * 创建于 2023年7月2日 上海市嘉定区安亭镇 + */ + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +// modified for Amkos + +#include "./config.h" +#include "./sys/types.h" + +namespace adl { + +Allocator defaultAllocator; + +} + + + + +// by Gemini 3.0 Pro + +// ------------------------------------------------------------- +// 全局 operator delete 重载 +// ------------------------------------------------------------- +// 因为编译选项使用了 -nostdlib,C++ 编译器生成的隐式析构调用 +// 找不到标准库的 delete,必须手动链接到 libc 的 free。 + +extern "C" void free(void* ptr); // 声明 C 库的 free 函数 + +void operator delete(void* ptr) noexcept { + if (ptr) { + free(ptr); + } +} + +// 既然你在写 C++,为了保险起见,建议把 operator new 也加上, +// 否则万一哪里隐式用了 new 也会报错。 +extern "C" void* malloc(unsigned long size); + +void* operator new(unsigned long size) { + return malloc(size); +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/config.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/config.h new file mode 100644 index 0000000..624834f --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/config.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * 设置。 + * + * 创建于 2023年7月2日 上海市嘉定区安亭镇 + */ + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +// modified for Amkos + +#pragma once + +#include "./Allocator.h" + + +namespace adl { + +extern Allocator defaultAllocator; + +} + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/endian.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/endian.h new file mode 100644 index 0000000..a4764ce --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/endian.h @@ -0,0 +1,144 @@ +// 124033910070 GTY +// gongty [at] tongji [dot] edu [dot] cn + +// created on 2024.11.25 +// at Jiangchuan, Minhang, Shanghai + +// implements: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html + + +#pragma once + +#include "./stdint.h" + + +#ifndef __BYTE_ORDER__ + #error "__BYTE_ORDER__ undefined. Compiler you are using is not supported by us." +#endif + +#ifndef BYTE_ORDER + #define BYTE_ORDER __BYTE_ORDER__ +#endif + +#ifndef BIG_ENDIAN + #define BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#endif + +#ifndef LITTLE_ENDIAN + #define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#endif + +#if ((BYTE_ORDER != BIG_ENDIAN) && (BYTE_ORDER != LITTLE_ENDIAN)) + #error "Strange system. Our system won't work on your device." +#endif + + +#if defined(_BYTEORDER_FUNC_DEFINED) +#error "Byteorder functions defined elsewhere. " \ + "You should include this header before any other." +#endif + +#define _BYTEORDER_FUNC_DEFINED + + +namespace adl { + + +inline uint16_t be16toh(uint16_t x) { +#if BYTE_ORDER == BIG_ENDIAN + return x; +#else // BYTE_ORDER == LITTLE_ENDIAN + return __builtin_bswap16(x); +#endif +} + + +inline uint32_t be32toh(uint32_t x) { +#if BYTE_ORDER == BIG_ENDIAN + return x; +#else // BYTE_ORDER == LITTLE_ENDIAN + return __builtin_bswap32(x); +#endif + +} +inline int32_t be32toh(int32_t x) { + return (int32_t) be32toh((uint32_t) x); +} + + +inline uint64_t be64toh(uint64_t x) { +#if BYTE_ORDER == BIG_ENDIAN + return x; +#else // BYTE_ORDER == LITTLE_ENDIAN + return __builtin_bswap64(x); +#endif + +} + + +inline int64_t be64toh(int64_t x) { + return (int64_t) be64toh((uint64_t) x); +} + + + +inline uint16_t htobe16(uint16_t x) { + return be16toh(x); +} +inline int32_t htobe32(int32_t x) { + return be32toh(x); +} +inline uint32_t htobe32(uint32_t x) { + return be32toh(x); +} +inline int64_t htobe64(int64_t x) { + return be64toh(x); +} +inline uint64_t htobe64(uint64_t x) { + return be64toh(x); +} + + + +inline uint16_t htole16(uint16_t x) { +#if BYTE_ORDER == BIG_ENDIAN + return __builtin_bswap16(x); +#else // BYTE_ORDER == LITTLE_ENDIAN + return x; +#endif + +} + + +inline uint32_t htole32(uint32_t x) { +#if BYTE_ORDER == BIG_ENDIAN + return __builtin_bswap32(x); +#else // BYTE_ORDER == LITTLE_ENDIAN + return x; +#endif +} + + +inline uint64_t htole64(uint64_t x) { +#if BYTE_ORDER == BIG_ENDIAN + return __builtin_bswap64(x); +#else // BYTE_ORDER == LITTLE_ENDIAN + return x; +#endif +} + + +inline uint16_t le16toh(uint16_t x) { + return htole16(x); +} + +inline uint32_t le32toh(uint32_t x) { + return htole32(x); +} + +inline uint64_t le64toh(uint64_t x) { + return htole64(x); +} + + +} // namespace adl diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/recursive_mutex b/projects/sel4test/apps/monkey-mnemosyne/src/adl/recursive_mutex new file mode 100644 index 0000000..657752f --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/recursive_mutex @@ -0,0 +1,84 @@ +/* + recursive lock + + Created on 2025.6.22 at Minhang + By gongty +*/ + +#pragma once + +#include +#include +#include +#include + +namespace adl { + + +class recursive_mutex { +public: + adl::int64_t count = 0; + Genode::Mutex countMutex; + Genode::Thread* owner; // only trusted when count is not 0. + + Genode::Semaphore waitSemaphore {1}; // used to wait for lock. + +public: + recursive_mutex() { + + } + + recursive_mutex(const recursive_mutex&) = delete; + recursive_mutex& operator=(const recursive_mutex&) = delete; + + + void lock() { + while (!try_lock()) + waitSemaphore.down(); + }; + + + bool try_lock() { + Genode::Mutex::Guard _g {countMutex}; + if (count > 0 && Genode::Thread::myself() == owner) { + count++; + return true; + } + else if (count == 0) { + owner = Genode::Thread::myself(); + count++; + return true; + } + else { + return false; + } + }; + + + void unlock() { + Genode::Mutex::Guard _g {countMutex}; + if (count > 0) { + count--; + if (count == 0) { + waitSemaphore.up(); + } + } + }; + + + class guard { + protected: + recursive_mutex& _mutex; + public: + explicit guard(recursive_mutex& mutex) : _mutex(mutex) { + _mutex.lock(); + }; + + ~guard() { + _mutex.unlock(); + }; + }; + +}; + +}; diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/stdarg.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/stdarg.h new file mode 100644 index 0000000..e9fcb71 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/stdarg.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * 可变参数。 + * 创建于 2022年7月13日。 + * + * 参考: + * https://pubs.opengroup.org/onlinepubs/9699919799/ + * https://cplusplus.com/reference/cstdarg/ + */ + +#pragma once + +#include // for NULL. + + +// 参考 linux5.19 include/linux/stdarg.h +// 原来 gcc 编译期已经实现了这个啊... 害得我思考半天怎么写... + +typedef __builtin_va_list va_list; + +#define va_start(ap, argN) __builtin_va_start(ap, argN) + +#define va_copy(dest, src) __builtin_va_copy(dest, src) + +#define va_arg(ap, type) __builtin_va_arg(ap, type) + +#define va_end(ap) __builtin_va_end(ap) diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/stdbool.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/stdbool.h new file mode 100644 index 0000000..5ec6207 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/stdbool.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * 为 C 语言提供 bool 数据类型支持。 + */ + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +#pragma once + +#ifndef __cplusplus + #ifndef __bool_true_false_are_defined + #define bool _Bool + #define true 1 + #define false 0 + #define __bool_true_false_are_defined 1 + #endif +#endif diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/stddef.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/stddef.h new file mode 100644 index 0000000..8fdc202 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/stddef.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + + stddef header + + 创建于 2023年2月12日 江西省上饶市玉山县 + +*/ + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +#pragma once + +#define offsetof(type, member) ((unsigned long) &((type*)0)->member) + +namespace adl { + +using nullptr_t = decltype(nullptr); + +} // namespace adl diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/stdint.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/stdint.h new file mode 100644 index 0000000..c8a0659 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/stdint.h @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * 整数类型头文件。 + * 创建于 2022年7月2日。 + */ + +#pragma once + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +// modified for Amkos + +namespace adl { + +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +// 拥有最大宽度的整数类型。 +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + +#ifndef intptr_t +typedef int64_t intptr_t; +#endif + +#ifndef uintptr_t +typedef uint64_t uintptr_t; +#endif + +} + + +// https://github.com/openbsd/src/blob/master/sys/sys/stdint.h + +/* + * 7.18.2 Limits of specified-width integer types. + * + * The following object-like macros specify the minimum and maximum limits + * of integer types corresponding to the typedef names defined above. + */ + +/* 7.18.2.1 Limits of exact-width integer types */ +#define INT8_MIN (-0x7f - 1) +#define INT16_MIN (-0x7fff - 1) +#define INT32_MIN (-0x7fffffff - 1) +#define INT64_MIN (-0x7fffffffffffffffLL - 1) + +#define INT8_MAX 0x7f +#define INT16_MAX 0x7fff +#define INT32_MAX 0x7fffffff +#define INT64_MAX 0x7fffffffffffffffLL + +#define UINT8_MAX 0xff +#define UINT16_MAX 0xffff +#define UINT32_MAX 0xffffffffU +#define UINT64_MAX 0xffffffffffffffffULL + +/* 7.18.2.4 Limits of integer types capable of holding object pointers */ +#ifdef __LP64__ +#define INTPTR_MIN (-0x7fffffffffffffffL - 1) +#define INTPTR_MAX 0x7fffffffffffffffL +#define UINTPTR_MAX 0xffffffffffffffffUL +#else +#define INTPTR_MIN (-0x7fffffffL - 1) +#define INTPTR_MAX 0x7fffffffL +#define UINTPTR_MAX 0xffffffffUL +#endif + +/* 7.18.2.5 Limits of greatest-width integer types */ +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/string.cpp b/projects/sel4test/apps/monkey-mnemosyne/src/adl/string.cpp new file mode 100644 index 0000000..305e652 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/string.cpp @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * 字符串处理函数。 + * 创建于 2022年7月4日。 + */ + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +// modified for Amkos + +#include + +namespace adl { + +char* strcpy(char* dest, const char* src) { + char* pDest = dest; + const char* pSrc = src; + while ((*pDest++ = *pSrc++) != '\0') + ; + + return dest; +} + +char* strncpy(char* dest, const char* src, size_t count) { + char* pDest = dest; + const char* pSrc = src; + + while (pDest < ((char*) dest) + count) { + *pDest = *pSrc; + if (*pDest == '\0') { + break; + } else { + ++pDest; + ++pSrc; + } + } + + *pDest = '\0'; + + return dest; +} + +char* strcat(char* dest, const char* src) { + strcpy(dest + strlen(dest), src); + return dest; +} + +char* strncat(char* dest, const char* src, size_t count) { + strncpy(dest + strlen(dest), src, count); + return dest; +} + +size_t strlen(const char* str) { + // 参考 glibc 的实现方式。 + // 解释见:https://blog.csdn.net/m0_62405272/article/details/125600719 + + /* 先将指针与字节对齐。 + 将指针转换成 long 这种操作在有些平台会报错。注意,不等号的优先级高于与运算。 */ + const char* charPtr = str; + while (((long) charPtr & (sizeof(long) - 1)) != 0) { + if (*charPtr == '\0') { + return charPtr - str; + } else { + ++charPtr; + } + } + + const unsigned long* longPtr = (const unsigned long*) charPtr; + + unsigned long highMagic = 0x80808080L; + unsigned long lowMagic = 0x01010101L; + if (sizeof(long) > 4) { + highMagic = ((highMagic << 16) << 16) | highMagic; + lowMagic = ((lowMagic << 16) << 16) | lowMagic; + } + // 暂不考虑 long 的宽度超过 8 字节的情况。 + + while (true) { + const unsigned long& longWords = *longPtr++; + if (((longWords - lowMagic) & ~longWords & highMagic) != 0) { + auto prevLongPtr = longPtr - 1; + const char* p = (const char*) prevLongPtr; + while (p < ((const char*) prevLongPtr) + sizeof(long)) { + if (*p == '\0') { + return p - str; + } else { + ++p; + } + } + } + } +} + +int strcmp(const char* lhs, const char* rhs) { + const char* pLhs = lhs; + const char* pRhs = rhs; + + while (*pLhs == *pRhs) { + if (*pLhs == '\0') { + return 0; + } else { + ++pLhs; + ++pRhs; + } + } + + return *(const unsigned char*) pLhs - *(const unsigned char*) pRhs; +} + +int strncmp(const char* lhs, const char* rhs, size_t count) { + const char* pLhs = lhs; + const char* pRhs = rhs; + + while (pLhs < lhs + count) { + if (*pLhs != *pRhs) { + return *(const unsigned char*) pLhs - *(const unsigned char*) pRhs; + } + else if (*pLhs == '\0') { + return 0; + } + else { + ++pLhs; + ++pRhs; + } + } + + return 0; +} + +char* strchr(const char* str, int ch) { + char b = (char) ch; + const char* p = str; + while (*p != '\0') { + if (b == *p) { + return (char*) p; + } else { + ++p; + } + } + + return NULL; +} + +char* strrchr(const char* str, int ch) { + char b = (char) ch; + const char* p = str + strlen(str) - 1; + + while (str <= p) { + if (b == *p) { + return (char*) p; + } else { + --p; + } + } + + return NULL; +} + +size_t strspn(const char* dest, const char* src) { + bool tar[256]; // 暂不考虑对 C 语言的兼容问题。 + memset(tar, 0, sizeof(tar)); + + const unsigned char* p = (const unsigned char*) src; + while (*p != '\0') { + tar[*p++] = true; + } + + p = (const unsigned char*) dest; + while (tar[*p++]) + ; + + return p - (const unsigned char*) dest - 1; +} + +size_t strcspn(const char* dest, const char* src) { + bool tar[256]; // 暂不考虑对 C 语言的兼容问题。 + memset(tar, 0xff, sizeof(tar)); + + const unsigned char* p = (const unsigned char*) src; + while (*p != '\0') { + tar[*p++] = false; + } + + p = (const unsigned char*) dest; + while (tar[*p++]) + ; + + return p - (const unsigned char*) dest - 1; +} + +char* strpbrk(const char* dest, const char* breakset) { + bool tar[256]; + memset(tar, 0, sizeof(tar)); +; + const unsigned char* p = (const unsigned char*) breakset; + while (*p != '\0') { + tar[*p++] = true; + } + + p = (const unsigned char*) dest; + while (*p != '\0') { + if (tar[*p]) { + return (char*) p; + } else { + ++p; + } + } + + return NULL; +} + +char* strstr(const char* str, const char* substr) { + size_t lenOfSubstr = strlen(substr); + size_t lenOfStr = strlen(str); + const char* pStr = str; + while (lenOfSubstr + (pStr - str) <= lenOfStr) { + if (strncmp(pStr, substr, lenOfSubstr) == 0) { + return (char*) pStr; + } else { + ++pStr; + } + } + + return NULL; + + // 注意:现在这种算法效率很低。可以考虑研究更好的算法。 +} + +char* strtok(char* str, const char* delim) { + bool tar[256]; + memset(tar, 0, sizeof(tar)); + + const unsigned char* p = (const unsigned char*) delim; + while (*p != 0) { + tar[*p++] = true; + } + + static unsigned char* searchPtr = (unsigned char*) str; + if (str != NULL) { + searchPtr = (unsigned char*) str; + } + + char* pToken = NULL; + while (*searchPtr != '\0') { + if (p[*searchPtr]) { + if (pToken != NULL) { + *searchPtr = '\0'; + ++searchPtr; + return pToken; + } + } else if (pToken == NULL) { + pToken = (char*) str; + } + + ++searchPtr; + } + + return NULL; +} + +void* memchr(const void* ptr, int ch, size_t count) { + auto* cPtr = (const unsigned char*) ptr; + auto b = (unsigned char) ch; + while (cPtr < ((const unsigned char*) ptr) + count) { + if (b == *cPtr) { + return (void*) cPtr; + } else { + ++cPtr; + } + } + + return nullptr; +} + +int memcmp(const void* lhs, const void* rhs, size_t count) { + auto pLhs = (const unsigned char*) lhs; + auto pRhs = (const unsigned char*) rhs; + while (pRhs < ((const unsigned char*) rhs) + count) { + if (*pLhs != *pRhs) { + return *pLhs - *pRhs; + } else { + ++pLhs; + ++pRhs; + } + } + + return 0; +} + +void* memset(void* dest, int ch, size_t count) { + char b = (char) ch; + char* p = (char*) dest; + while (p < ((char*) dest) + count) { + *p++ = b; + } + + return dest; +} + +/** + * 快速内存拷贝。调用时,假设传入的内存可以互相对齐,否则性能会降低。 + * + * 传统内存拷贝时,一次只能拷贝1个字节,速度较慢。 + * 本方法在观察到源地址和目的地址低 n 位相同时, + * 每次拷贝一个 CPU 字长。其中,n 为机器字长除以字节长度 8。 + * + * 例如,当 src 为 0x345A, dest 为 0x987A 时, + * src 和 dest 的低 4 位相同。当机器字长为 4 位时,可以先使用单字节拷贝方法, + * 将 src 和 dest 与内存对齐。将 src 和 dest 分别对齐到 0b..000, + * 即可借助机器字长的优势,一次性拷贝更多字符。 + * + * 覆盖问题:memcpy 本身不允许拷贝区间互相覆盖。因此,本方法不保证此方面安全性。 + * 短拷贝:当 src 和 dest 的距离小于一个寄存器的长度时,根本无法互相对齐。因此,不存在此问题。 + * + * @param dest 拷贝目标地址。需要与 src 距离对齐,不要求与内存单元对齐。 + * @param src 拷贝源地址。 + * @param count 拷贝字符总数。 + * @return dest + */ +static inline void* memcpy_aligned(void* dest, const void* src, size_t count) { + size_t remain = count; + char* pDest = (char*) dest; + char* pSrc = (char*) src; + while (pSrc < ((char*) src) + count && (uintptr_t(pSrc) & (sizeof(long) - 1))) { + *pDest++ = *pSrc++; + remain--; + } + + while (remain >= sizeof(long)) { + *(long*) pDest = *(long*) pSrc; + pSrc += sizeof(long); + pDest += sizeof(long); + remain -= sizeof(long); + } + + while (remain) { + *pDest++ = *pSrc++; + remain--; + } + + return dest; + +} + + +void* memcpy(void* dest, const void* src, size_t count) { + + // 如果 src 和 dest 可以互相对齐,则使用快速拷贝。 + if (!((uintptr_t(dest) ^ uintptr_t(src)) & (sizeof(long) - 1))) { + return memcpy_aligned(dest, src, count); + } + + // 单字节拷贝。 + char* pDest = (char*) dest; + char* pSrc = (char*) src; + while (pSrc < ((char*) src) + count) { + *pDest++ = *pSrc++; + } + + return dest; +} + +} + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/string.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/string.h new file mode 100644 index 0000000..84fd93c --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/string.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * 字符串处理函数头文件。 + * 创建于 2022年7月4日。 + * + * 参考: + * cppreference.com. Null-terminated byte strings + * https://en.cppreference.com/w/c/string/byte + */ + +#pragma once + +#include "./sys/types.h" + + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +// modified for Amkos + +namespace adl { + +char* strcpy(char* dest, const char* src); +char* strncpy(char* dest, const char* src, size_t count); +char* strcat(char* dest, const char* src); +char* strncat(char* dest, const char* src, size_t count); +size_t strlen(const char* str); +int strcmp(const char* lhs, const char* rhs); +int strncmp(const char* lhs, const char* rhs, size_t count); +char* strchr(const char* str, int ch); +char* strrchr(const char* str, int ch); +size_t strspn(const char* dest, const char* src); +size_t strcspn(const char* dest, const char* src); +char* strpbrk(const char* dest, const char* breakset); +char* strstr(const char* str, const char* substr); +char* strtok(char* str, const char* delim); + +void* memchr(const void* ptr, int ch, size_t count); +int memcmp(const void* lhs, const void* rhs, size_t count); +void* memset(void* dest, int ch, size_t count); +void* memcpy(void* dest, const void* src, size_t count); + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/sys/types.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/sys/types.h new file mode 100644 index 0000000..0e0f9d7 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/sys/types.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MulanPSL-2.0 + +/* + * 数据类型定义。 + * 创建于 2022年7月2日。 + */ + +// forked from yros stdlib. +// github.com/FlowerBlackG/YurongOS + +// modified for Amkos + +#pragma once + +#include "../stdint.h" + +/** 文件结尾。 */ +#ifndef EOF + #define EOF -1 +#endif + +/** 空指针。 */ +#ifndef NULL + #define NULL 0 +#endif + +#ifdef ADL_DEFINE_GCC_SHORT_MACROS +/** 结构体紧凑。 */ +#ifndef __packed + #define __packed __attribute__((packed)) +#endif + +#ifndef __no_return + #define __no_return __attribute__((__noreturn__)) +#endif + +/** 令函数不保存 rsp 和 rbp。 */ +#ifndef __omit_frame_pointer + #define __omit_frame_pointer __attribute__((optimize("omit-frame-pointer"))) +#endif + +/** 强制内联。 */ +#ifndef __force_inline + #define __force_inline __attribute__((always_inline)) inline +#endif + +/** 内联汇编。 */ +#ifndef __asm + #define __asm __asm__ __volatile__ +#endif + +/** 指定代码存放的区域。 */ +#ifndef __section + #define __section(name) __attribute__((section(name))) +#endif +#endif +namespace adl { + +typedef unsigned long size_t; + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/type_traits b/projects/sel4test/apps/monkey-mnemosyne/src/adl/type_traits new file mode 100644 index 0000000..1296c0f --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/type_traits @@ -0,0 +1,27 @@ +// 124033910070 GTY +// gongty [at] tongji [dot] edu [dot] cn + +// created on 2024.11.25 +// at Jiangchuan, Minhang, Shanghai + +// implements: https://cplusplus.com/reference/type_traits + + +#pragma once + +namespace adl { + + +struct true_type { + static constexpr bool value = true; + constexpr operator bool() const noexcept { return value; } +}; + +struct false_type { + static constexpr bool value = false; + constexpr operator bool() const noexcept { return value; } +}; + + +} // namespace adl + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/utility b/projects/sel4test/apps/monkey-mnemosyne/src/adl/utility new file mode 100644 index 0000000..1830499 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/utility @@ -0,0 +1,43 @@ +// 124033910070 GTY +// gongty [at] tongji [dot] edu [dot] cn + +// created on 2024.11.25 +// at Jiangchuan, Minhang, Shanghai + +// reference: std: + +#pragma once + +namespace adl { + + +template +struct pair { + U first; + V second; + pair(const U& first = U(), const V& second = V()) : first(first), second(second) {} +}; + + +template +inline pair make_pair(const U& first, const V& second) { + return pair {first, second}; +} + + +template +struct ref_pair { + U& first; + V& second; + ref_pair(U& first, V& second) : first(first), second(second) {} +}; + + +template +inline ref_pair make_ref_pair(U& first, V& second) { + return ref_pair {first, second}; +} + + + +} // namespace adl diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/wl_list.cpp b/projects/sel4test/apps/monkey-mnemosyne/src/adl/wl_list.cpp new file mode 100644 index 0000000..0ff6867 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/wl_list.cpp @@ -0,0 +1,97 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +/* + Modified from Wayland + https://gitlab.freedesktop.org/wayland/wayland + + For Amkos ADL use. + +*/ + + +#include + + +void +wl_list_init(struct wl_list *list) +{ + list->prev = list; + list->next = list; +} + +void +wl_list_insert(struct wl_list *list, struct wl_list *elm) +{ + elm->prev = list; + elm->next = list->next; + list->next = elm; + elm->next->prev = elm; +} + +void +wl_list_remove(struct wl_list *elm) +{ + elm->prev->next = elm->next; + elm->next->prev = elm->prev; + elm->next = nullptr; + elm->prev = nullptr; +} + +int +wl_list_length(const struct wl_list *list) +{ + struct wl_list *e; + int count; + + count = 0; + e = list->next; + while (e != list) { + e = e->next; + count++; + } + + return count; +} + +int +wl_list_empty(const struct wl_list *list) +{ + return list->next == list; +} + +void +wl_list_insert_list(struct wl_list *list, struct wl_list *other) +{ + if (wl_list_empty(other)) + return; + + other->next->prev = list; + other->prev->next = list->next; + list->next->prev = other->prev; + list->next = other->next; +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/wl_list.h b/projects/sel4test/apps/monkey-mnemosyne/src/adl/wl_list.h new file mode 100644 index 0000000..8e5b8a7 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/wl_list.h @@ -0,0 +1,324 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +/* + Modified from Wayland + https://gitlab.freedesktop.org/wayland/wayland + + For Amkos ADL use. + +*/ + +#pragma once + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#define WL_TYPEOF(expr) typeof(expr) +#else +#define WL_TYPEOF(expr) __typeof__(expr) +#endif + + +/** \class wl_list + * + * \brief Doubly-linked list + * + * On its own, an instance of `struct wl_list` represents the sentinel head of + * a doubly-linked list, and must be initialized using wl_list_init(). + * When empty, the list head's `next` and `prev` members point to the list head + * itself, otherwise `next` references the first element in the list, and `prev` + * refers to the last element in the list. + * + * Use the `struct wl_list` type to represent both the list head and the links + * between elements within the list. Use wl_list_empty() to determine if the + * list is empty in O(1). + * + * All elements in the list must be of the same type. The element type must have + * a `struct wl_list` member, often named `link` by convention. Prior to + * insertion, there is no need to initialize an element's `link` - invoking + * wl_list_init() on an individual list element's `struct wl_list` member is + * unnecessary if the very next operation is wl_list_insert(). However, a + * common idiom is to initialize an element's `link` prior to removal - ensure + * safety by invoking wl_list_init() before wl_list_remove(). + * + * Consider a list reference `struct wl_list foo_list`, an element type as + * `struct element`, and an element's link member as `struct wl_list link`. + * + * The following code initializes a list and adds three elements to it. + * + * \code + * struct wl_list foo_list; + * + * struct element { + * int foo; + * struct wl_list link; + * }; + * struct element e1, e2, e3; + * + * wl_list_init(&foo_list); + * wl_list_insert(&foo_list, &e1.link); // e1 is the first element + * wl_list_insert(&foo_list, &e2.link); // e2 is now the first element + * wl_list_insert(&e2.link, &e3.link); // insert e3 after e2 + * \endcode + * + * The list now looks like [e2, e3, e1]. + * + * The `wl_list` API provides some iterator macros. For example, to iterate + * a list in ascending order: + * + * \code + * struct element *e; + * wl_list_for_each(e, foo_list, link) { + * do_something_with_element(e); + * } + * \endcode + * + * See the documentation of each iterator for details. + * \sa http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/linux/list.h + */ +struct wl_list { + /** Previous list element */ + struct wl_list *prev; + /** Next list element */ + struct wl_list *next; +}; + + + + +/** + * Initializes the list. + * + * \param list List to initialize + * + * \memberof wl_list + */ +void +wl_list_init(struct wl_list *list); + +/** + * Inserts an element into the list, after the element represented by \p list. + * When \p list is a reference to the list itself (the head), set the containing + * struct of \p elm as the first element in the list. + * + * \note If \p elm is already part of a list, inserting it again will lead to + * list corruption. + * + * \param list List element after which the new element is inserted + * \param elm Link of the containing struct to insert into the list + * + * \memberof wl_list + */ +void +wl_list_insert(struct wl_list *list, struct wl_list *elm); + +/** + * Removes an element from the list. + * + * \note This operation leaves \p elm in an invalid state. + * + * \param elm Link of the containing struct to remove from the list + * + * \memberof wl_list + */ +void +wl_list_remove(struct wl_list *elm); + +/** + * Determines the length of the list. + * + * \note This is an O(n) operation. + * + * \param list List whose length is to be determined + * + * \return Number of elements in the list + * + * \memberof wl_list + */ +int +wl_list_length(const struct wl_list *list); + +/** + * Determines if the list is empty. + * + * \param list List whose emptiness is to be determined + * + * \return 1 if empty, or 0 if not empty + * + * \memberof wl_list + */ +int +wl_list_empty(const struct wl_list *list); + +/** + * Inserts all of the elements of one list into another, after the element + * represented by \p list. + * + * \note This leaves \p other in an invalid state. + * + * \param list List element after which the other list elements will be inserted + * \param other List of elements to insert + * + * \memberof wl_list + */ +void +wl_list_insert_list(struct wl_list *list, struct wl_list *other); + +/** + * Retrieves a pointer to a containing struct, given a member name. + * + * This macro allows "conversion" from a pointer to a member to its containing + * struct. This is useful if you have a contained item like a wl_list, + * wl_listener, or wl_signal, provided via a callback or other means, and would + * like to retrieve the struct that contains it. + * + * To demonstrate, the following example retrieves a pointer to + * `example_container` given only its `destroy_listener` member: + * + * \code + * struct example_container { + * struct wl_listener destroy_listener; + * // other members... + * }; + * + * void example_container_destroy(struct wl_listener *listener, void *data) + * { + * struct example_container *ctr; + * + * ctr = wl_container_of(listener, ctr, destroy_listener); + * // destroy ctr... + * } + * \endcode + * + * \note `sample` need not be a valid pointer. A null or uninitialised pointer + * is sufficient. + * + * \param ptr Valid pointer to the contained member + * \param sample Pointer to a struct whose type contains \p ptr + * \param member Named location of \p ptr within the \p sample type + * + * \return The container for the specified pointer + */ +#define wl_container_of(ptr, sample, member) \ + (WL_TYPEOF(sample))((char *)(ptr) - \ + offsetof(WL_TYPEOF(*sample), member)) + +/** + * Iterates over a list. + * + * This macro expresses a for-each iterator for wl_list. Given a list and + * wl_list link member name (often named `link` by convention), this macro + * assigns each element in the list to \p pos, which can then be referenced in + * a trailing code block. For example, given a wl_list of `struct message` + * elements: + * + * \code + * struct message { + * char *contents; + * wl_list link; + * }; + * + * struct wl_list *message_list; + * // Assume message_list now "contains" many messages + * + * struct message *m; + * wl_list_for_each(m, message_list, link) { + * do_something_with_message(m); + * } + * \endcode + * + * \param pos Cursor that each list element will be assigned to + * \param head Head of the list to iterate over + * \param member Name of the link member within the element struct + * + * \relates wl_list + */ +#define wl_list_for_each(pos, head, member) \ + for (pos = wl_container_of((head)->next, pos, member); \ + &pos->member != (head); \ + pos = wl_container_of(pos->member.next, pos, member)) + +/** + * Iterates over a list, safe against removal of the list element. + * + * \note Only removal of the current element, \p pos, is safe. Removing + * any other element during traversal may lead to a loop malfunction. + * + * \sa wl_list_for_each() + * + * \param pos Cursor that each list element will be assigned to + * \param tmp Temporary pointer of the same type as \p pos + * \param head Head of the list to iterate over + * \param member Name of the link member within the element struct + * + * \relates wl_list + */ +#define wl_list_for_each_safe(pos, tmp, head, member) \ + for (pos = wl_container_of((head)->next, pos, member), \ + tmp = wl_container_of((pos)->member.next, tmp, member); \ + &pos->member != (head); \ + pos = tmp, \ + tmp = wl_container_of(pos->member.next, tmp, member)) + +/** + * Iterates backwards over a list. + * + * \sa wl_list_for_each() + * + * \param pos Cursor that each list element will be assigned to + * \param head Head of the list to iterate over + * \param member Name of the link member within the element struct + * + * \relates wl_list + */ +#define wl_list_for_each_reverse(pos, head, member) \ + for (pos = wl_container_of((head)->prev, pos, member); \ + &pos->member != (head); \ + pos = wl_container_of(pos->member.prev, pos, member)) + +/** + * Iterates backwards over a list, safe against removal of the list element. + * + * \note Only removal of the current element, \p pos, is safe. Removing + * any other element during traversal may lead to a loop malfunction. + * + * \sa wl_list_for_each() + * + * \param pos Cursor that each list element will be assigned to + * \param tmp Temporary pointer of the same type as \p pos + * \param head Head of the list to iterate over + * \param member Name of the link member within the element struct + * + * \relates wl_list + */ +#define wl_list_for_each_reverse_safe(pos, tmp, head, member) \ + for (pos = wl_container_of((head)->prev, pos, member), \ + tmp = wl_container_of((pos)->member.prev, tmp, member); \ + &pos->member != (head); \ + pos = tmp, \ + tmp = wl_container_of(pos->member.prev, tmp, member)) + + + \ No newline at end of file -- Gitee From ad5bf7922c927fb5784dd0a7576ed0c4b67ef606 Mon Sep 17 00:00:00 2001 From: Flower Black Date: Wed, 3 Dec 2025 21:52:18 +0800 Subject: [PATCH 013/133] monkey libs. --- .../apps/monkey-mnemosyne/CMakeLists.txt | 3 + .../src/adl/collections/RedBlackTree.hpp | 107 +-- .../apps/monkey-mnemosyne/src/base/log.h | 60 ++ .../apps/monkey-mnemosyne/src/monkey/Status.h | 29 + .../apps/monkey-mnemosyne/src/monkey/config.h | 12 + .../monkey-mnemosyne/src/monkey/crypto/rc4.cc | 86 ++ .../monkey-mnemosyne/src/monkey/crypto/rc4.h | 47 ++ .../apps/monkey-mnemosyne/src/monkey/log.h | 22 + .../src/monkey/net/IP4Addr.cc | 53 ++ .../monkey-mnemosyne/src/monkey/net/IP4Addr.h | 50 ++ .../src/monkey/net/Socket4.cc | 101 +++ .../monkey-mnemosyne/src/monkey/net/Socket4.h | 67 ++ .../src/monkey/net/SunflowerLounge.h | 41 + .../monkey-mnemosyne/src/monkey/net/TcpIo.cc | 48 ++ .../monkey-mnemosyne/src/monkey/net/TcpIo.h | 75 ++ .../monkey-mnemosyne/src/monkey/net/config.h | 14 + .../monkey-mnemosyne/src/monkey/net/hexview.h | 145 ++++ .../src/monkey/net/protocol.h | 24 + .../net/protocol/Protocol1Connection.cc | 753 ++++++++++++++++++ .../monkey/net/protocol/Protocol1Connection.h | 275 +++++++ .../net/protocol/Protocol2Connection.cc | 251 ++++++ .../monkey/net/protocol/Protocol2Connection.h | 89 +++ .../monkey/net/protocol/ProtocolConnection.cc | 333 ++++++++ .../monkey/net/protocol/ProtocolConnection.h | 222 ++++++ .../net/protocol/ProtocolConnectionDock.h | 31 + .../src/monkey/net/protocol/defines.cc | 91 +++ .../src/monkey/net/protocol/defines.h | 99 +++ 27 files changed, 3038 insertions(+), 90 deletions(-) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/base/log.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/Status.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/config.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/crypto/rc4.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/crypto/rc4.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/log.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/IP4Addr.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/IP4Addr.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/SunflowerLounge.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/config.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hexview.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol1Connection.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol1Connection.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol2Connection.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol2Connection.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnection.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnection.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnectionDock.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/defines.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/defines.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt index 75e3008..e99e83a 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt +++ b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt @@ -37,6 +37,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) # GCC(g++) 编译选项。 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostartfiles") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-pic") @@ -50,6 +51,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-use-cxa-atexit") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1") # 适当优化。 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-sized-deallocation") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unused-function") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp index b0f5ccf..de06f5f 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp @@ -11,13 +11,19 @@ // 2024.11.6: modified for Amkos -#include #include "../Allocator.h" #include "../config.h" #include "../sys/types.h" -#include #include + +static void __die() { + printf("die RedBlackTree.hpp\n"); + while (1) + ; +} + + namespace adl { @@ -115,7 +121,7 @@ public: public: - struct RuntimeError : Genode::Exception {}; + protected: @@ -207,34 +213,6 @@ protected: adl::Allocator* allocator = nullptr; - - /** - * Locking for multi-thread access. - */ - struct { - Genode::Mutex mutex; - - int readerCount = 0; - - Genode::Semaphore access {1}; - Genode::Semaphore write {1}; - } locking; - - - struct ReadGuard { - RedBlackTree* tree; - ReadGuard(RedBlackTree* t) : tree(t) { tree->lock(LockType::READ); } - ~ReadGuard() { tree->unlock(LockType::READ); } - }; - - - struct WriteGuard { - RedBlackTree* tree; - WriteGuard(RedBlackTree* t) : tree(t) { tree->lock(LockType::WRITE); } - ~WriteGuard() { tree->unlock(LockType::WRITE); } - }; - - }; @@ -341,7 +319,7 @@ RedBlackTree::~RedBlackTree() template void RedBlackTree::clear() { - WriteGuard _g {this}; + if (this->root != nullptr) { this->cleanup(this->root); this->root = nullptr; @@ -352,7 +330,7 @@ void RedBlackTree::clear() template bool RedBlackTree::hasKey(const KeyType& queryKey) { - ReadGuard _g {this}; + return !!locateNode(queryKey); } @@ -360,7 +338,7 @@ bool RedBlackTree::hasKey(const KeyType& queryKey) template DataType& RedBlackTree::getData(const KeyType& key) { - ReadGuard _g {this}; + auto node = locateNode(key); if (node) { @@ -371,7 +349,7 @@ DataType& RedBlackTree::getData(const KeyType& key) #if 0 throw std::runtime_error("could not find your key in the object."); // key not found. #else - throw RuntimeError {}; + __die(); #endif } @@ -383,7 +361,6 @@ DataType& RedBlackTree::operator [] (const KeyType& key) { { - ReadGuard _g {this}; auto node = locateNode(key); if (node) { @@ -394,7 +371,6 @@ DataType& RedBlackTree::operator [] (const KeyType& key) this->setData(key, DataType {}); - ReadGuard _g {this}; return locateNode(key)->data; } @@ -405,7 +381,7 @@ DataType& RedBlackTree::operator [] (const KeyType& key) template DataType RedBlackTree::copyData(const KeyType& key, const DataType* fallback) { - ReadGuard _g {this}; + auto node = locateNode(key); if (node) { @@ -417,7 +393,7 @@ DataType RedBlackTree::copyData(const KeyType& key, const Dat #if 0 throw std::runtime_error("could not find your key in the object."); // key not found. #else - throw RuntimeError {}; + __die(); #endif } @@ -428,7 +404,6 @@ RedBlackTree& RedBlackTree::setData( const DataType& data ) { - WriteGuard _g {this}; Node* currentNode = root; @@ -491,7 +466,6 @@ RedBlackTree& RedBlackTree::removeKey( bool noExcept ) { - WriteGuard _g {this}; Node* currentNode = root; @@ -513,7 +487,7 @@ RedBlackTree& RedBlackTree::removeKey( #if 0 throw std::runtime_error("could not find your key in the object."); // key not found. #else - throw RuntimeError {}; + __die(); #endif } } @@ -947,7 +921,6 @@ RedBlackTree& RedBlackTree::removeKey( template adl::size_t RedBlackTree::size() { - ReadGuard _g {this}; adl::size_t count = 0; for (const auto& it : *this) { @@ -965,7 +938,7 @@ adl::size_t RedBlackTree::rangeScan( void* data, bool (*collector) (void* data, const KeyType&, const DataType&) ) { - ReadGuard _g {this}; + return this->root ? doRangeScan(this->root, lhs, rhs, data, collector) : 0; } @@ -1357,51 +1330,5 @@ void RedBlackTree::rebalanceChildren(Node* node) } -template -void RedBlackTree::lock(LockType lockType) { - - locking.access.down(); - - if (lockType == LockType::READ) { - - Genode::Mutex::Guard _g { locking.mutex }; - - if (locking.readerCount++ == 0) { - locking.write.down(); - } - - locking.access.up(); - - } else { // lockType is WRITE - - locking.write.down(); - - } -} - - -template -void RedBlackTree::unlock(LockType lockType) { - - if (lockType == LockType::READ) { - - Genode::Mutex::Guard _g { locking.mutex }; - - if (--locking.readerCount == 0) { - locking.write.up(); - } - - } else { // lockType is WRITE - - locking.access.up(); - locking.write.up(); - - } - -} - - - - } // namespace adl diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/base/log.h b/projects/sel4test/apps/monkey-mnemosyne/src/base/log.h new file mode 100644 index 0000000..a9f6334 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/base/log.h @@ -0,0 +1,60 @@ +// Mock Genode::log +// by Gemini 3.0 Pro + +#pragma once + +#include + +namespace Genode { + + // --- 1. Helper overloads --- + + // Signed integers + inline void print_arg(int v) { printf("%d", v); } + inline void print_arg(long v) { printf("%ld", v); } + inline void print_arg(long long v) { printf("%lld", v); } + + // Unsigned integers (Fixes "ambiguous overload" errors) + inline void print_arg(unsigned int v) { printf("%u", v); } + inline void print_arg(unsigned long v) { printf("%lu", v); } + inline void print_arg(unsigned long long v) { printf("%llu", v); } + + // Pointers and Strings + inline void print_arg(const char* v) { printf("%s", v); } + inline void print_arg(void* v) { printf("%p", v); } + + // --- 2. Base cases (Fixes "no matching function" errors) --- + + inline void log() { + printf("\n"); + } + + inline void error() { + printf("\n"); + } + + inline void warning() { + printf("\n"); + } + + // --- 3. Recursive variadic templates --- + + template + inline void log(T value, Args... args) { + print_arg(value); + log(args...); // Recurse + } + + template + inline void error(T value, Args... args) { + print_arg(value); + error(args...); // Recurse + } + + template + inline void warning(T value, Args... args) { + print_arg(value); + warning(args...); // Recurse + } + +} \ No newline at end of file diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/Status.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/Status.h new file mode 100644 index 0000000..ab50ef4 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/Status.h @@ -0,0 +1,29 @@ +/* + Monkey Status + + Created on 2025.1.5 + + gongty [at] tongji [dot] edu [dot] cn + +*/ + +#pragma once + +#include + +namespace monkey { + +enum class Status : adl::int32_t { + SUCCESS = 0, + OUT_OF_RESOURCE, + NETWORK_ERROR, + PROTOCOL_ERROR, + INVALID_PARAMETERS, + NOT_FOUND, + NOT_INITIALIZED, + FAILED +}; + +} + + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/config.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/config.h new file mode 100644 index 0000000..7bbd0cb --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/config.h @@ -0,0 +1,12 @@ +/* + + monkey system global config + + by gty + created on 2025.10.30, Shanghai +*/ + + +#pragma once + +#define MONKEY_CFG_ENABLE_DEBUG_LOGS 0 diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/crypto/rc4.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/crypto/rc4.cc new file mode 100644 index 0000000..bf030d7 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/crypto/rc4.cc @@ -0,0 +1,86 @@ +/* + + RC4 + + + Created on 2025.1.6 at Jiangchuan, Minhang, Shanghai + + gongty [at] tongji [dot] edu [dot] cn + feng.yt [at] sjtu [dot] edu [dot] cn + + + Reference: + https://blog.csdn.net/weixin_45582916/article/details/121429688 + +*/ + +#include +#include + + +using namespace adl; + + +static void rc4Init(unsigned char* s, const ByteArray& key) { + int j = 0; + char k[256] = { 0 }; + unsigned char tmp = 0; + for (unsigned int i = 0; i < 256; i++) { + s[i] = (unsigned char) i; + k[i] = (char) key[i % key.size()]; + } + for (unsigned int i = 0; i < 256; i++) { + j = (j + s[i] + k[i]) % 256; + tmp = s[i]; + s[i] = s[j]; + s[j] = tmp; + } +} + + +namespace monkey::crypto { + +ByteArray rc4(const ByteArray& dataIn, const ByteArray& key) { + ByteArray data = dataIn; + rc4Inplace(data, key); + return data; +} + + +void rc4Inplace(ByteArray& data, const ByteArray& key) { + unsigned char s[256]; + rc4Init(s, key); + int i = 0, j = 0, t = 0; + unsigned long k = 0; + unsigned char tmp; + for (k = 0; k < data.size(); k++) { + i = (i + 1) % 256; + j = (j + s[i]) % 256; + tmp = s[i]; + s[i] = s[j]; + s[j] = tmp; + t = (s[i] + s[j]) % 256; + data[k] = data[k] ^ s[t]; + } + +} + + +bool rc4Verify(const ByteArray& key, const ByteArray& challenge, const ByteArray& cipher) { + ByteArray data = rc4(challenge, key); + return data == cipher; +} + + + +int64_t rc4Verify(const ArrayList& keyring, const ByteArray& challenge, const ByteArray& cipher) { + for (size_t i = 0; i < keyring.size(); i++) { + if (rc4Verify(keyring[i], challenge, cipher)) + return int64_t(i); + } + + return -1; +} + + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/crypto/rc4.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/crypto/rc4.h new file mode 100644 index 0000000..b8826ad --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/crypto/rc4.h @@ -0,0 +1,47 @@ +/* + + RC4 + + + Created on 2025.1.6 at Jiangchuan, Minhang, Shanghai + + gongty [at] tongji [dot] edu [dot] cn + feng.yt [at] sjtu [dot] edu [dot] cn + + +*/ + + +#pragma once + +#include +#include + + + +namespace monkey::crypto { + + +adl::ByteArray rc4(const adl::ByteArray& dataIn, const adl::ByteArray& key); + + +void rc4Inplace(adl::ByteArray& data, const adl::ByteArray& key); + + +bool rc4Verify(const adl::ByteArray& key, const adl::ByteArray& challenge, const adl::ByteArray& cipher); + + +/** + * + * @return -1 if not found. + */ +adl::int64_t rc4Verify( + const adl::ArrayList& keyring, + const adl::ByteArray& challenge, + const adl::ByteArray& cipher +); + + + +} + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/log.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/log.h new file mode 100644 index 0000000..1e585c8 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/log.h @@ -0,0 +1,22 @@ +/* + + monkey logging + + by gty + created on 2025.10.30, Shanghai +*/ + +#pragma once + +#include +#include "./config.h" + +#define MONKEY_LOG_INFO(...) Genode::log("[MONKEY][INFO] ", __VA_ARGS__) +#define MONKEY_LOG_WARN(...) Genode::warning("[MONKEY][WARN] ", __VA_ARGS__) +#define MONKEY_LOG_ERROR(...) Genode::error("[MONKEY][ERROR] ", __VA_ARGS__) + +#if MONKEY_CFG_ENABLE_DEBUG_LOGS + #define MONKEY_LOG_DEBUG(...) Genode::log("[MONKEY][DEBUG] ", __VA_ARGS__) +#else + #define MONKEY_LOG_DEBUG(...) do { } while (0) +#endif diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/IP4Addr.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/IP4Addr.cc new file mode 100644 index 0000000..4d201fd --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/IP4Addr.cc @@ -0,0 +1,53 @@ +/* + * IPv4 Addr + * + * Created on 2025.1.20 at Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + +#include +#include +#include + +using namespace adl; + +namespace monkey::net { + + +TString IP4Addr::toString() const { + adl::TString ipStr; + + ipStr += adl::TString::to_string((adl::uint32_t) ui8arr[0]); + ipStr += '.'; + ipStr += adl::TString::to_string((adl::uint32_t) ui8arr[1]); + ipStr += '.'; + ipStr += adl::TString::to_string((adl::uint32_t) ui8arr[2]); + ipStr += '.'; + ipStr += adl::TString::to_string((adl::uint32_t) ui8arr[3]); + return ipStr; +} + + +monkey::Status IP4Addr::set(const adl::TString& str) { + ArrayList segments; + str.split(".", segments); + + if (segments.size() != 4) + return Status::INVALID_PARAMETERS; + + for (adl::size_t i = 0; i < 4; i++) { + + // todo: check errors like "192.16aaa.1.1" + + ui8arr[i] = (adl::uint8_t) segments[i].toInt64(); + } + + return Status::SUCCESS; +} + + + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/IP4Addr.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/IP4Addr.h new file mode 100644 index 0000000..d195482 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/IP4Addr.h @@ -0,0 +1,50 @@ +/* + * IPv4 Addr + * + * Created on 2025.1.20 at Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + +#pragma once + +#include +#include +#include +#include + + +namespace monkey::net { + +class IP4Addr { +public: + union { + adl::uint32_t ui32; // in net order + adl::int32_t i32; // in net order + adl::uint8_t ui8arr[4]; + adl::int8_t i8arr[4]; + }; + +public: + IP4Addr() { i32 = 0; } + IP4Addr(adl::uint8_t ip[4]) { ui32 = * (adl::uint32_t*) ip; } + IP4Addr(adl::int8_t ip[4]) { ui32 = * (adl::uint32_t*) ip; } + IP4Addr(adl::uint32_t ip, bool netOrder = true) { ui32 = netOrder ? ip : adl::htonl(ip); } + IP4Addr(adl::int32_t ip, bool netOrder = true) { i32 = netOrder ? ip : adl::htonl(ip); } + + adl::TString toString() const; + monkey::Status set(const adl::TString&); // like "192.168.1.1" + + bool operator == (const IP4Addr& other) const { return ui32 == other.ui32; } + bool operator != (const IP4Addr& other) const { return ui32 != other.ui32; } + bool operator <= (const IP4Addr& other) const { return ui32 <= other.ui32; } + bool operator >= (const IP4Addr& other) const { return ui32 >= other.ui32; } + bool operator < (const IP4Addr& other) const { return ui32 < other.ui32; } + bool operator > (const IP4Addr& other) const { return ui32 > other.ui32; } + +}; + + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.cc new file mode 100644 index 0000000..df2f623 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.cc @@ -0,0 +1,101 @@ +/* + TCP/IPv4 Socket + + created on 2025.2.6 at Xiangzhou, Zhuhai, Guangdong + + gongty [at] tongji [dot] edu [dot] cn +*/ + +#include + +#include + +#include +#include + + +using namespace monkey; + + +namespace monkey::net { + + +Status Socket4::connect() { + close(); + + Genode::log("[Socket4] Connecting to ", ip.toString().c_str(), " : ", port); + + socketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (socketFd == -1) { + return Status::NETWORK_ERROR; + } + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = adl::htons(port); + addr.sin_addr.s_addr = ip.ui32; + + if (::connect(socketFd, (struct sockaddr*)&addr, sizeof(addr))) { + return Status::NETWORK_ERROR; + } + + return Status::SUCCESS; +} + + +Status Socket4::start(adl::size_t maxClients) { + if (maxClients > INT32_MAX) { + return Status::INVALID_PARAMETERS; + } + + close(); + + socketFd = socket(AF_INET, SOCK_STREAM, 0); + + if (socketFd == 0) { + return Status::NETWORK_ERROR; + } + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ip.ui32; + addr.sin_port = adl::htons(port); + + if (bind(socketFd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + close(); + return Status::NETWORK_ERROR; + } + + if (listen(socketFd, adl::int32_t(maxClients)) < 0) { + close(); + return Status::NETWORK_ERROR; + } + + Genode::log("Server started. Addr: ", ip.toString().c_str(), " : ", port); + return Status::SUCCESS; +} + + +Socket4 Socket4::accept(bool ignoreError) { + while (true) { + Genode::log("Waiting for connection..."); + + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + auto fd = ::accept(socketFd, (struct sockaddr*)&addr, &addrlen); + + if (fd != -1) { + Socket4 s; + s.socketFd = fd; + s.ip.ui32 = addr.sin_addr.s_addr; + s.port = adl::ntohs(addr.sin_port); + return s; + } + else if (!ignoreError) { + return Socket4 {}; + } + } +} + + +} // namespace monkey::net diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.h new file mode 100644 index 0000000..942ac02 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.h @@ -0,0 +1,67 @@ +/* + TCP/IPv4 Socket + + created on 2025.2.5 at Xiangzhou, Zhuhai, Guangdong + + gongty [at] tongji [dot] edu [dot] cn +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace monkey::net { + + +/** + * TCP/IPv4 Socket. + * + * Set `ip` and `port` accrodingly, then call `connect` to `start` open connection(s). + */ +struct Socket4 : public monkey::net::PromisedSocketIo { + + /** + * In server mode, this is which ip to listen. + * In client mode, this is which ip to connect to. + */ + IP4Addr ip = { 0 }; + + + /** + * In server mode, this is our port listens for connections. + * In client mode, this is server's port. + */ + adl::uint16_t port = 0; + + + /** + * For client mode. + * + * This should do 2 things: + * 1. Create socket + * 2. Connect to server + */ + virtual monkey::Status connect(); + + + // For server mode. + virtual monkey::Status start(adl::size_t maxClients = 32); + + + /** + * For server mode. + * + * Socket4 returned is a connection client to the other side. It might be INVALID. + * So check whether `valid()` before using that connection. + */ + Socket4 accept(bool ignoreError = false); + +}; + + +} // namespace monkey::net + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/SunflowerLounge.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/SunflowerLounge.h new file mode 100644 index 0000000..ba54fea --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/SunflowerLounge.h @@ -0,0 +1,41 @@ +/* + * Sunflower Lounge : Base type for various lounges like App Lounge and Memory Lounge. + * + * Using lounge, you can let clients access only specified APIs. + * + * You should ensure client is authenticated before invited to the lounge. + * + * The name `Sunflower` is inspired by CMB. + * https://www.cmbchina.com/personal/allinonecard/cardinfo.aspx?guid=be48a9ab-300f-4393-b80e-02308ddf1a7f + * + * gongty [at] tongji [dot] edu [dot] cn + * Created on 2025.1.25 at Xiangzhou, Zhuhai, Guangdong + */ + +#pragma once + +#include + +namespace monkey::net { + +template +struct SunflowerLounge { + + ContextType& context; + ProtocolConnectionType client; + + SunflowerLounge( + ContextType& context, + ProtocolConnectionType& client + ) + : context {context}, client {client} + {} + + virtual ~SunflowerLounge() {} + + virtual monkey::Status serve() = 0; +}; + + +} // namespace monkey::net + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.cc new file mode 100644 index 0000000..3f458bb --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.cc @@ -0,0 +1,48 @@ +/* + * Monkey Net :: TcpIo + * + * Created on 2025.1.21 at Xiangzhou, Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + + +#include +#include + +namespace monkey::net { + + +void PromisedSocketIo::close() { + if (valid()) { + ::close(socketFd); + socketFd = -1; + } +} + + +adl::int64_t PromisedSocketIo::recv(void* buf, adl::size_t len) { + adl::size_t sum = 0; + while (sum < len) { + adl::int64_t curr = read(socketFd, ((char*) buf) + sum, len - sum); + +// Genode::warning("sum ", sum, ", curr ", curr, ", len ", len); + if (curr <= 0) { + return -1; + } + + + sum += size_t(curr); + } + + return adl::int64_t(sum); +} + +adl::int64_t PromisedSocketIo::send(const void* buf, adl::size_t len) { + return ::write(socketFd, buf, len); +} + + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.h new file mode 100644 index 0000000..4dd716a --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.h @@ -0,0 +1,75 @@ +/* + * Monkey Net :: TcpIo + * + * Created on 2024.12.27 at Minhang, Shanghai + * + * + * feng.yt [at] sjtu [dot] edu [dot] cn + * gongty [at] tongji [dot] edu [dot] cn + * + */ + + +#pragma once + +#include +#include +#include + +namespace monkey::net { + + +class TcpIo { + +public: + virtual ~TcpIo() {} + + /** + * + * @param len Buffer size. + */ + virtual adl::int64_t recv(void* buf, adl::size_t len) = 0; + + + /** + * + * @param len Size of data. + */ + virtual adl::int64_t send(const void* buf, adl::size_t len) = 0; + +}; + + +/** + * Promised Socket IO is NOT closed automatically. + * You should call `close` as soon as you are finished with it. + */ +class PromisedSocketIo : public TcpIo { +public: + int socketFd = -1; + + + /** + * This method is reentrant. + * + * This method is a no-op if the socket has already been closed. + * + * You should call this as soon as you doesn't use this socket. + */ + virtual void close(); + + virtual ~PromisedSocketIo() override { + + } + + virtual bool valid() { return socketFd > 1; } + + virtual adl::int64_t recv(void* buf, adl::size_t len) override; + + virtual adl::int64_t send(const void* buf, adl::size_t len) override; + +}; + + + +} // namespace monkey::net diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/config.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/config.h new file mode 100644 index 0000000..f831dee --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/config.h @@ -0,0 +1,14 @@ +/* + Monkey Net Config + + Created by gongty on 2025.6.29 at Minhang +*/ + +#pragma once + +#include + +/** + * @deprecated + */ +#define VESPER_PROTOCOL_DEBUG MONKEY_CFG_ENABLE_DEBUG_LOGS diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hexview.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hexview.h new file mode 100644 index 0000000..0154901 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hexview.h @@ -0,0 +1,145 @@ +/* + * Portable hexdump util for C/C++ + * + * gongty [at] tongji [dot] edu [dot] cn + * Created on 2024.12.8 at Jiangchuan, Minhang, Shanghai + * + * + * Usage: + * You can change ReadFunc and WriteFunc to any type you want. + * But you should ensure their prototype: + * - ReadFunc : Callable. Returning one character as int. EOF for end. + * - WriteFunc: Callable. Receive one int as character. + */ + + +#include +#include +#include + +namespace monkey::net::debug { + +/** + * @param num + * @param width Pass 0 for non-fixed width. + * If num's hex width is wider, this argument is ignored. + * @param upperCase Pass non-zero for upper-case. Pass zero for lower-case. + */ +template +static inline void writeHex(unsigned long num, int width, int upperCase, WriteFunc out) { + int digitWidth = 1; + + { + unsigned long needle = 0xF; + while (needle < num) { + needle <<= 4; + needle |= 0xF; + digitWidth ++; + } + + if (width > 0) { + for (int w = digitWidth; w < width; w++) + out('0'); + } + } + + + while (digitWidth) { + unsigned long digit = num >> ((digitWidth - 1) * 4); + digit &= 0xF; + if (digit >= 0 && digit <= 9) { + out(digit + '0'); + } else { // digit is in [a, f] + out(digit - 0xA + (upperCase ? 'A' : 'a')); + } + + digitWidth --; + } + +} + + +template +static inline void writeLine(int addr, int buf[], int len, int upperCase, WriteFunc out) { + writeHex(addr, 8, upperCase, out); + out(' '); + out(' '); + + for (int i = 0; i < 16; i++) { + if (i == 8) { + out(i < len ? '-' : ' '); + out(' '); + } + if (i < len) { + writeHex(buf[i] & 0xFF, 2, upperCase, out); + } else { + out(' '); + out(' '); + } + out(' '); + } + + out(' '); + out(' '); + out(' '); + out(' '); + for (int i = 0; i < len; i++) { + if (buf[i] >= 33 && buf[i] <= 126) + out(buf[i]); + else + out('.'); + } + + out('\n'); +} + + +template +inline adl::size_t hexView(int upperCase, ReadFunc in, WriteFunc out) { + int buf[16]; + int pos = 0; // One after last occupied. + + adl::size_t addr = 0; + adl::size_t bytesRead = 0; + + while (true) { + int ch = in(); + if (ch == EOF) + break; + + buf[pos++] = ch; + if (pos == 16) { + writeLine(addr, buf, 16, upperCase, out); + addr += 16; + pos = 0; + } + } + + + if (pos) + writeLine(addr, buf, pos, upperCase, out); + return bytesRead; +} + + +inline adl::size_t hexView(const void* data, adl::size_t len) { + adl::TString hex; + adl::size_t count = 0; + auto ret = hexView( + true, + [&] () { + if (count < len) + return (int) ((const char*) data)[count++]; + else + return EOF; + }, + [&] (int ch) { + hex += char(ch); + } + ); + MONKEY_LOG_INFO(hex.c_str()); + return ret; +} + + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol.h new file mode 100644 index 0000000..0084b50 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol.h @@ -0,0 +1,24 @@ +/* + * Monkey Network Protocol + * + * Created on 2024.12 at Minhang, Shanghai + * + * + * feng.yt [at] sjtu [dot] edu [dot] cn + * gongty [at] tongji [dot] edu [dot] cn + * + */ + + +#pragma once + +#include + +namespace monkey::net::protocol { + const adl::int32_t LATEST_VERSION = 2; +} + +#include + +#include +#include diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol1Connection.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol1Connection.cc new file mode 100644 index 0000000..f53681e --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol1Connection.cc @@ -0,0 +1,753 @@ +/* + * Protocol V1 Connection + * + * Created on 2025.1.21 at Xiangzhou, Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + + +#include +#include + +#define RECV_AND_HANDLE_RESPONSE MONKEY_PROTOCOL_RECV_AND_HANDLE_RESPONSE + +namespace monkey::net { + +using protocol::Msg; +using protocol::MsgType; +using protocol::Response; + + +void Protocol1Connection::LastError::set(protocol::Response* response, const char* api) { + set(response->code, response->msg, response->msgLen, api); +} + + +void Protocol1Connection::LastError::set( + adl::uint32_t code, + const void* msg, + adl::size_t msgLen, + const char* api +) { + this->code = code; + this->api = api; + + adl::ByteArray b; + b.append(msg, msgLen); + this->msg = b.toString(); +} + + +void Protocol1Connection::LastError::clear() { + code = 0; + msg.clear(); + api.clear(); +} + + +Status Protocol1Connection::sendResponse( + const adl::uint32_t code, + const adl::size_t msgLen, + const void* msg, + const adl::size_t paimonLen, + const void* paimon +) { + adl::ByteArray data; + data.resize(8 + msgLen + paimonLen); + (* (uint32_t*) &data[0]) = adl::htonl(code); + (* (uint32_t*) &data[4]) = adl::htonl(uint32_t(msgLen)); + adl::memcpy(data.data() + 8, msg, msgLen); + adl::memcpy(data.data() + 8 + msgLen, paimon, paimonLen); + return sendMsg(MsgType::Response, data); +} + + +Status Protocol1Connection::sendResponse(const uint32_t code, const adl::ByteArray& msg) { + adl::ByteArray data; + data.resize(8 + msg.size()); + + (* (uint32_t*) &data[0]) = adl::htonl(code); + (* (uint32_t*) &data[4]) = adl::htonl(uint32_t(msg.size())); + adl::memcpy(data.data() + 8, msg.data(), msg.size()); + + Status status = sendMsg(MsgType::Response, data); + return status; +} + + +Status Protocol1Connection::sendResponse(const adl::uint32_t code, const adl::TString& msg) { + return sendResponse(code, adl::ByteArray {msg.c_str()}); +} + + +Status Protocol1Connection::sendResponse(const adl::uint32_t code, const char* msg) { + return sendResponse(code, adl::ByteArray {msg}); +} + + + +Status Protocol1Connection::recvResponse(Response** response) { + Msg* rawMsg = nullptr; + Status libResult = recvMsg(&rawMsg, MsgType::Response); + + if (libResult != Status::SUCCESS) { + return libResult; + } + + + *response = (Response*) rawMsg; + + + (*response)->code = adl::ntohl((*response)->code); + (*response)->msgLen = adl::ntohl((*response)->msgLen); + + return Status::SUCCESS; +} + + +// ------ 0x1001 : Auth ------ + +Status Protocol1Connection::auth(const adl::ByteArray& key) { + adl::ByteArray challenge; + Status status = recvAuth(challenge); + if (status != Status::SUCCESS) + return status; + + crypto::rc4Inplace(challenge, key); + if ((status = sendResponse(0, challenge)) != Status::SUCCESS) { + return status; + } + + protocol::Response* response = nullptr; + status = recvResponse(&response); + if (status != Status::SUCCESS) + return status; + + if (response->code != 0) + status = Status::PROTOCOL_ERROR; + + this->freeMsg(response); + return status; +} + + +Status Protocol1Connection::auth( + const adl::HashMap& appsKeyring, + const adl::ArrayList& memoryNodesKeyring +) { + return auth(&appsKeyring, &memoryNodesKeyring); +} + + +Status Protocol1Connection::auth( + const adl::HashMap* appsKeyring, + const adl::ArrayList* memoryNodesKeyring +) { + adl::ByteArray challenge { "fyt's score is A+" }; // TODO + + Status status; + if ( (status = sendAuth(challenge)) != Status::SUCCESS ) + return status; + + Response* response = nullptr; + if ((status = recvResponse(&response)) != Status::SUCCESS) + return status; + + adl::ByteArray cipher { response->msg, response->msgLen }; + + bool verified = false; + + // check if app + + if (appsKeyring) { + for (const auto& it : *appsKeyring) { + verified = crypto::rc4Verify(it.second, challenge, cipher); + if (verified) { + appId = it.first; + nodeType = NodeType::App; + break; + } + } + } + + // check if memory node + + if (!verified && memoryNodesKeyring) { + if (crypto::rc4Verify(*memoryNodesKeyring, cipher, challenge) != -1) { + nodeType = NodeType::MemoryNode; + verified = true; + } + else { + nodeType = NodeType::Unknown; + } + } + + sendResponse(!!(nodeType == NodeType::Unknown), nullptr); + + this->freeMsg(response); + response = nullptr; + return Status::SUCCESS; +} + + + +Status Protocol1Connection::sendAuth(const adl::ByteArray& challenge) { + return sendMsg(MsgType::Auth, challenge); +} + + +Status Protocol1Connection::recvAuth(adl::ByteArray& challenge) { + protocol::Msg* msg = nullptr; + Status status = recvMsg(&msg, MsgType::Auth); + if (status != Status::SUCCESS) + return status; + + if (!challenge.resize(msg->header.length)) { + adl::defaultAllocator.free(msg); + return Status::OUT_OF_RESOURCE; + } + + adl::memcpy(challenge.data(), msg->data, challenge.size()); + this->freeMsg(msg); + return Status::SUCCESS; +} + +// ------ 0x1100 : Get Identity Keys ------ + +struct GetIdentityKeysKeyHeaderPacked { + Protocol1Connection::ReplyGetIdentityKeysParams::NodeType nodeType; + Protocol1Connection::ReplyGetIdentityKeysParams::KeyType keyType; + adl::int8_t reserved0[2] = {0}; + + adl::int64_t offset; + adl::int32_t len; + + adl::int64_t id; + adl::int8_t reserved1[16] = {0}; +} __packed; + + +Status Protocol1Connection::ReplyGetIdentityKeysParams::addKey( + NodeType nodeType, + KeyType keyType, + const adl::ByteArray& key, + adl::int64_t id +) { + + // check params + + if (nodeType != NodeType::App && nodeType != NodeType::MemoryNode) + return Status::INVALID_PARAMETERS; + + if (keyType != KeyType::RC4) // only RC4 allowed. + return Status::INVALID_PARAMETERS; + + if (key.size() > INT32_MAX) + return Status::INVALID_PARAMETERS; + + + // allocate header + + const adl::size_t headerSize = sizeof(GetIdentityKeysKeyHeaderPacked); + + if ( ! keyHeaders.resize(keyHeaders.size() + headerSize) ) { + Genode::error("Vesper Protocol ReplyGetIdentityKeys: Failed to add key. Out of resource."); + Genode::error("> Resize failed. Target size: ", keyHeaders.size() + headerSize); + return Status::OUT_OF_RESOURCE; + } + + auto head = (GetIdentityKeysKeyHeaderPacked*) (keyHeaders.data() + keyHeaders.size() - headerSize); + + adl::memset(head, 0, headerSize); + + head->nodeType = nodeType; + head->keyType = keyType; + head->offset = (adl::int64_t) keys.size(); + head->len = (adl::int32_t) key.size(); + head->id = id; + + if (keys.append(key.data(), key.size()) != 0 /* Indicates error. */) { + keyHeaders.resize(keyHeaders.size() - headerSize); + Genode::error("Vesper Protocol ReplyGetIdentityKeys: Failed to add key. Out of resource."); + return Status::OUT_OF_RESOURCE; + } + +#if VESPER_PROTOCOL_DEBUG + Genode::log("Key added to reply get identity keys params. ", key.toString().c_str()); + Genode::log("> keyHeaders.size() = ", keyHeaders.size()); +#endif + return Status::SUCCESS; +} + + +Status Protocol1Connection::sendGetIdentityKeys() { + return sendMsg(MsgType::GetIdentityKeys); +} + + +Status Protocol1Connection::replyGetIdentityKeys(const ReplyGetIdentityKeysParams& params) { + + adl::ByteArray msg; + + if (!msg.resize(8 + params.keyHeaders.size() + params.keys.size())) + return Status::OUT_OF_RESOURCE; + +#if VESPER_PROTOCOL_DEBUG + Genode::log("replyGetIdentityKeys"); + Genode::log("> params.keyHeaders.size() = ", params.keyHeaders.size()); + Genode::log("> params.keys.size() = ", params.keys.size()); +#endif + + adl::uint64_t nkeys = params.keyHeaders.size() / sizeof(GetIdentityKeysKeyHeaderPacked); + * (adl::uint64_t *) msg.data() = adl::htonq(nkeys); + + adl::memcpy(msg.data() + 8, params.keyHeaders.data(), params.keyHeaders.size()); + adl::memcpy(msg.data() + 8 + params.keyHeaders.size(), params.keys.data(), params.keys.size()); + + auto pHeader = (GetIdentityKeysKeyHeaderPacked*) (msg.data() + 8); + + while ((void*) pHeader < msg.data() + 8 + params.keyHeaders.size()) { + pHeader->len = adl::htonl(pHeader->len); + + pHeader->offset = adl::htonq( + pHeader->offset + adl::int64_t(sizeof(nkeys)) + adl::int64_t(params.keyHeaders.size()) + ); + + pHeader->id = adl::htonq(pHeader->id); + + pHeader++; + } + + return sendResponse(0, msg); +} + + +Status Protocol1Connection::appreciateGetIdentityKeys( + protocol::Response& response, + void* data, + void (*record) ( + ReplyGetIdentityKeysParams::NodeType nodeType, + ReplyGetIdentityKeysParams::KeyType keyType, + const adl::ByteArray& key, + adl::int64_t id, + void* data + ) +) { + if (response.code != 0) { + Genode::error("Vesper Protocol [appreciate GetIdentityKeys]: response.code is not 0."); + return Status::PROTOCOL_ERROR; + } + + auto pMsg = (const char*) response.msg; + auto nkeys = adl::ntohq (* (adl::uint64_t *) pMsg); + + adl::size_t msgLen = response.header.length - 8; // Response body minus error-code and msg-len. + if (msgLen < 8 /* nkeys tooks 8 bytes */ + nkeys * sizeof(GetIdentityKeysKeyHeaderPacked)) { + Genode::error("Vesper Protocol [appreciate GetIdentityKeys]: msg too short."); + return Status::PROTOCOL_ERROR; + } + + + auto pHeader = (GetIdentityKeysKeyHeaderPacked*) (pMsg + 8); + + for (adl::size_t i = 0; i < nkeys; i++) { + auto& header = pHeader[i]; + + const auto RC4Type = ReplyGetIdentityKeysParams::KeyType::RC4; + const auto AppType = ReplyGetIdentityKeysParams::NodeType::App; + const auto MemoryNodeType = ReplyGetIdentityKeysParams::NodeType::MemoryNode; + + if (header.keyType != RC4Type || (header.nodeType != MemoryNodeType && header.nodeType != AppType)) { + Genode::error("Vesper Protocol [appreciate GetIdentityKeys]: Bad Type."); + Genode::error( + "> key type: ", + adl::uint32_t(header.keyType), + ", node type: ", + adl::uint32_t(header.nodeType) + ); + return Status::PROTOCOL_ERROR; + } + + auto offset = adl::ntohq(header.offset); + auto len = adl::ntohl(header.len); + auto id = adl::ntohq(header.id); + if (offset + len > adl::int64_t(msgLen)) { + Genode::error("Vesper Protocol [appreciate GetIdentityKeys]: Out of bound."); + Genode::error("> ", offset, " + ", len, " IS NOT LARGER THAN ", msgLen); + return Status::PROTOCOL_ERROR; + } + + adl::ByteArray key; + if (key.append(pMsg + offset, (adl::size_t) len) != 0) { + Genode::error("Vesper Protocol [appreciate GetIdentityKeys]: Out of resource."); + return Status::OUT_OF_RESOURCE; + } + + record(header.nodeType, header.keyType, key, id, data); + } + + return Status::SUCCESS; +} + + +// ------ 0x2000 : Memory Node Show ID ------ + + +Status Protocol1Connection::sendMemoryNodeShowId(adl::int64_t id) { + adl::int64_t netOrderId = adl::htonq(id); + return sendMsg( + protocol::MsgType::MemoryNodeShowId, + &netOrderId, + 8 + ); +} + + +Status Protocol1Connection::decodeMemoryNodeShowId(protocol::Msg* msg, adl::int64_t* id) { + + if (msg->header.length < sizeof(adl::int64_t)) + return Status::PROTOCOL_ERROR; + + *id = adl::ntohq( *(adl::int64_t*) msg->data); + return Status::SUCCESS; +} + + +// ------ 0x2001 : Memory Node Clock In ------ + + +Status Protocol1Connection::memoryNodeClockIn(adl::int64_t* id, IP4Addr ip, adl::uint16_t port) { + Status status = sendMemoryNodeClockIn(ip.ui32, port); + if (status != Status::SUCCESS) + return Status::PROTOCOL_ERROR; + + RECV_AND_HANDLE_RESPONSE( + auto& res = response; + if (res->header.length < 16) { + Genode::error("Bad response. Bad length: ", adl::uint64_t(res->header.length)); + status = Status::PROTOCOL_ERROR; + } + else { + *id = adl::ntohq(* (adl::int64_t *) res->msg); + } + ); + + return status; +} + + +Status Protocol1Connection::sendMemoryNodeClockIn(adl::uint32_t tcp4Ip, adl::uint16_t port) { + adl::uint8_t data[12]; + *(adl::int32_t*) data = adl::htonl(4); // TCP Protocol Version 4. + *(adl::uint32_t*) (data + 4) = adl::htonl((adl::uint32_t) port); + *(adl::uint32_t*) (data + 8) = tcp4Ip; + return sendMsg(MsgType::MemoryNodeClockIn, data, sizeof(data)); +} + + +Status Protocol1Connection::decodeMemoryNodeClockIn( + protocol::Msg* msg, + adl::int32_t* tcpVer, + adl::uint16_t* port, + adl::uint8_t ip[] +) { + if (msg->header.length < 12) { + return Status::PROTOCOL_ERROR; + } + + *tcpVer = adl::ntohl( *(adl::int32_t*) (msg->data + 0) ); + *port = (adl::uint16_t) adl::ntohl( *(adl::uint32_t*) (msg->data + 4) ); + + if (*tcpVer != 4) + return Status::PROTOCOL_ERROR; + + adl::memcpy(ip, msg->data + 8, (*tcpVer == 4 ? 4 : 16)); + return Status::SUCCESS; +} + + + +// ------ 0x2004 : Locate Memory Nodes ------ + +Status Protocol1Connection::sendLocateMemoryNodes() { + return sendMsg(MsgType::LocateMemoryNodes); +} + + +Status Protocol1Connection::locateMemoryNodes(adl::ArrayList& out) { + Status status = sendLocateMemoryNodes(); + if (status != Status::SUCCESS) + return status; + + RECV_AND_HANDLE_RESPONSE( + out.clear(); + auto& r = response; + + auto ENTRY_SIZE = sizeof(LocateMemoryNodeNodeInfoEntry); + for (adl::size_t off = 0; off < r->header.length - 8; off += ENTRY_SIZE) { + auto& entryPacked = *(LocateMemoryNodeNodeInfoEntry*) (r->msg + off); + + auto tcpVer = adl::ntohl(entryPacked.tcpVersion); + if (tcpVer != 4) { + Genode::error( + "VesperProtocol [locate memory nodes]: Entry on offset ", + off, + " 's tcp version is ", + (tcpVer + 0), // member of packed struct cannot in Genode::log directly. + ". Ignored." + ); + + continue; + } + + out.append({}); + out.back().id = adl::ntohq(entryPacked.id); + out.back().port = (adl::uint16_t) adl::ntohl(entryPacked.port); + out.back().tcpVersion = tcpVer; + out.back().ip = entryPacked.inet4addr; + } + ); + + return status; +} + + +// ------ 0x3001 : Try Alloc ------ + +Status Protocol1Connection::sendTryAlloc(adl::size_t blockSize, adl::size_t nBlocks) { + adl::ByteArray data; + if (!data.resize(16)) { + return Status::OUT_OF_RESOURCE; + } + + auto a = (adl::uint64_t*) data.data(); + a[0] = adl::htonq(adl::uint64_t(blockSize)); + a[1] = adl::htonq(adl::uint64_t(nBlocks)); + return sendMsg(MsgType::TryAlloc, data); +} + + +Status Protocol1Connection::decodeTryAlloc(Msg* msg, adl::size_t* blockSize, adl::size_t* nBlocks) { + // Generated by Google Gemini 2.0 Flash. Checked by GTY. + + if (msg->header.length < 2 * sizeof(adl::uint64_t)) { + return Status::PROTOCOL_ERROR; + } + + *blockSize = adl::ntohq(*reinterpret_cast(msg->data)); + *nBlocks = adl::ntohq(*reinterpret_cast(msg->data + 8)); + + return Status::SUCCESS; +} + + +Status Protocol1Connection::replyTryAlloc(adl::ArrayList blockIds) { + adl::ArrayList data = blockIds; + + for (auto& it : data) { + it = adl::htonq(it); + } + + return sendResponse(0, data.size() * sizeof(data[0]), data.data()); +} + + +Status Protocol1Connection::tryAlloc( + adl::size_t blockSize, + adl::size_t nBlocks, + adl::ArrayList& idOut +) { + Status status = sendTryAlloc(blockSize, nBlocks); + if (status != Status::SUCCESS) + return status; + + + RECV_AND_HANDLE_RESPONSE( + auto& r = response; + idOut.clear(); + for (auto p = (adl::int64_t*) r->msg; (const char*) p < r->msg + r->header.length - 8; p++) { + idOut.append(adl::ntohq(*p)); + } + ); + + return status; +} + + + +// ------ 0x3002 : Read Block ------ + +Status Protocol1Connection::sendReadBlock(adl::int64_t blockId) { + adl::ByteArray data; + if (!data.resize(sizeof(blockId))) + return Status::OUT_OF_RESOURCE; + + *(adl::int64_t*) data.data() = adl::htonq(blockId); + return sendMsg(MsgType::ReadBlock, data); +} + + +Status Protocol1Connection::decodeReadBlock(protocol::Msg* msg, adl::int64_t* blockId) { + // Generated by Google Gemini 2.0 Flash. Checked by GTY. + + if (msg->header.length < sizeof(adl::int64_t)) { + return Status::PROTOCOL_ERROR; + } + *blockId = adl::ntohq(*reinterpret_cast(msg->data)); + return Status::SUCCESS; +} + + +Status Protocol1Connection::replyReadBlock(const void* data, adl::size_t size) { + return sendResponse(0, size, data); +} + + + +Status Protocol1Connection::readBlock(adl::int64_t blockId, void* buf, adl::size_t bufSize) { + Status status = sendReadBlock(blockId); + + if (status != Status::SUCCESS) + return status; + + RECV_AND_HANDLE_RESPONSE( + auto& r = response; + auto blockSize = r->header.length - 8; + if (blockSize != bufSize) { + Genode::error( + "Vesper Protocol [read block]: bufSize ", + bufSize, + " not match block size ", + blockSize + ); + status = Status::PROTOCOL_ERROR; + } + else { + adl::memcpy(buf, r->msg, bufSize); + } + ); + + return status; +} + + +// ------ 0x3003 : Write Block ------ + +Status Protocol1Connection::sendWriteBlock(adl::int64_t blockId, const void* data, adl::size_t size) { + adl::ByteArray msg; + + if (!msg.resize(size + 8)) { + return Status::OUT_OF_RESOURCE; + } + + *((adl::int64_t*) msg.data()) = adl::htonq(blockId); + adl::memcpy(msg.data() + 8, data, size); + + return sendMsg(MsgType::WriteBlock, msg); +} + + +Status Protocol1Connection::decodeWriteBlock(protocol::Msg* msg, adl::int64_t* id, adl::ByteArray& data) { + auto len = msg->header.length; + if (len < 8) { // Block ID + return Status::PROTOCOL_ERROR; + } + + *id = *(adl::int64_t*) msg->data; + *id = adl::ntohq(*id); + + auto blockSize = len - 8; + if (!data.resize(blockSize)) { + return Status::OUT_OF_RESOURCE; + } + + adl::memcpy(data.data(), msg->data + 8, blockSize); + + return Status::SUCCESS; +} + + + +Status Protocol1Connection::writeBlock(adl::int64_t blockId, const void* data, adl::size_t size) { + Status status = Status::SUCCESS; + + if ((status = sendWriteBlock(blockId, data, size)) != Status::SUCCESS) { + return status; + } + + RECV_AND_HANDLE_RESPONSE({}); + + return status; +} + + + + +// ------ 0x3004 : Check Avail Mem ------ + +Status Protocol1Connection::sendCheckAvailMem() { + return sendMsg(MsgType::CheckAvailMem); +} + + +Status Protocol1Connection::replyCheckAvailMem(adl::size_t availMem) { + adl::uint64_t availMemNetOrder = adl::htonq((adl::uint64_t) availMem); + return sendResponse(0, sizeof(availMem), &availMemNetOrder); +} + + +Status Protocol1Connection::checkAvailMem(adl::size_t* availMem) { + Status status = sendCheckAvailMem(); + if (status != Status::SUCCESS) { + return status; + } + + RECV_AND_HANDLE_RESPONSE( + *availMem = (adl::size_t) adl::htonq(*(adl::uint64_t*) response->msg); + ); + + return status; +} + + + + +// ------ 0x3005 : Free Block ------ + +Status Protocol1Connection::sendFreeBlock(adl::int64_t blockId) { + auto bId_netOrder = adl::htonq(blockId); + return sendMsg(MsgType::FreeBlock, &bId_netOrder, sizeof(bId_netOrder)); +} + + +Status Protocol1Connection::decodeFreeBlock(Msg* msg, adl::int64_t* blockId) { + // Generated by Google Gemini 2.0 Flash. Checked by GTY. + + if (msg->header.length < sizeof(adl::int64_t)) { + return Status::PROTOCOL_ERROR; + } + + *blockId = adl::ntohq(*reinterpret_cast(msg->data)); + + return Status::SUCCESS; +} + + +Status Protocol1Connection::freeBlock(adl::int64_t blockId) { + // Generated by Google Gemini 2.0 Flash. Checked by GTY. + + Status status = sendFreeBlock(blockId); + if (status != Status::SUCCESS) { + return status; + } + + RECV_AND_HANDLE_RESPONSE({}); + + return status; +} + + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol1Connection.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol1Connection.h new file mode 100644 index 0000000..7318a0f --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol1Connection.h @@ -0,0 +1,275 @@ +/* + * Protocol V1 Connection + * + * Created on 2025.1.21 at Xiangzhou, Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + +#pragma once + +#include +#include +#include + +namespace monkey::net { + + +/** + * @deprecated Use latest protocol version is suggested. + */ +class Protocol1Connection : public ProtocolConnection { +public: + struct MemoryNodeInfo { + adl::int64_t id; + adl::int32_t tcpVersion; + adl::uint16_t port; + + IP4Addr ip; + }; + + enum class NodeType { + MemoryNode, + App, + Unknown, + Other + } nodeType = NodeType::Other; + + + /** + * Other side's id. + */ + union { + adl::int64_t memoryNodeId = -1; + adl::int64_t appId; + adl::int64_t nodeId; + }; + + + static const adl::int64_t VERSION = 1; + virtual adl::int64_t version() override { return VERSION; } + + + /** + * errno: only remembers last failed response received in one-click methods. + */ + struct LastError { + adl::uint32_t code = 0; + adl::TString msg; + adl::TString api; + + void set(protocol::Response* response, const char* api = nullptr); + void set(adl::uint32_t code, const void* msg, adl::size_t msgLen, const char* api = nullptr); + void clear(); + } lastError; + + + // Connection operations + + + Status sendResponse( + const adl::uint32_t code, + const adl::size_t msgLen, + const void* msg, + const adl::size_t paimonLen = 0, + const void* paimon = nullptr + ); + + Status sendResponse(const adl::uint32_t code, const adl::ByteArray& msg); + Status sendResponse(const adl::uint32_t code, const adl::TString& msg); + Status sendResponse(const adl::uint32_t code, const char* msg = nullptr); + + + + /** + * The caller is responsible for freeing `response` + * when monkey::Status is SUCCESS. + */ + Status recvResponse(protocol::Response** response); + + + + // ------ 0x1001 : Auth ------ + + Status sendAuth(const adl::ByteArray& challenge); + Status recvAuth(adl::ByteArray& challenge); + + /** + * Client-mode auth: Show identity to server. + */ + Status auth(const adl::ByteArray& key); + + /** + * Server-mode auth: Check client's identity. + * + * @return monkey::Status::SUCCESS Only means no network error occurred. + * You should also check whether `this->nodeType` is correct. + */ + Status auth( + // app id -> app key + const adl::HashMap& appsKeyring, + const adl::ArrayList& memoryNodesKeyring + ); + + + /** + * Server-mode auth: Check client's identity. + * This method works just like the one above except: You can set keyring to nullptr + * to ignore some type(s). + * + * @return monkey::Status::SUCCESS Only means no network error occurred. + * You should also check whether `this->nodeType` is correct. + */ + Status auth( + // app id -> app key + const adl::HashMap* appsKeyring, + const adl::ArrayList* memoryNodesKeyring + ); + + + // ------ 0x1100 : Get Identity Keys ------ + + struct ReplyGetIdentityKeysParams { + adl::ByteArray keyHeaders; + adl::ByteArray keys; + + enum class NodeType : adl::int8_t { + App = 0, + MemoryNode = 1 + }; + + enum class KeyType : adl::int8_t { + RC4 = 0 + }; + + monkey::Status addKey( + NodeType nodeType, + KeyType keyType, + const adl::ByteArray& key, + adl::int64_t id = 0 // ignored for Memory Node + ); + }; + + + Status sendGetIdentityKeys(); + Status replyGetIdentityKeys(const ReplyGetIdentityKeysParams&); + Status appreciateGetIdentityKeys( + protocol::Response& response, + void* data, + + /** + * Called for each key. + * + * @param data Same as `data` passed to `appreciate`. + */ + void (*record) ( + ReplyGetIdentityKeysParams::NodeType nodeType, + ReplyGetIdentityKeysParams::KeyType keyType, + const adl::ByteArray& key, + adl::int64_t id, + void* data + ) + ); + + + // ------ 0x2000 : Memory Node Show ID ------ + + Status sendMemoryNodeShowId(adl::int64_t id); + Status decodeMemoryNodeShowId(protocol::Msg* msg, adl::int64_t* id); + + + // ------ 0x2001 : Memory Node Clock In ------ + + + Status memoryNodeClockIn(adl::int64_t* id, IP4Addr ip, adl::uint16_t port); + + + /** + * For TCP4. + */ + Status sendMemoryNodeClockIn(adl::uint32_t tcp4Ip, adl::uint16_t port); + + /** + * + * + * @param ip Should be at least 16 bytes (array length at least 16). + * For TCP4, ip[0] to ip[3] is filled. + * For TCP6, ip[0] to ip[15] is filled. + * + */ + Status decodeMemoryNodeClockIn( + protocol::Msg* msg, + adl::int32_t* tcpVer, + adl::uint16_t* port, + adl::uint8_t ip[] + ); + + + // ------ 0x2004 : Locate Memory Nodes ------ + + struct LocateMemoryNodeNodeInfoEntry { + adl::int64_t id; + adl::int32_t tcpVersion; // must be 4 (in net order) + adl::uint32_t port; + + adl::uint32_t inet4addr; + adl::int8_t padding[12]; + } __packed; + + Status sendLocateMemoryNodes(); + Status locateMemoryNodes(adl::ArrayList&); + + + // ------ 0x3001 : Try Alloc ------ + + Status sendTryAlloc(adl::size_t blockSize, adl::size_t nBlocks); + Status decodeTryAlloc(protocol::Msg* msg, adl::size_t* blockSize, adl::size_t* nBlocks); + Status replyTryAlloc(adl::ArrayList blockIds); + Status tryAlloc(adl::size_t blockSize, adl::size_t nBlocks, adl::ArrayList& idOut); + + // ------ 0x3002 : Read Block ------ + + Status sendReadBlock(adl::int64_t blockId); + Status decodeReadBlock(protocol::Msg* msg, adl::int64_t* blockId); + Status replyReadBlock(const void* data, adl::size_t size); + + /** + * Read data from remote, and fill it into `bufSize`. + * + * @param bufSize If 0, api would not check buf's boundary. + * If not 0 and response's size not match, + * error reported with no change to buf. + */ + Status readBlock(adl::int64_t blockId, void* buf, adl::size_t bufSize = 0); + + + // ------ 0x3003 : Write Block ------ + + Status sendWriteBlock(adl::int64_t blockId, const void* data, adl::size_t size); + Status decodeWriteBlock(protocol::Msg* msg, adl::int64_t* id, adl::ByteArray& data); + + Status writeBlock(adl::int64_t blockId, const void* data, adl::size_t size); + + // ------ 0x3004 : Check Avail Mem ------ + + Status sendCheckAvailMem(); + Status replyCheckAvailMem(adl::size_t availMem); + Status checkAvailMem(adl::size_t* availMem); + + + // ------ 0x3005 : Free Block ------ + + Status sendFreeBlock(adl::int64_t blockId); + Status decodeFreeBlock(protocol::Msg*, adl::int64_t* blockId); + Status freeBlock(adl::int64_t blockId); + +}; + + + +MONKEY_NET_IMPL_DOCK_PROTOCOL(Protocol1Connection); + + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol2Connection.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol2Connection.cc new file mode 100644 index 0000000..cf3480b --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol2Connection.cc @@ -0,0 +1,251 @@ +/* + Monkey Protocol Connection Version 2 + + + Created on 2025.6.7 at Wujing, Minhang + gongty [at] alumni [dot] tongji [dot] edu [dot] cn +*/ + + +#include + +using namespace monkey; +using namespace monkey::net; +using monkey::net::protocol::Response; + + +#define RECV_AND_HANDLE_RESPONSE MONKEY_PROTOCOL_RECV_AND_HANDLE_RESPONSE + +// ------ 0x3001 : Try Alloc ------ + +Status Protocol2Connection::sendTryAlloc() { + return sendMsg(protocol::MsgType::TryAlloc); +} + + +Status Protocol2Connection::replyTryAlloc(adl::int64_t blockId, adl::int64_t dataVer, adl::int64_t readKey, adl::int64_t writeKey) { + + struct { + adl::int64_t blockId; + adl::int64_t dataVer; + adl::int64_t rdkey; + adl::int64_t wrkey; + } __packed payload; + + payload.blockId = adl::htonq(blockId); + payload.dataVer = adl::htonq(dataVer); + payload.rdkey = readKey; + payload.wrkey = writeKey; + + return sendResponse(0, sizeof(payload), &payload); +} + + +Status Protocol2Connection::tryAlloc(adl::int64_t* blockId, adl::int64_t* dataVer, adl::int64_t* readKey, adl::int64_t* writeKey) { + + Status status = sendTryAlloc(); + if (status != Status::SUCCESS) + return status; + + + RECV_AND_HANDLE_RESPONSE( + auto& r = response; + + struct Payload { + adl::int64_t blockId; + adl::int64_t dataVer; + adl::int64_t rdkey; + adl::int64_t wrkey; + } __packed; + + if (r->header.length < sizeof(Payload)) { + Genode::error("TryAlloc: Failed to decode response."); + status = Status::PROTOCOL_ERROR; + break; + } + + Payload* pPayload = (Payload*) r->msg; + Payload& payload = *pPayload; + if (blockId) + *blockId = adl::ntohq(payload.blockId); + if (dataVer) + *dataVer = adl::ntohq(payload.dataVer); + if (readKey) + *readKey = payload.rdkey; + if (writeKey) + *writeKey = payload.wrkey; + ); + + return status; +} + + + +// ------ 0x3002 : Read Block ------ + + + + +Status Protocol2Connection::replyReadBlock(adl::int64_t dataVer, const void* data) { + auto header = makeHeader((adl::uint32_t) protocol::MsgType::Response, 4096 + 8 + sizeof(dataVer)); + + adl::int64_t dataVerNetOrder = adl::htonq(dataVer); + auto acc = send(&header, sizeof(net::protocol::Header)); + + adl::int32_t zero = 0; + + // code and msgLen all set to 0. + acc += send(&zero, sizeof(zero)); + acc += send(&zero, sizeof(zero)); + + acc += send(&dataVerNetOrder, sizeof(dataVerNetOrder)); + acc += send(data, 4096); + + return (acc == sizeof(header) + 4096 + 8 + sizeof(dataVer)) ? Status::SUCCESS : Status::NETWORK_ERROR; +} + + +Status Protocol2Connection::readBlock(adl::int64_t blockId, void* buf, adl::int64_t* dataVer) { + Status status = sendReadBlock(blockId); + + if (status != Status::SUCCESS) + return status; + + RECV_AND_HANDLE_RESPONSE( + auto& r = response; + auto blockSize = r->header.length - 8 - sizeof(*dataVer); + if (blockSize != 4096) { + Genode::error( + "Vesper Protocol [read block]: bufSize ", + 4096, + " not match block size ", + blockSize + ); + status = Status::PROTOCOL_ERROR; + } + else { + if (dataVer) + *dataVer = adl::htonq(* (adl::int64_t*) r->msg); + adl::memcpy(buf, r->msg + sizeof(dataVer), blockSize /* 4096 */); + } + ); + + return status; +} + + + +// ------ 0x3003 : Write Block ------ + + + +Status Protocol2Connection::writeBlock(adl::int64_t blockId, const void* data, adl::int64_t* dataVer) { + Status status = sendWriteBlock(blockId, data); + + if (status != Status::SUCCESS) { + return status; + } + + RECV_AND_HANDLE_RESPONSE( + auto& r = response; + if (r->msgLen != 8) { + Genode::error("Protocol Error: ", __FUNCTION__); + status = Status::PROTOCOL_ERROR; + break; + } + + if (dataVer) { + *dataVer = adl::ntohq(* (adl::int64_t*) r->msg); + } + ); + + return status; +} + + + +// ------ 0x3006 : Ref Block ------ + +Status Protocol2Connection::decodeRefBlock(protocol::Msg* msg, adl::int64_t* accessKey) { + + if (msg->header.length < sizeof(adl::int64_t)) { + return Status::PROTOCOL_ERROR; + } + + *accessKey = *(adl::int64_t*) msg->data; + return Status::SUCCESS; +} + + +Status Protocol2Connection::refBlock(adl::int64_t accessKey, adl::int64_t* blockId) { + Status status = sendMsg(protocol::MsgType::RefBlock, &accessKey, sizeof(accessKey)); + + + RECV_AND_HANDLE_RESPONSE( + auto& r = response; + // assert response is valid. so skip msglen check. + + if (blockId) + *blockId = adl::ntohq(* (adl::int64_t *) r->msg); + ); + + return status; +} + + + +// ------ 0x3007 : Unref Block ------ + + +Status Protocol2Connection::decodeUnrefBlock(protocol::Msg* msg, adl::int64_t* blockId) { + if (msg->header.length < sizeof(adl::int64_t)) { + return Status::PROTOCOL_ERROR; + } + + *blockId = adl::ntohq(*(adl::int64_t*) msg->data); + return Status::SUCCESS; +} + + +Status Protocol2Connection::unrefBlock(adl::int64_t blockId) { + adl::int64_t blockIdNetOrder = adl::htonq(blockId); + Status status = sendMsg(protocol::MsgType::UnrefBlock, &blockIdNetOrder, sizeof(blockIdNetOrder)); + if (status != Status::SUCCESS) { + return status; + } + + RECV_AND_HANDLE_RESPONSE({}); + + return status; +} + + + +// ------ 0x3008 : Get Block Data Version ------ + +Status Protocol2Connection::decodeGetBlockDataVersion(protocol::Msg* msg, adl::int64_t* id) { + + if (msg->header.length < sizeof(adl::int64_t)) { + return Status::PROTOCOL_ERROR; + } + + *id = adl::ntohq(*reinterpret_cast(msg->data)); + return Status::SUCCESS; +} + + +Status Protocol2Connection::getBlockDataVersion(adl::int64_t blockId, adl::int64_t* dataVer) { + adl::int64_t blockIdNetOrder = adl::htonq(blockId); + Status status = sendMsg(protocol::MsgType::GetBlockDataVersion, &blockIdNetOrder, sizeof(blockIdNetOrder)); + if (status != Status::SUCCESS) { + return status; + } + + RECV_AND_HANDLE_RESPONSE( + if (dataVer) + *dataVer = adl::ntohq(*(adl::int64_t*) response->msg); + ); + + return status; +} + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol2Connection.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol2Connection.h new file mode 100644 index 0000000..20bf6c2 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/Protocol2Connection.h @@ -0,0 +1,89 @@ +/* + Monkey Protocol Connection Version 2 + + + Created on 2025.6.7 at Wujing, Minhang + gongty [at] alumni [dot] tongji [dot] edu [dot] cn +*/ + +#pragma once + +#include +#include + + +namespace monkey::net { + +class Protocol2Connection : public Protocol1Connection { +public: + + static const adl::int64_t VERSION = 2; + virtual adl::int64_t version() override { return VERSION; } + + + // ------ 0x3001 : Try Alloc ------ + + Status sendTryAlloc(); + Status decodeTryAlloc(protocol::Msg* msg, adl::size_t* blockSize, adl::size_t* nBlocks) = delete; + Status replyTryAlloc(adl::int64_t blockId, adl::int64_t dataVer, adl::int64_t readKey, adl::int64_t writeKey); + Status tryAlloc( + adl::int64_t* blockId, + adl::int64_t* dataVer = nullptr, + adl::int64_t* readKey = nullptr, + adl::int64_t* writeKey = nullptr); + + + // ------ 0x3002 : Read Block ------ + + inline Status sendReadBlock(adl::int64_t blockId) { + return Protocol1Connection::sendReadBlock(blockId); + } + inline Status decodeReadBlock(protocol::Msg* msg, adl::int64_t* blockId) { + return Protocol1Connection::decodeReadBlock(msg, blockId); + } + Status replyReadBlock(adl::int64_t dataVer, const void* data); + + /** + * Read data from remote, and fill it into `bufSize`. + * + * You should ensure `buf` size is enough (not less than 4KB). + */ + Status readBlock(adl::int64_t blockId, void* buf, adl::int64_t* dataVer = nullptr); + + + // ------ 0x3003 : Write Block ------ + + inline Status sendWriteBlock(adl::int64_t blockId, const void* data) { + return Protocol1Connection::sendWriteBlock(blockId, data, 4096); + } + inline Status decodeWriteBlock(protocol::Msg* msg, adl::int64_t* id, adl::ByteArray& data) { + return Protocol1Connection::decodeWriteBlock(msg, id, data); + } + + Status writeBlock(adl::int64_t blockId, const void* data, adl::int64_t* dataVer = nullptr); + + + // ------ 0x3006 : Ref Block ------ + + Status decodeRefBlock(protocol::Msg* msg, adl::int64_t* accessKey); + Status refBlock(adl::int64_t accessKey, adl::int64_t* blockId); + + // ------ 0x3007 : Unref Block ------ + + + Status decodeUnrefBlock(protocol::Msg* msg, adl::int64_t* blockId); + Status unrefBlock(adl::int64_t blockId); + + // ------ 0x3008 : Get Block Data Version ------ + + Status decodeGetBlockDataVersion(protocol::Msg* msg, adl::int64_t* id); + Status getBlockDataVersion(adl::int64_t blockId, adl::int64_t* dataVer); + +}; + + + +MONKEY_NET_IMPL_DOCK_PROTOCOL(Protocol2Connection); + +} // namespace monkey::net + diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnection.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnection.cc new file mode 100644 index 0000000..a775f8d --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnection.cc @@ -0,0 +1,333 @@ +/* + * Protocol Connection Base + * + * Created on 2025.1.21 at Xiangzhou, Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + + +#include +#include + + +#if VESPER_PROTOCOL_DEBUG +#include +#endif + +namespace monkey::net { + +using protocol::MsgType; +using protocol::Msg; +using protocol::Header; + + + +Status ProtocolConnection::sendMsg(MsgType type, const void* data, adl::uint64_t dataLen) { + Header header = makeHeader(adl::uint32_t(type), dataLen); + + +#if VESPER_PROTOCOL_DEBUG + { + adl::TString hex; + adl::size_t pos = 0; + debug::hexView( + 1, + [&] () { + if (pos < sizeof(Header)) { + return (int) ((char*) &header)[pos++]; + } + else if (pos - sizeof(Header) < dataLen && /* TODO */ pos - sizeof(Header) < 0x40) { + return (int) ((char*) data)[pos++ - sizeof(Header)]; + } + else { + return EOF; + } + }, + [&] (int ch) { + hex += char(ch); + } + ); + + Genode::log("VESPER_PROTOCOL_DEBUG : --- Send Msg -->"); + auto typeMachineOrder = adl::ntohl(header.type); + Genode::log( + "> header.type : ", + Genode::Hex(typeMachineOrder), + " ", protocol::msgTypeToString(MsgType(typeMachineOrder)) + ); + Genode::log("> header.length: ", adl::size_t(adl::ntohq(header.length))); + Genode::log(hex.c_str()); + } +#endif + + const adl::uint32_t headerSize = sizeof(header); + adl::uint64_t msgSize = headerSize + dataLen; + + adl::int64_t dataWrote = send(&header, headerSize); + if (data && dataLen) + dataWrote += send(data, dataLen); + + // Returns whether all msg sent. + return dataWrote >= 0 && adl::uint64_t(dataWrote) == msgSize ? Status::SUCCESS : Status::NETWORK_ERROR; +} + + +Status ProtocolConnection::sendMsg(MsgType type, const adl::ByteArray& data) { + return sendMsg(type, data.data(), data.size()); +} + + + + +Status ProtocolConnection::recvHeader(Header* header) { + adl::uint64_t received = 0; + while(received < sizeof(Header)){ + adl::int64_t num_bytes = recv( ((char*) header) + received, sizeof(Header) - received); + if(num_bytes == -1) { + Genode::error("[Receive_header] Read failed."); + return Status::NETWORK_ERROR; + } + received += uint64_t(num_bytes); + } + + + // Check magic. + + if ( !magicMatch(header->magic) ) { + Genode::error("[Receive Header] Header mismatch!"); + return Status::PROTOCOL_ERROR; + } + + + // Data from network is in network's byte-order. Convert it to native one. + + header->type = adl::ntohl(header->type); + header->length = adl::ntohq(header->length); + + return Status::SUCCESS; +} + + +Status ProtocolConnection::recvMsg(Msg** msg, MsgType type) { + Status libResult; + + Header header; + if ((libResult = recvHeader(&header)) != Status::SUCCESS) { + return libResult; + } + + + adl::uint64_t msgSize = (adl::uint64_t) header.length + sizeof(Header); + auto protocolMsg = adl::defaultAllocator.alloc(1, true, msgSize); + + if (protocolMsg == nullptr) { + // out of resource. + return Status::OUT_OF_RESOURCE; + } + + adl::memcpy(protocolMsg, &header, sizeof(Header)); + + adl::uint64_t received = 0; + while(received < header.length){ + adl::int64_t num_bytes = recv(((char*) protocolMsg) + sizeof(Header) + received, header.length - received); + if(num_bytes == -1){ + Genode::error("[Recv_msg] Read failed."); + adl::defaultAllocator.free(protocolMsg); + return Status::NETWORK_ERROR; + } + received += adl::uint64_t(num_bytes); + } + + + + if (type != MsgType::None && protocolMsg->header.type != adl::uint32_t(type)) { + adl::defaultAllocator.free(protocolMsg); + return Status::PROTOCOL_ERROR; + } + + + *msg = protocolMsg; + + +#if VESPER_PROTOCOL_DEBUG + { + adl::TString hex; + adl::size_t pos = 0; + debug::hexView( + 1, + [&] () { + if (pos < sizeof(Header) + header.length && /* TODO */ pos - sizeof(Header) < 0x40) { + return (int) ((char*) protocolMsg)[pos++]; + } + else { + return EOF; + } + }, + [&] (int ch) { + hex += char(ch); + } + ); + + Genode::log("VESPER_PROTOCOL_DEBUG : <-- Recv Msg ---"); + Genode::log( + "> header.type : ", + Genode::Hex(header.type), " ", + protocol::msgTypeToString(MsgType(header.type)) + ); + Genode::log("> header.length: ", adl::size_t(header.length)); + Genode::log(hex.c_str()); + Genode::log("Note: Header field is adjusted to host's byte order."); + } +#endif + + + return Status::SUCCESS; +} + + +// ------ 0x1000 : Hello ------ + +static Status serverModeHello( + ProtocolConnection& client, + const adl::ArrayList& protocolVersions, + adl::int64_t* finalVersion +) { + Genode::log("Vesper Protocol [Hello]: Running server-mode Hello."); + adl::ArrayList clientVers; + Status status; + if ((status = client.recvHello(clientVers)) != Status::SUCCESS) { + Genode::error("Vesper Protocol [Hello]: Failed on phase 1."); + return status; + } + + adl::ArrayList versionsSupported; + + for (auto& it : clientVers) { + if (protocolVersions.contains(it)) { + versionsSupported.append(it); + } + } + + if (versionsSupported.isEmpty()) { + Genode::warning("Vesper Protocol [Hello]: Versions supported by client is empty."); + client.sendHello(adl::ArrayList{}); + return status = Status::PROTOCOL_ERROR; + } + + + if ((status = client.sendHello(versionsSupported)) != Status::SUCCESS) { + Genode::error("Vesper Protocol [Hello]: Failed on phase 2."); + return status; + } + + + if ((status = client.recvHello(clientVers)) != Status::SUCCESS) { + Genode::error("Vesper Protocol [Hello]: Failed on phase 3."); + return status; + } + + if (clientVers.size() == 1 && versionsSupported.contains(clientVers[0])) { + *finalVersion = clientVers[0]; + return Status::SUCCESS; + } + + Genode::error("Vesper Protocol [Hello]: No agreement reached with client."); + return Status::PROTOCOL_ERROR; +} + + +static Status clientModeHello( + ProtocolConnection& client, + const adl::ArrayList& protocolVersions, + adl::int64_t* finalVersion +) { + if (protocolVersions.isEmpty()) { + Genode::error("Vesper Protocol [Hello]: Protocol version list is empty."); + return Status::INVALID_PARAMETERS; + } + + Genode::log("Vesper Protocol [Hello]: Running client-mode Hello."); + + Status status; + if ((status = client.sendHello(protocolVersions)) != Status::SUCCESS) + return status; + + adl::ArrayList vers; + if ((status = client.recvHello(vers)) != Status::SUCCESS) { + Genode::error("Vesper Protocol [Hello]: Failed on phase 2."); + return status; + } + + adl::int64_t maxVer = -1; + for (auto& it : vers) { + if (it > maxVer && protocolVersions.contains(it)) { + maxVer = it; + } + } + + if (maxVer == -1) + return Status::PROTOCOL_ERROR; + + vers.clear(); + vers.append(maxVer); + if ((status = client.sendHello(vers)) != Status::SUCCESS) + return status; + + *finalVersion = maxVer; + return Status::SUCCESS; +} + + +Status ProtocolConnection::hello( + const adl::ArrayList& protocolVersions, + HelloMode mode, + adl::int64_t* finalVersion +) { + auto handler = (mode == HelloMode::SERVER ? serverModeHello : clientModeHello); + return handler(*this, protocolVersions, finalVersion); +} + + +Status ProtocolConnection::hello(adl::int64_t version, HelloMode mode) { + adl::ArrayList arr; + arr.append(version); + adl::int64_t finalVer; + return hello(arr, mode, &finalVer); +} + + +Status ProtocolConnection::sendHello(const adl::ArrayList& protocolVersions) { + auto data = protocolVersions; + for (auto& it : data) + it = adl::htonq(it); + + return sendMsg(MsgType::Hello, data.data(), data.size() * sizeof(adl::int64_t)); +} + + + +Status ProtocolConnection::recvHello(adl::ArrayList& out) { + Msg* msg; + Status status = recvMsg(&msg, MsgType::Hello); + if (status != Status::SUCCESS) + return status; + + out.clear(); + + for (size_t i = 0; i < msg->header.length / sizeof(int64_t); i++) { + void* p = msg->data + i * sizeof(int64_t); + adl::int64_t value = * (int64_t *) p; + out.append( adl::ntohq(value) ); + } + + this->freeMsg(msg); + + return status; +} + + + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnection.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnection.h new file mode 100644 index 0000000..c889e56 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnection.h @@ -0,0 +1,222 @@ +/* + * Protocol Connection Base + * + * Created on 2025.1.21 at Xiangzhou, Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +#include + +#include + + + +/** + * This macro is designed for simple "ping-pong" like requests. + * + * This macro should be used after sending request to server. + * + * The macro do follows: + * 1. recv response (return if error). + * 2. if response code is not 0, record and set `status` to error type. + * 3. if response code is 0, run code in `onSuccess`. `response` is used for raw msg. + * 4. free up response message. + */ +#define MONKEY_PROTOCOL_RECV_AND_HANDLE_RESPONSE(onSuccess) \ + do { \ + Response* response = nullptr; \ + if ((status = recvResponse(&response)) != Status::SUCCESS) { \ + return status; \ + } \ + if (response->code != 0) { \ + MONKEY_LOG_ERROR( \ + "VesperProtocol: Response code is ", \ + (response->code + 0), \ + " in api ", \ + __FUNCTION__ \ + ); \ + lastError.set(response, __FUNCTION__); \ + MONKEY_LOG_ERROR( \ + "> Error message is: ", \ + (lastError.msg.c_str() ? lastError.msg.c_str() : "") \ + ); \ + status = Status::PROTOCOL_ERROR; \ + } \ + else { \ + onSuccess \ + } \ + if (response) { \ + this->freeMsg(response); \ + response = nullptr; \ + } \ + } while (0) + + +namespace monkey::net { + +/** + * Agent for a Vesper Protocol based socket connection. + * + * Method types: + * - send [TYPE] : Send a [TYPE] msg to other. + * - decode [TYPE] : Decode a [TYPE] msg received. [TYPE] should be checked. + * - reply [TYPE] : Send a response msg to reply a received [TYPE] msg. + * - appreciate [TYPE] : Understand the reply of [TYPE] request. `appreciate` often appreciates `response`. + * You should ensure msg passed to method is really the response of [TYPE] request. + * - (do) [TYPE] : Do the entire [TYPE] procedure as client (or server if specific method called). + * + * + * + * Client Server + * ============================== + * + * (send) \ + * ----------> . | + * . | + * (decode) | + * . \ + * . > "one click" + * (reply) / + * . <----------- | + * . | + * . | + * (appreciate) / + * + * + * This class only maintains basic info for connection, so it can be copied freely. + * Do not store data like `request counter` in it. + */ +class ProtocolConnection : public Socket4 { +public: + + // Static methods + + inline static const char* magic() { return protocol::MAGIC; } + inline static adl::size_t magicLen() { return protocol::MAGIC_LEN; } + + inline static bool magicMatch(const adl::uint8_t magic[]) { + return * (const adl::uint32_t*) ProtocolConnection::magic() == * (const adl::uint32_t*) magic; + } + + + // Basic info methods + + /** + * This marks current protocol version supported by this object, + * not the one protocol is using. + * + * Each connection should starts with a version 0 connection, then upgrade to + * certain version after "Hello". + * + * See `Protocol.md` for details. + */ + virtual adl::int64_t version() { + return 0; // Override this for each version. + } + + + // Connection operations + + + inline virtual void makeHeader( + protocol::Header* header, + adl::uint32_t type, + adl::uint64_t length, + bool netOrder = true, + bool copyMagic = true + ) { + if (copyMagic) { + adl::memcpy(header->magic, magic(), magicLen()); + } + + header->type = netOrder ? adl::htonl(type) : type; + header->length = netOrder ? adl::htonq(length) : length; + } + + + inline virtual protocol::Header makeHeader(adl::uint32_t type, adl::uint64_t length, bool netOrder = true) { + protocol::Header header; + makeHeader(&header, type, length, netOrder, false); + return header; // We believe NRV would optimize this. + } + + + virtual Status sendMsg( + protocol::MsgType type, + const void* data = nullptr, + adl::uint64_t dataLen = 0 + ); + + + virtual Status sendMsg( + protocol::MsgType type, + const adl::ByteArray& data + ); + + + + virtual Status recvHeader(protocol::Header*); + + + /** + * The caller is responsible for freeing `msg` by calling freeMsg. + * when monkey::Status is SUCCESS. + * + * @param type Ensure type. Set to MsgType::None to disable this check. + */ + virtual Status recvMsg(protocol::Msg** msg, protocol::MsgType type = protocol::MsgType::None); + virtual inline void freeMsg(protocol::Msg* msg) { + if (msg) { + adl::defaultAllocator.free(msg); + } + } + + virtual inline void freeMsg(protocol::Response* msg) { + this->freeMsg((protocol::Msg*) msg); + } + + + + // ------ 0x1000 : Hello ------ + + enum class HelloMode : bool { + SERVER, CLIENT + }; + + virtual Status hello( + const adl::ArrayList& protocolVersions, + HelloMode, + adl::int64_t* finalVersion + ) final; + + /** + * Use a specified version of protocol. If failed, no more negotiation needed. + * + * @param version The version you want to use. + * @param serverMode Whether works in server mode. + * + * @return Status If SUCCESS, negotiation success. + */ + virtual Status hello(adl::int64_t version, HelloMode) final; + + virtual Status sendHello(const adl::ArrayList& protocolVersions) final; + virtual Status recvHello(adl::ArrayList& out) final; + + +}; + + +} \ No newline at end of file diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnectionDock.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnectionDock.h new file mode 100644 index 0000000..6b8dac4 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/ProtocolConnectionDock.h @@ -0,0 +1,31 @@ +/* + Protocol Connection Dock Support + + Created on 2025.6.7 at Wujing, Minhang + + gongty [at] alumni [dot] tongji [dot] edu [dot] cn + +*/ + +/* + TODO (For server mode): + 1. bind + 2. listen + 3. socket +*/ + + +#pragma once + +#include + +#include + + +// Place this in public access field. +#define MONKEY_NET_PROTOCOL_USE_DOCK() + + + +#define MONKEY_NET_IMPL_DOCK_PROTOCOL(baseclass) + \ No newline at end of file diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/defines.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/defines.cc new file mode 100644 index 0000000..923131e --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/defines.cc @@ -0,0 +1,91 @@ +/* + * Basic type defines + * + * Created on 2025.1.21 at Xiangzhou, Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + +#include + +namespace monkey::net::protocol { + +const char* MAGIC = "mkOS"; + + +const char* msgTypeToString(MsgType type) { + switch (type) { + case MsgType::None: + return "None"; + + + case MsgType::Response: + return "Response"; + + + case MsgType::Hello: + return "Hello"; + + case MsgType::Auth: + return "Auth"; + + + case MsgType::GetIdentityKeys: + return "GetIdentityKeys"; + + + case MsgType::MemoryNodeShowId: + return "MemoryNodeShowId"; + + case MsgType::MemoryNodeClockIn: + return "MemoryNodeClockIn"; + + case MsgType::MemoryNodeClockOut: + return "MemoryNodeClockOut"; + + case MsgType::MemoryNodeHandover: + return "MemoryNodeHandover"; + + case MsgType::LocateMemoryNodes: + return "LocateMemoryNodes"; + + + case MsgType::TryAlloc: + return "TryAlloc"; + + case MsgType::ReadBlock: + return "ReadBlock"; + + case MsgType::WriteBlock: + return "WriteBlock"; + + case MsgType::CheckAvailMem: + return "CheckAvailMem"; + + case MsgType::FreeBlock: + return "FreeBlock"; + + + case MsgType::RefBlock: + return "RefBlock"; + + case MsgType::UnrefBlock: + return "UnrefBlock"; + + case MsgType::GetBlockDataVersion: + return "GetBlockDataVersion"; + + + case MsgType::PingPong: + return "PingPong"; + + + default: + return "UNDEFINED"; + }; +} + + +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/defines.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/defines.h new file mode 100644 index 0000000..ef0cad5 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/protocol/defines.h @@ -0,0 +1,99 @@ +/* + * Basic type defines + * + * Created on 2025.1.21 at Xiangzhou, Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include +#include + + +#define __packed __attribute__((__packed__)) + + + +namespace monkey::net::protocol { + + +extern const char* MAGIC; +const adl::size_t MAGIC_LEN = 4; + + +enum class MsgType : adl::uint32_t { + None = 0x0000, + + Response = 0xA001, + + Hello = 0x1000, + Auth = 0x1001, + + GetIdentityKeys = 0x1100, + + MemoryNodeShowId = 0x2000, + MemoryNodeClockIn = 0x2001, + MemoryNodeClockOut = 0x2002, + MemoryNodeHandover = 0x2003, + LocateMemoryNodes = 0x2004, + + TryAlloc = 0x3001, + ReadBlock = 0x3002, + WriteBlock = 0x3003, + CheckAvailMem = 0x3004, + FreeBlock = 0x3005, + RefBlock = 0x3006, + UnrefBlock = 0x3007, + GetBlockDataVersion = 0x3008, + + PingPong = 0x4001 +}; + + +const char* msgTypeToString(MsgType); + + +struct Header { + union { + adl::uint8_t magic[4]; + + // You can treat it as 32-bit integer. + adl::uint32_t magicI32 = [] () {return * (adl::uint32_t*) MAGIC;} (); + } __packed; + + adl::uint32_t type; + adl::uint64_t length; // Tell byte-order whenever use. +} __packed; + + +/** + * Common message. + */ +struct Msg { + Header header; + adl::uint8_t data[0]; +} __packed; + + +struct Response { + Header header; + adl::uint32_t code; + adl::uint32_t msgLen; + + const char msg[0]; // Not null-terminated for error. +} __packed; + + +} -- Gitee From 73b7bf32bc4179a860440527f0fb454052316d39 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Mon, 8 Dec 2025 21:39:37 +0800 Subject: [PATCH 014/133] transport the shmvaddr data test to test-drivers --- kernel/src/object/tcb.c | 8 +- .../seL4_libs/libsel4debug/src/bootinfo.c | 14 +- projects/sel4runtime/src/env.c | 57 ++++ .../sel4test/apps/hello-world/src/mem_test.c | 248 ++++++++++++++++++ .../sel4test/apps/sel4test-driver/src/main.c | 53 +++- .../apps/sel4test-tests/src/tests/scheduler.c | 6 +- .../apps/sel4test-tests/src/tests/vspace.c | 13 - 7 files changed, 371 insertions(+), 28 deletions(-) diff --git a/kernel/src/object/tcb.c b/kernel/src/object/tcb.c index 4bbb82a..a99bea3 100755 --- a/kernel/src/object/tcb.c +++ b/kernel/src/object/tcb.c @@ -1230,8 +1230,8 @@ exception_t decodeSetPriority(cap_t cap, word_t length, word_t *buffer) tcb_t *authTCB = TCB_PTR(cap_thread_cap_get_capTCBPtr(authCap)); exception_t status = checkPrio(newPrio, authTCB); if (status != EXCEPTION_NONE) { - userError("TCB SetPriority: Requested priority %lu too high (max %lu).", - (unsigned long) newPrio, (unsigned long) authTCB->tcbMCP); + // userError("TCB SetPriority: Requested priority %lu too high (max %lu).", + // (unsigned long) newPrio, (unsigned long) authTCB->tcbMCP); return status; } @@ -1274,8 +1274,8 @@ exception_t decodeSetMCPriority(cap_t cap, word_t length, word_t *buffer) tcb_t *authTCB = TCB_PTR(cap_thread_cap_get_capTCBPtr(authCap)); exception_t status = checkPrio(newMcp, authTCB); if (status != EXCEPTION_NONE) { - userError("TCB SetMCPriority: Requested maximum controlled priority %lu too high (max %lu).", - (unsigned long) newMcp, (unsigned long) authTCB->tcbMCP); + // userError("TCB SetMCPriority: Requested maximum controlled priority %lu too high (max %lu).", + // (unsigned long) newMcp, (unsigned long) authTCB->tcbMCP); return status; } diff --git a/projects/seL4_libs/libsel4debug/src/bootinfo.c b/projects/seL4_libs/libsel4debug/src/bootinfo.c index 8b56b9e..a452fe9 100755 --- a/projects/seL4_libs/libsel4debug/src/bootinfo.c +++ b/projects/seL4_libs/libsel4debug/src/bootinfo.c @@ -30,13 +30,13 @@ void debug_print_bootinfo(seL4_BootInfo *info) printf("Paddr | Size | Device\n"); int sizes[CONFIG_WORD_SIZE] = {0}; - for (int i = 0; i < CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS && i < (info->untyped.end - info->untyped.start); i++) { - int index = info->untypedList[i].sizeBits; - assert(index < ARRAY_SIZE(sizes)); - sizes[index]++; - printf("%p | %zu | %d\n", (void *)info->untypedList[i].paddr, (size_t)info->untypedList[i].sizeBits, - (int)info->untypedList[i].isDevice); - } + // for (int i = 0; i < CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS && i < (info->untyped.end - info->untyped.start); i++) { + // int index = info->untypedList[i].sizeBits; + // assert(index < ARRAY_SIZE(sizes)); + // sizes[index]++; + // printf("%p | %zu | %d\n", (void *)info->untypedList[i].paddr, (size_t)info->untypedList[i].sizeBits, + // (int)info->untypedList[i].isDevice); + // } printf("Untyped summary\n"); for (int i = 0; i < ARRAY_SIZE(sizes); i++) { diff --git a/projects/sel4runtime/src/env.c b/projects/sel4runtime/src/env.c index 12d7b2c..c4b1cf3 100755 --- a/projects/sel4runtime/src/env.c +++ b/projects/sel4runtime/src/env.c @@ -11,6 +11,38 @@ #include #include +/* Use libsel4 debug printing rather than which isn't available + * in the cross-compile sysroot. Provide small helpers for string and hex + * output using seL4 debug syscalls. + */ + +static void sel4runtime_debug_puts(const char *s) +{ + /* seL4_DebugPutString is generated into the libsel4 stubs */ + // seL4_DebugPutString((char *)s); + return; +} + +static void sel4runtime_debug_puthex(unsigned long v) +{ + // char buf[2 + (sizeof(unsigned long) * 2) + 1]; + // char *p = &buf[sizeof(buf) - 1]; + // const char hex[] = "0123456789abcdef"; + // *p = '\0'; + // if (v == 0) { + // *--p = '0'; + // } else { + // while (v) { + // *--p = hex[v & 0xf]; + // v >>= 4; + // } + // } + // *--p = 'x'; + // *--p = '0'; + // seL4_DebugPutString(p); + return; +} + #include "init.h" #include "util.h" @@ -173,6 +205,9 @@ sel4runtime_uintptr_t sel4runtime_move_initial_tls(void *tls_memory) sel4runtime_set_tls_base(tls_base); if (env.initial_thread_ipc_buffer != SEL4RUNTIME_NULL) { + sel4runtime_debug_puts("Setting initial thread IPC buffer: "); + sel4runtime_debug_puthex((unsigned long)env.initial_thread_ipc_buffer); + sel4runtime_debug_puts("\n"); __sel4_ipc_buffer = env.initial_thread_ipc_buffer; } @@ -250,6 +285,10 @@ void __sel4runtime_load_env( auxv_t const auxv[] ) { + sel4runtime_debug_puts("sel4runtime: __sel4runtime_load_env argv0="); + sel4runtime_debug_puts((argc > 0 && argv && argv[0]) ? argv[0] : "(null)"); + sel4runtime_debug_puts("\n"); + empty_tls(); parse_auxv(auxv); parse_phdrs(); @@ -282,6 +321,11 @@ static void name_process(char const *name) static void parse_auxv(auxv_t const auxv[]) { for (int i = 0; auxv[i].a_type != AT_NULL; i++) { + sel4runtime_debug_puts("sel4runtime: parse_auxv["); + sel4runtime_debug_puthex((unsigned long)i); + sel4runtime_debug_puts("] type="); + sel4runtime_debug_puthex((unsigned long)auxv[i].a_type); + sel4runtime_debug_puts("\n"); switch (auxv[i].a_type) { case AT_PHENT: { env.program_header.size = auxv[i].a_un.a_val; @@ -303,14 +347,27 @@ static void parse_auxv(auxv_t const auxv[]) env.bootinfo = bootinfo; env.initial_thread_ipc_buffer = bootinfo->ipcBuffer; env.initial_thread_tcb = seL4_CapInitThreadTCB; + sel4runtime_debug_puts("sel4runtime: AT_SEL4_BOOT_INFO bootinfo="); + sel4runtime_debug_puthex((unsigned long)bootinfo); + sel4runtime_debug_puts(" ipcBuffer="); + sel4runtime_debug_puthex((unsigned long)bootinfo->ipcBuffer); + sel4runtime_debug_puts(" tcb="); + sel4runtime_debug_puthex((unsigned long)env.initial_thread_tcb); + sel4runtime_debug_puts("\n"); break; } case AT_SEL4_IPC_BUFFER_PTR: { env.initial_thread_ipc_buffer = auxv[i].a_un.a_ptr; + sel4runtime_debug_puts("sel4runtime: AT_SEL4_IPC_BUFFER_PTR -> "); + sel4runtime_debug_puthex((unsigned long)env.initial_thread_ipc_buffer); + sel4runtime_debug_puts("\n"); break; } case AT_SEL4_TCB: { env.initial_thread_tcb = auxv[i].a_un.a_val; + sel4runtime_debug_puts("sel4runtime: AT_SEL4_TCB -> "); + sel4runtime_debug_puthex((unsigned long)env.initial_thread_tcb); + sel4runtime_debug_puts("\n"); break; } default: diff --git a/projects/sel4test/apps/hello-world/src/mem_test.c b/projects/sel4test/apps/hello-world/src/mem_test.c index 7559ebe..af8960d 100644 --- a/projects/sel4test/apps/hello-world/src/mem_test.c +++ b/projects/sel4test/apps/hello-world/src/mem_test.c @@ -10,8 +10,195 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* forward declarations for test sections */ +static void basic_mem_kv_test(void); +static void stress_test(void); +static void fs_test(void); +static void pthread_test(void); +static void sel4_native_test(void); + +/* sel4 native threaded worker */ +#if defined(CONFIG_KERNEL_MCS) || 1 +/* small defaults used for bootstrap pool */ +#define SEL4_TEST_ALLOC_POOL_PAGES 40 +static char sel4_alloc_pool[SEL4_TEST_ALLOC_POOL_PAGES * PAGE_SIZE_4K]; + +static volatile int sel4_thread_done[8] = {0}; + +static void sel4_worker(void *arg0, void *arg1, void *ipc) { + intptr_t id = (intptr_t)arg0; + /* avoid any I/O from worker; just mark done */ + sel4_thread_done[id] = 1; + /* keep thread alive to avoid return-related faults */ + for (;;) { + seL4_Yield(); + } +} + +static void sel4_native_test(void) +{ + // const char *env = getenv("ENABLE_SEL4_THREAD_TEST"); + // if (!env) { + // printf("\n=== sel4 native thread test skipped (set ENABLE_SEL4_THREAD_TEST=1 to enable) ===\n"); + // return; + // } + printf("\n=== sel4 native thread test (enabled) ===\n"); + + /* init simple from bootinfo */ + simple_t simple; + printf("[mem_test] before platsupport_get_bootinfo()\n"); fflush(stdout); + seL4_BootInfo *bi = platsupport_get_bootinfo(); + printf("[mem_test] after platsupport_get_bootinfo(): %p\n", (void*)bi); fflush(stdout); + if (!bi) { printf("no bootinfo, skipping sel4 native test\n"); fflush(stdout); return; } + else { printf("get bootinfo success.\n"); fflush(stdout); } + simple_default_init_bootinfo(&simple, bi); + + /* bootstrap allocman and vka */ + printf("[mem_test] before bootstrap_use_current_simple()\n"); fflush(stdout); + allocman_t *allocman = bootstrap_use_current_simple(&simple, sizeof(sel4_alloc_pool), sel4_alloc_pool); + printf("[mem_test] after bootstrap_use_current_simple(): %p\n", (void*)allocman); fflush(stdout); + if (!allocman) { printf("allocman bootstrap failed\n"); fflush(stdout); return; } + vka_t vka; + allocman_make_vka(&vka, allocman); + printf("[mem_test] after allocman_make_vka()\n"); fflush(stdout); + + /* bootstrap vspace */ + sel4utils_alloc_data_t alloc_data; + vspace_t vspace; + seL4_CPtr pd = simple_get_pd(&simple); + printf("[mem_test] before second platsupport_get_bootinfo()\n"); fflush(stdout); + seL4_BootInfo *bootinfo = platsupport_get_bootinfo(); + printf("[mem_test] after second platsupport_get_bootinfo(): %p\n", (void*)bootinfo); fflush(stdout); + printf("[mem_test] before sel4utils_bootstrap_vspace_with_bootinfo_leaky()\n"); fflush(stdout); + int vres = sel4utils_bootstrap_vspace_with_bootinfo_leaky(&vspace, &alloc_data, pd, &vka, bootinfo); + printf("[mem_test] after sel4utils_bootstrap_vspace_with_bootinfo_leaky(): %d\n", vres); fflush(stdout); + if (vres) { + printf("bootstrap vspace failed\n"); fflush(stdout); + return; + } + + /* configure and start threads */ + const int nthreads = 4; + sel4utils_thread_t threads[nthreads]; + /* create a fault endpoint to catch thread faults */ + vka_object_t fault_ep; + if (vka_alloc_endpoint(&vka, &fault_ep) != 0) { + printf("[mem_test] failed to allocate fault endpoint\n"); fflush(stdout); + return; + } + for (int i = 0; i < nthreads; i++) { + /* Use sel4utils default config which sets reply cap, IPC buffer, priority, and fault EP */ + seL4_Word guard_data = api_make_guard_skip_word(seL4_WordBits - simple_get_cnode_size_bits(&simple)); + sel4utils_thread_config_t config = thread_config_default(&simple, + simple_get_cnode(&simple), + guard_data, + fault_ep.cptr, + 100); + if (config_set(CONFIG_KERNEL_MCS)) { + config.sched_params = sched_params_round_robin(config.sched_params, &simple, 0, 1000); + } + if (sel4utils_configure_thread_config(&vka, &vspace, &vspace, config, &threads[i]) != 0) { + printf("configure thread %d failed\n", i); + return; + } + printf("[mem_test] configured thread %d\n", i); fflush(stdout); + sel4_thread_done[i] = 0; + if (sel4utils_start_thread(&threads[i], sel4_worker, (void*)(intptr_t)i, NULL, 0) != 0) { + printf("start thread %d failed\n", i); + return; + } + int r = seL4_TCB_Resume(threads[i].tcb.cptr); + printf("[mem_test] started thread %d, Resume rc=%d\n", i, r); fflush(stdout); + } + + /* wait for completion */ + int all = 0; + printf("[mem_test] waiting for threads to finish...\n"); fflush(stdout); + while (!all) { + all = 1; + for (int i = 0; i < nthreads; i++) if (!sel4_thread_done[i]) { all = 0; break; } + if (!all) { + /* poll for any faults from worker threads */ + seL4_Word badge = 0; + seL4_MessageInfo_t tag = seL4_Poll(fault_ep.cptr, &badge); + if (seL4_MessageInfo_get_length(tag) != 0) { + /* A fault occurred; print some info */ + sel4utils_print_fault_message(tag, "mem_test worker"); + /* break to avoid tight loop */ + } + seL4_Yield(); + } + } + printf("[mem_test] threads finished\n"); fflush(stdout); + printf("sel4 native threads finished\n"); + + /* cleanup threads */ + for (int i = 0; i < nthreads; i++) sel4utils_clean_up_thread(&vka, &vspace, &threads[i]); + +} +#else +static void sel4_native_test(void) { printf("sel4 native test not supported in this build\n"); } +#endif void run_mem_tests(void) +{ + basic_mem_kv_test(); + stress_test(); + // sel4_native_test(); + // pthread_test(); + fs_test(); +} + +static void basic_mem_kv_test(void) +{ + /* ----- Basic xmalloc/xfree and minikv smoke tests ----- */ + printf("\n=== Basic mem/kv tests ===\n"); + xmalloc_init(NULL); + + void *s1 = xmalloc(32); + if (s1) { + printf("xmalloc small (32) OK: %p\n", s1); + xfree(s1); + printf("xfree small OK\n"); + } else { + printf("xmalloc small failed\n"); + } + + void *s2 = xmalloc(4096); /* larger than slab classes -> heap */ + if (s2) { + printf("xmalloc large (4096) returned %p\n", s2); + xfree(s2); + } else { + printf("xmalloc large returned NULL (expected if no heap)\n"); + } + + if (minikv_init(NULL) == 0) { + const char *k = "hello"; + void *val = (void *)0x1234; + if (minikv_set(k, val) == 0) { + printf("minikv set OK\n"); + void *g = minikv_get(k); + printf("minikv get: %p\n", g); + if (minikv_delete(k) == 0) printf("minikv delete OK\n"); else printf("minikv delete failed\n"); + } else { + printf("minikv set failed\n"); + } + } else { + printf("minikv_init failed\n"); + } +} + +static void stress_test(void) { /* ----- Stress tests for slab allocator and minikv ----- */ printf("\n=== Stress tests: xmalloc and minikv stress ===\n"); @@ -101,7 +288,68 @@ void run_mem_tests(void) } else { printf("minikv_init failed for stress tests\n"); } +} + +/* pthread test: optional, only when pthreads are available */ +#if defined(__has_include) +# if __has_include() +# include +# define HAVE_PTHREAD 1 +# endif +#endif + +#if defined(HAVE_PTHREAD) +static void *pthread_worker(void *arg) +{ + intptr_t id = (intptr_t)arg; + for (int i = 0; i < 1000; i++) { + void *p = xmalloc(32 + (i & 31)); + if (p) xfree(p); + if ((i & 127) == 0) { + /* do small kv operations locally to avoid global races */ + char k[32]; + snprintf(k, sizeof(k), "th%02ld-%04d", (long)id, i); + minikv_set(k, (void*)id); + minikv_get(k); + minikv_delete(k); + } + } + return NULL; +} +static void pthread_test(void) +{ + /* Only run pthread test when explicitly enabled via env var. */ + const char *env = getenv("ENABLE_PTHREAD_TEST"); + if (!env) { + printf("\n=== pthread test skipped (set ENABLE_PTHREAD_TEST=1 to enable) ===\n"); + return; + } + printf("\n=== pthread concurrency test (enabled) ===\n"); + xmalloc_init(NULL); + const int nthreads = 4; + pthread_t th[nthreads]; + for (int i = 0; i < nthreads; i++) { + int rc = pthread_create(&th[i], NULL, pthread_worker, (void*)(intptr_t)i); + if (rc != 0) { + printf("pthread_create failed (rc=%d) -- skipping pthread test\n", rc); + /* join any threads that were created */ + for (int j = 0; j < i; j++) pthread_join(th[j], NULL); + return; + } + } + for (int i = 0; i < nthreads; i++) pthread_join(th[i], NULL); + printf("pthread test finished\n"); +} +#else +static void pthread_test(void) +{ + printf("\n=== pthread test skipped (no pthreads available) ===\n"); +} +#endif + +static void fs_test(void) +{ /* ----- Test immfs (in-memory filesystem) ----- */ printf("\n=== immfs tests ===\n"); immfs_init(); diff --git a/projects/sel4test/apps/sel4test-driver/src/main.c b/projects/sel4test/apps/sel4test-driver/src/main.c index 7beb1a8..64593f5 100755 --- a/projects/sel4test/apps/sel4test-driver/src/main.c +++ b/projects/sel4test/apps/sel4test-driver/src/main.c @@ -375,7 +375,7 @@ void sel4test_stop_tests(test_result_t result, int tests_done, int tests_failed, } else { printf("All is well in the universe\n"); } - printf("\n\n"); + printf("\n"); } static int collate_tests(testcase_t *tests_in, int n, testcase_t *tests_out[], int out_index, @@ -735,6 +735,24 @@ static void sel4test_exit(int code) seL4_TCB_Suspend(seL4_CapInitThreadTCB); } +void printRawString(char* str) +{ + size_t max_len = 1024;/* 为安全起见,可以限制最大读长度,避免读到非法内存 */ + size_t i = 0; + putchar('"'); + while (i < max_len) { + char c = str[i]; + if (c == '\0') + { + break; + } + putchar(c); + i++; + } + putchar('"'); + putchar('\n'); +} + int main(void) { /* Set exit handler */ @@ -830,5 +848,38 @@ int main(void) test_assert_fatal(res == 0); ZF_LOGI("=== seL4 Test Driver completed ==="); + + printf("----------------ROOTSERVER--------------------\n"); + seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); + unsigned long long *vaddrs=(unsigned long long *)*Ptr2ShmemCommBuff; + + // 打印三个共享内存区域的虚拟地址 + printf("Shmem Comm VAddrs:\n"); + printf(" ROOT_Q VADDR: 0x%llx\n", vaddrs[0]); // [0] = ROOT_Q + printf(" SEL4_Q VADDR: 0x%llx\n", vaddrs[1]); // [1] = SEL4_Q + printf(" DATA VADDR: 0x%llx\n", vaddrs[2]); // [2] = DATA + printf("--------------------------------------------\n"); + // === 新增:边界访问测试(只测合法范围,避免 crash)=== + volatile char *root_q = (char*)vaddrs[0]; + volatile char *sel4_q = (char*)vaddrs[1]; + volatile char *data = (char*)vaddrs[2]; + + // 测试 ROOT_Q: 4KB → offset 0 和 4095 + root_q[0] = 'R'; + root_q[4095] = 'r'; + printf("ROOT_Q boundary test: OK\n"); + + // 测试 SEL4_Q: 4KB → offset 0 和 4095 + sel4_q[0] = 'S'; + sel4_q[4095] = 's'; + printf("SEL4_Q boundary test: OK\n"); + + // 测试 DATA: 4MB → offset 0 和 0x3FFFFF (4MB - 1) + data[0] = 'D'; + data[0x3FFFFF] = 'd'; // 4MB - 1 = 0x400000 - 1 = 0x3FFFFF + printf("DATA boundary test: OK\n"); + // === 边界测试结束 === + printRawString((char*)(vaddrs[1])); + return 0; } diff --git a/projects/sel4test/apps/sel4test-tests/src/tests/scheduler.c b/projects/sel4test/apps/sel4test-tests/src/tests/scheduler.c index a4f05eb..4d445c8 100755 --- a/projects/sel4test/apps/sel4test-tests/src/tests/scheduler.c +++ b/projects/sel4test/apps/sel4test-tests/src/tests/scheduler.c @@ -312,7 +312,7 @@ static int test_all_priorities(struct env *env) return sel4test_get_result(); } -DEFINE_TEST(SCHED0004, "Test threads at all priorities", test_all_priorities, true) +DEFINE_TESTQ(SCHED0004, "Test threads at all priorities", test_all_priorities, true) #define SCHED0005_HIGHEST_PRIO (seL4_MaxPrio - 2) /* @@ -424,7 +424,7 @@ static int test_set_priority(struct env *env) cleanup_helper(env, &thread2); return sel4test_get_result(); } -DEFINE_TEST(SCHED0005, "Test set priority", test_set_priority, true) +DEFINE_TESTQ(SCHED0005, "Test set priority", test_set_priority, true) #endif /* @@ -1067,7 +1067,7 @@ int test_one_periodic_thread(env_t env) return sel4test_get_result(); } -DEFINE_TEST(SCHED0012, "Test one periodic thread", test_one_periodic_thread, +DEFINE_TESTQ(SCHED0012, "Test one periodic thread", test_one_periodic_thread, config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER)) int diff --git a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c index cfe213e..f95b6a8 100755 --- a/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c +++ b/projects/sel4test/apps/sel4test-tests/src/tests/vspace.c @@ -144,19 +144,6 @@ test_unmap_after_delete(env_t env) /* unmap the frame */ seL4_ARM_Page_Unmap(frame); - printf("----------------ROOTSERVER--------------------\n"); - seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); - printf("Shmem Comm Buff Pptr: 0x%lx\n", (seL4_Word)Ptr2ShmemCommBuff); - unsigned long long *vaddrs=(unsigned long long *)*Ptr2ShmemCommBuff; - printf("Shmem Comm VAddrs ptr: 0x%llx\n", (seL4_Word)vaddrs); - - // 打印三个共享内存区域的虚拟地址 - // printf("Shmem Comm VAddrs:\n"); - // printf(" ROOT_Q VADDR: 0x%llx\n", vaddrs[0]); // [0] = ROOT_Q - // printf(" SEL4_Q VADDR: 0x%llx\n", vaddrs[1]); // [1] = SEL4_Q - // printf(" DATA VADDR: 0x%llx\n", vaddrs[2]); // [2] = DATA - // printf("--------------------------------------------\n"); - return sel4test_get_result(); } DEFINE_TESTQ(VSPACE0001, "Test unmapping a page after deleting the PD", test_unmap_after_delete, true) -- Gitee From 7df9592af0803496696170eddc35b30e851bea51 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Tue, 9 Dec 2025 16:26:50 +0800 Subject: [PATCH 015/133] synthesize xmalloc test and shm test --- .../apps/sel4test-driver/CMakeLists.txt | 2 +- .../sel4test/apps/sel4test-driver/src/init.h | 768 ++++++++++++++++++ .../sel4test/apps/sel4test-driver/src/main.c | 723 +---------------- .../apps/sel4test-driver/src/malloc_stub.c | 201 +++++ 4 files changed, 975 insertions(+), 719 deletions(-) create mode 100644 projects/sel4test/apps/sel4test-driver/src/init.h create mode 100644 projects/sel4test/apps/sel4test-driver/src/malloc_stub.c diff --git a/projects/sel4test/apps/sel4test-driver/CMakeLists.txt b/projects/sel4test/apps/sel4test-driver/CMakeLists.txt index bb4a6a1..21a7742 100755 --- a/projects/sel4test/apps/sel4test-driver/CMakeLists.txt +++ b/projects/sel4test/apps/sel4test-driver/CMakeLists.txt @@ -82,7 +82,7 @@ add_subdirectory(../sel4test-tests sel4test-tests) include(cpio) MakeCPIO(archive.o "$") -add_executable(sel4test-driver EXCLUDE_FROM_ALL ${static} archive.o) +add_executable(sel4test-driver EXCLUDE_FROM_ALL ${static} src/malloc_stub.c archive.o) target_include_directories(sel4test-driver PRIVATE "include") target_link_libraries( sel4test-driver diff --git a/projects/sel4test/apps/sel4test-driver/src/init.h b/projects/sel4test/apps/sel4test-driver/src/init.h new file mode 100644 index 0000000..b9addb3 --- /dev/null +++ b/projects/sel4test/apps/sel4test-driver/src/init.h @@ -0,0 +1,768 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include "test.h" +#include "timer.h" + +#include + +/* ammount of untyped memory to reserve for the driver (8mb) */ +#define DRIVER_UNTYPED_MEMORY (1 << 25) // 32MB for Phytium Pi +/* Number of untypeds to try and use to allocate the driver memory. + * if we cannot get 8mb with 16 untypeds then something is probably wrong */ +#define DRIVER_NUM_UNTYPEDS 16 + +/* dimensions of virtual memory for the allocator to use */ +#define ALLOCATOR_VIRTUAL_POOL_SIZE ((1 << seL4_PageBits) * 50) + +/* static memory for the allocator to bootstrap with */ +#define ALLOCATOR_STATIC_POOL_SIZE ((1 << seL4_PageBits) * 40) // 40 pages for Phytium Pi +static char allocator_mem_pool[ALLOCATOR_STATIC_POOL_SIZE]; + +/* static memory for virtual memory bootstrapping */ +static sel4utils_alloc_data_t data; + +/* environment encapsulating allocation interfaces etc */ +static struct driver_env env; +/* list of untypeds to give out to test processes */ +static vka_object_t untypeds[CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS]; +/* list of sizes (in bits) corresponding to untyped */ +static uint8_t untyped_size_bits_list[CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS]; + +extern char _cpio_archive[]; +extern char _cpio_archive_end[]; + +static elf_t tests_elf; + +/* initialise our runtime environment */ +static void init_env(driver_env_t env) +{ + allocman_t *allocman; + reservation_t virtual_reservation; + int error; + + /* create an allocator */ + printf("Creating allocator with pool size: %d bytes\n", ALLOCATOR_STATIC_POOL_SIZE); + allocman = bootstrap_use_current_simple(&env->simple, ALLOCATOR_STATIC_POOL_SIZE, allocator_mem_pool); + if (allocman == NULL) { + ZF_LOGF("Failed to create allocman"); + } + printf("Allocator created successfully at %p\n", allocman); + + /* create a vka (interface for interacting with the underlying allocator) */ + printf("Creating VKA interface\n"); + allocman_make_vka(&env->vka, allocman); + printf("VKA interface created successfully\n"); + + /* create a vspace (virtual memory management interface). We pass + * boot info not because it will use capabilities from it, but so + * it knows the address and will add it as a reserved region */ + printf("Getting BootInfo for VSpace initialization\n"); + seL4_BootInfo *bootinfo = platsupport_get_bootinfo(); + if (bootinfo == NULL) { + ZF_LOGF("BootInfo is NULL - critical failure"); + } + printf("BootInfo at %p, userImageFrames: %lu-%lu, userImagePaging: %lu-%lu\n", + bootinfo, bootinfo->userImageFrames.start, bootinfo->userImageFrames.end, + bootinfo->userImagePaging.start, bootinfo->userImagePaging.end); + printf("BootInfo untypedList: %lu-%lu, initThreadCNodeSizeBits: %lu, initThreadDomain: %lu\n", + bootinfo->untyped.start, bootinfo->untyped.end, + bootinfo->initThreadCNodeSizeBits, bootinfo->initThreadDomain); + + printf("Getting page directory from simple interface\n"); + seL4_CPtr pd_cap = simple_get_pd(&env->simple); + if (pd_cap == seL4_CapNull) { + ZF_LOGF("Failed to get page directory capability from simple interface"); + } + printf("Page directory capability: %lu\n", pd_cap); + + printf("Bootstrapping VSpace with BootInfo\n"); + error = sel4utils_bootstrap_vspace_with_bootinfo_leaky(&env->vspace, + &data, pd_cap, + &env->vka, bootinfo); + if (error) { + printf("Failed to bootstrap vspace, error: %d\n", error); + ZF_LOGF("Failed to bootstrap vspace, error: %d", error); + } + printf("VSpace bootstrapped successfully\n"); + + /* fill the allocator with virtual memory */ + printf("Reserving virtual memory pool of size: %d bytes\n", ALLOCATOR_VIRTUAL_POOL_SIZE); + void *vaddr; + virtual_reservation = vspace_reserve_range(&env->vspace, + ALLOCATOR_VIRTUAL_POOL_SIZE, seL4_AllRights, 1, &vaddr); + if (virtual_reservation.res == 0) { + printf("Failed to provide virtual memory for allocator, vaddr: %p\n", vaddr); + ZF_LOGF("Failed to provide virtual memory for allocator, vaddr: %p", vaddr); + } + printf("Virtual memory reserved successfully at %p\n", vaddr); + + printf("Configuring virtual pool for allocator\n"); + bootstrap_configure_virtual_pool(allocman, vaddr, + ALLOCATOR_VIRTUAL_POOL_SIZE, pd_cap); + printf("Virtual pool configured successfully\n"); + + printf("Initializing IO operations\n"); + + /* WORKAROUND: Skip FDT processing in hypervisor environment to avoid MMIO faults + * Root cause: FDT parsing in simple_get_extended_bootinfo causes invalid memory access + * that triggers MMIO faults at addresses like 0x47f8xx in hypervisor environment */ + printf("WORKAROUND: Manually initializing IO ops without FDT for hypervisor compatibility\n"); + memset(&env->ops, 0, sizeof(env->ops)); + + /* Initialize malloc ops */ + error = sel4platsupport_new_malloc_ops(&env->ops.malloc_ops); + ZF_LOGF_IF(error, "Failed to initialise malloc ops, error: %d", error); + + /* Initialize io mapper */ + error = sel4platsupport_new_io_mapper(&env->vspace, &env->vka, &env->ops.io_mapper); + ZF_LOGF_IF(error, "Failed to initialise io mapper, error: %d", error); + + /* Skip FDT initialization - set to NULL for hypervisor compatibility */ + env->ops.io_fdt.cookie = NULL; + env->ops.io_fdt.get_fn = NULL; + + /* Initialize IRQ ops */ + error = sel4platsupport_new_irq_ops(&env->ops.irq_ops, &env->vka, &env->simple, + DEFAULT_IRQ_INTERFACE_CONFIG, &env->ops.malloc_ops); + ZF_LOGF_IF(error, "Failed to initialise IRQ ops, error: %d", error); + + printf("IO operations initialized successfully (without FDT)\n"); + + printf("=== init_env completed successfully ===\n"); +} + +/* Free a list of objects */ +static void free_objects(vka_object_t *objects, unsigned int num) +{ + for (unsigned int i = 0; i < num; i++) { + vka_free_object(&env.vka, &objects[i]); + } +} + +/* Allocate untypeds till either a certain number of bytes is allocated + * or a certain number of untyped objects */ +static unsigned int allocate_untypeds(vka_object_t *untypeds, size_t bytes, unsigned int max_untypeds) +{ + unsigned int num_untypeds = 0; + size_t allocated = 0; + + ZF_LOGI("=== Starting allocate_untypeds ==="); + ZF_LOGI("Target bytes: %zu, max_untypeds: %u", bytes, max_untypeds); + + /* try to allocate as many of each possible untyped size as possible */ + for (uint8_t size_bits = 28; size_bits > PAGE_BITS_4K; size_bits--) { + int allocations_this_size = 0; + /* keep allocating until we run out, or if allocating would + * cause us to allocate too much memory*/ + while (num_untypeds < max_untypeds && + allocated + BIT(size_bits) <= bytes && + vka_alloc_untyped(&env.vka, size_bits, &untypeds[num_untypeds]) == 0) { + allocated += BIT(size_bits); + allocations_this_size++; + num_untypeds++; + } + if (allocations_this_size > 0) { + ZF_LOGI("Allocated %d untypeds of size %u bits (%zu bytes each)", + allocations_this_size, size_bits, BIT(size_bits)); + } + } + + ZF_LOGI("Total allocated: %u untypeds, %zu bytes", num_untypeds, allocated); + ZF_LOGI("=== allocate_untypeds completed ==="); + return num_untypeds; +} + +/* extract a large number of untypeds from the allocator */ +static unsigned int populate_untypeds(vka_object_t *untypeds) +{ + ZF_LOGI("=== Starting populate_untypeds ==="); + + /* First reserve some memory for the driver */ + ZF_LOGI("Reserving %zu bytes (%u MB) for driver with max %u untypeds", + DRIVER_UNTYPED_MEMORY, DRIVER_UNTYPED_MEMORY >> 20, DRIVER_NUM_UNTYPEDS); + vka_object_t reserve[DRIVER_NUM_UNTYPEDS]; + unsigned int reserve_num = allocate_untypeds(reserve, DRIVER_UNTYPED_MEMORY, DRIVER_NUM_UNTYPEDS); + ZF_LOGI("Reserved %u untypeds for driver", reserve_num); + + /* Now allocate everything else for the tests */ + ZF_LOGI("Allocating untypeds for tests (max %zu bytes, max %zu objects)", + (size_t)(1 << 26), ARRAY_SIZE(untyped_size_bits_list)); + unsigned int num_untypeds = allocate_untypeds(untypeds, (1 << 26), ARRAY_SIZE(untyped_size_bits_list)); + ZF_LOGI("Allocated %u untypeds for tests", num_untypeds); + + /* Fill out the size_bits list */ + for (unsigned int i = 0; i < num_untypeds; i++) { + untyped_size_bits_list[i] = untypeds[i].size_bits; + } + + /* Return reserve memory */ + ZF_LOGI("Freeing %u reserved objects", reserve_num); + free_objects(reserve, reserve_num); + + /* Return number of untypeds for tests */ + if (num_untypeds == 0) { + ZF_LOGF("No untypeds for tests!"); + } + + ZF_LOGI("=== populate_untypeds completed: %u untypeds available ===", num_untypeds); + return num_untypeds; +} + +static void init_timer(void) +{ + if (config_set(CONFIG_HAVE_TIMER)) { + int error; + + /* setup the timers and have our wrapper around simple capture the IRQ caps */ + printf("Initializing ltimer_default_init...\n"); + error = ltimer_default_init(&env.ltimer, env.ops, NULL, NULL); + ZF_LOGF_IF(error, "Failed to setup the timers"); + printf("ltimer_default_init completed\n"); + + /* Check timer IRQ configuration */ + if (env.ltimer.get_num_irqs != NULL) { + size_t num_irqs = env.ltimer.get_num_irqs(env.ltimer.data); + printf("Timer IRQs configured: %zu\n", num_irqs); + } else { + printf("WARNING: Timer get_num_irqs function is NULL - no IRQ support\n"); + } + + error = vka_alloc_notification(&env.vka, &env.timer_notify_test); + ZF_LOGF_IF(error, "Failed to allocate notification object for tests"); + + /* Allocate the timer notification if not already done (for platforms without IRQs) */ + if (env.timer_notification.cptr == seL4_CapNull) { + error = vka_alloc_notification(&env.vka, &env.timer_notification); + ZF_LOGF_IF(error, "Failed to allocate timer notification object"); + } + + error = seL4_TCB_BindNotification(simple_get_tcb(&env.simple), env.timer_notification.cptr); + ZF_LOGF_IF(error, "Failed to bind timer notification to sel4test-driver"); + + /* set up the timer manager */ + tm_init(&env.tm, &env.ltimer, &env.ops, 1); + printf("Timer initialization completed\n"); + } +} + +void sel4test_start_suite(const char *name) +{ + if (config_set(CONFIG_PRINT_XML)) { + printf("\n"); + } else { + printf("Starting test suite %s\n", name); + } +} + +void sel4test_start_test(const char *name, int n) +{ + if (config_set(CONFIG_PRINT_XML)) { + printf("\t\n", "sel4test", name); + } else { + printf("Starting test %d: %s\n", n, name); + } + sel4test_reset(); + sel4test_start_printf_buffer(); +} + +void sel4test_end_test(test_result_t result) +{ + sel4test_end_printf_buffer(); + test_check(result == SUCCESS); + + if (config_set(CONFIG_PRINT_XML)) { + printf("\t\n"); + } + + if (config_set(CONFIG_HAVE_TIMER)) { + timer_reset(&env); + } +} + +void sel4test_end_suite(int num_tests, int num_tests_passed, int skipped_tests) +{ + if (config_set(CONFIG_PRINT_XML)) { + printf("\n"); + } else { + if (num_tests_passed != num_tests) { + printf("Test suite failed. %d/%d tests passed.\n", num_tests_passed, num_tests); + } else { + printf("Test suite passed. %d tests passed. %d tests disabled.\n", num_tests, skipped_tests); + } + } +} + +void sel4test_stop_tests(test_result_t result, int tests_done, int tests_failed, int num_tests, int skipped_tests) +{ + /* if its a special abort case, output why we are aborting */ + switch (result) { + case ABORT: + printf("Halting on fatal assertion...\n"); + break; + case FAILURE: + assert(config_set(CONFIG_TESTPRINTER_HALT_ON_TEST_FAILURE)); + printf("Halting on first test failure\n"); + break; + default: + /* nothing to output if its successful */ + break; + } + + /* last test - test all tests ran */ + sel4test_start_test("Test all tests ran", num_tests + 1); + /* Account for skipped tests: tests_done should equal num_tests (enabled tests) */ + printf("Debug: tests_done=%d, num_tests=%d, skipped_tests=%d\n", tests_done, num_tests, skipped_tests); + test_eq(tests_done, num_tests); + if (sel4test_get_result() != SUCCESS) { + tests_failed++; + } + tests_done++; + num_tests++; + sel4test_end_test(sel4test_get_result()); + + sel4test_end_suite(tests_done, tests_done - tests_failed, skipped_tests); + + if (tests_failed > 0) { + printf("*** FAILURES DETECTED ***\n"); + } else if (tests_done < num_tests) { + printf("*** ALL tests not run ***\n"); + } else { + printf("All is well in the universe\n"); + } + printf("\n"); +} + +static int collate_tests(testcase_t *tests_in, int n, testcase_t *tests_out[], int out_index, + regex_t *reg, int *skipped_tests) +{ + for (int i = 0; i < n; i++) { + /* make sure the string is null terminated */ + tests_in[i].name[TEST_NAME_MAX - 1] = '\0'; + if (regexec(reg, tests_in[i].name, 0, NULL, 0) == 0) { + if (tests_in[i].enabled) { + tests_out[out_index] = &tests_in[i]; + out_index++; + } else { + (*skipped_tests)++; + } + } + } + + return out_index; +} + +void sel4test_run_tests(struct driver_env *e) +{ + /* Iterate through test types. */ + int max_test_types = (int)(__stop__test_type - __start__test_type); + struct test_type *test_types[max_test_types]; + int num_test_types = 0; + for (struct test_type *i = __start__test_type; i < __stop__test_type; i++) { + test_types[num_test_types] = i; + num_test_types++; + } + + /* Ensure we iterate through test types in order of ID. */ + qsort(test_types, num_test_types, sizeof(struct test_type *), test_type_comparator); + + /* Count how many tests actually exist and allocate space for them */ + int driver_tests = (int)(__stop__test_case - __start__test_case); + uint64_t tc_size = 0; + testcase_t *sel4test_tests = (testcase_t *) sel4utils_elf_get_section(&tests_elf, "_test_case", &tc_size); + if (sel4test_tests == NULL) { + ZF_LOGF(TESTS_APP": Failed to find section: _test_case"); + } + int tc_tests = tc_size / sizeof(testcase_t); + int all_tests = driver_tests + tc_tests; + testcase_t *tests[all_tests]; + + /* Extract and filter the tests based on the regex */ + regex_t reg; + int error = regcomp(®, CONFIG_TESTPRINTER_REGEX, REG_EXTENDED | REG_NOSUB); + ZF_LOGF_IF(error, "Error compiling regex \"%s\"", CONFIG_TESTPRINTER_REGEX); + + int skipped_tests = 0; + /* get all the tests in the test case section in the driver */ + int num_tests = collate_tests(__start__test_case, driver_tests, tests, 0, ®, &skipped_tests); + /* get all the tests in the sel4test_tests app */ + num_tests = collate_tests(sel4test_tests, tc_tests, tests, num_tests, ®, &skipped_tests); + + /* finished with regex */ + regfree(®); + + /* Sort the tests to remove any non determinism in test ordering */ + qsort(tests, num_tests, sizeof(testcase_t *), test_comparator); + + /* Now that they are sorted we can easily ensure there are no duplicate tests. + * this just ensures some sanity as if there are duplicates, they could have some + * arbitrary ordering, which might result in difficulty reproducing test failures */ + for (int i = 1; i < num_tests; i++) { + ZF_LOGF_IF(strcmp(tests[i]->name, tests[i - 1]->name) == 0, "tests have no strict order! %s %s", + tests[i]->name, tests[i - 1]->name); + } + + /* Check that we don't miss any tests because of an undeclared test type */ + int tests_done = 0; + int tests_failed = 0; + + sel4test_start_suite("sel4test"); + /* First: test that there are tests to run */ + sel4test_start_test("Test that there are tests", tests_done); + test_gt(num_tests, 0); + sel4test_end_test(sel4test_get_result()); + tests_done++; + + /* Iterate through test types so that we run them in order of test type, then name. + * Test types are ordered by ID in test.h. */ + for (int tt = 0; tt < num_test_types; tt++) { + /* set up */ + if (test_types[tt]->set_up_test_type != NULL) { + test_types[tt]->set_up_test_type((uintptr_t)e); + } + + for (int i = 0; i < num_tests; i++) { + if (tests[i]->test_type == test_types[tt]->id) { + sel4test_start_test(tests[i]->name, tests_done); + if (test_types[tt]->set_up != NULL) { + test_types[tt]->set_up((uintptr_t)e); + } + + test_result_t result = test_types[tt]->run_test(tests[i], (uintptr_t)e); + + if (test_types[tt]->tear_down != NULL) { + test_types[tt]->tear_down((uintptr_t)e); + } + sel4test_end_test(result); + + if (result != SUCCESS) { + tests_failed++; + if (config_set(CONFIG_TESTPRINTER_HALT_ON_TEST_FAILURE) || result == ABORT) { + sel4test_stop_tests(result, tests_done + 1, tests_failed, num_tests + 1, skipped_tests); + return; + } + } + tests_done++; + } + } + + /* tear down */ + if (test_types[tt]->tear_down_test_type != NULL) { + test_types[tt]->tear_down_test_type((uintptr_t)e); + } + } + + /* and we're done */ + sel4test_stop_tests(SUCCESS, tests_done, tests_failed, num_tests + 1, skipped_tests); +} + +void *main_continued(void *arg UNUSED) +{ + + /* elf region data */ + int num_elf_regions; + sel4utils_elf_region_t elf_regions[MAX_REGIONS]; + + printf("=== main_continued started ===\n"); + printf("env.vspace pointer: %p\n", &env.vspace); + printf("env.vka pointer: %p\n", &env.vka); + printf("env.vspace content check:\n"); + printf(" env.vspace.data: %p\n", env.vspace.data); + printf(" env.vspace.new_pages: %p\n", env.vspace.new_pages); + printf(" env.vspace.reserve_range_at: %p\n", env.vspace.reserve_range_at); + printf("Validating vspace structure...\n"); + if (env.vspace.data == NULL) { + printf("ERROR: env.vspace.data is NULL!\n"); + } + if (env.vspace.new_pages == NULL) { + printf("ERROR: env.vspace.new_pages function pointer is NULL!\n"); + } + if (env.vspace.reserve_range_at == NULL) { + printf("ERROR: env.vspace.reserve_range_at function pointer is NULL!\n"); + } + + unsigned long elf_size; + unsigned long cpio_len = _cpio_archive_end - _cpio_archive; + const void *elf_file = cpio_get_file(_cpio_archive, cpio_len, TESTS_APP, &elf_size); + ZF_LOGF_IF(elf_file == NULL, "Error: failed to lookup ELF file"); + int status = elf_newFile(elf_file, elf_size, &tests_elf); + ZF_LOGF_IF(status, "Error: invalid ELF file"); + + /* Print welcome banner. */ + printf("\n"); + printf("seL4 Test\n"); + printf("=========\n"); + printf("\n"); + + int error; + + /* allocate a piece of device untyped memory for the frame tests, + * note that spike doesn't have any device untypes so the tests that require device untypes are turned off */ + if (!config_set(CONFIG_PLAT_SPIKE)) { + bool allocated = false; + int untyped_count = simple_get_untyped_count(&env.simple); + for (int i = 0; i < untyped_count; i++) { + bool device = false; + uintptr_t ut_paddr = 0; + size_t ut_size_bits = 0; + seL4_CPtr ut_cptr = simple_get_nth_untyped(&env.simple, i, &ut_size_bits, &ut_paddr, &device); + if (device) { + error = vka_alloc_frame_at(&env.vka, seL4_PageBits, ut_paddr, &env.device_obj); + if (!error) { + allocated = true; + /* we've allocated a single device frame and that's all we need */ + break; + } + } + } + ZF_LOGF_IF(allocated == false, "Failed to allocate a device frame for the frame tests"); + } + + /* allocate lots of untyped memory for tests to use */ + printf("About to populate untypeds...\n"); + env.num_untypeds = populate_untypeds(untypeds); + env.untypeds = untypeds; + printf("Untypeds populated: %u\n", env.num_untypeds); + + /* create a frame that will act as the init data, we can then map that + * in to target processes */ + printf("About to allocate init data frame, vspace at: %p\n", &env.vspace); + printf("Before vspace_new_pages call:\n"); + printf(" env.vspace.data: %p\n", env.vspace.data); + printf(" env.vspace.new_pages: %p\n", env.vspace.new_pages); + printf(" Calling vspace_new_pages(&env.vspace=%p, seL4_AllRights, 1, PAGE_BITS_4K)\n", &env.vspace); + + if (&env.vspace == NULL) { + printf("ERROR: env.vspace is NULL!\n"); + ZF_LOGF("env.vspace is NULL!"); + } + if (env.vspace.data == NULL) { + printf("ERROR: env.vspace.data is NULL before vspace_new_pages!\n"); + ZF_LOGF("env.vspace.data is NULL before vspace_new_pages!"); + } + + env.init = (test_init_data_t *) vspace_new_pages(&env.vspace, seL4_AllRights, 1, PAGE_BITS_4K); + assert(env.init != NULL); + printf("Init data frame allocated at: %p\n", env.init); + + /* copy the untyped size bits list across to the init frame */ + memcpy(env.init->untyped_size_bits_list, untyped_size_bits_list, sizeof(uint8_t) * env.num_untypeds); + + /* parse elf region data about the test image to pass to the tests app */ + // printf("About to parse ELF regions...\n"); + // num_elf_regions = sel4utils_elf_num_regions(&tests_elf); + // assert(num_elf_regions <= MAX_REGIONS); + // printf("TEST 0001\n"); + // sel4utils_elf_reserve(NULL, &tests_elf, elf_regions); + // printf("TEST 0002\n"); + + // /* copy the region list for the process to clone itself */ + // memcpy(env.init->elf_regions, elf_regions, sizeof(sel4utils_elf_region_t) * num_elf_regions); + // env.init->num_elf_regions = num_elf_regions; + // printf("env num_elf_regions: %d\n", num_elf_regions); + + /* setup init data that won't change test-to-test */ + env.init->priority = seL4_MaxPrio - 1; + if (plat_init) { + plat_init(&env); + } + + /* Allocate a reply object for the RT kernel. */ + if (config_set(CONFIG_KERNEL_MCS)) { + error = vka_alloc_reply(&env.vka, &env.reply); + ZF_LOGF_IF(error, "Failed to allocate reply"); + } + + /* now run the tests */ + sel4test_run_tests(&env); + + return NULL; +} + +/* Note that the following globals are place here because it is not expected that + * this function be refactored out of sel4test-driver in its current form. */ +/* Number of objects to track allocation of. Currently all serial devices are + * initialised with a single Frame object. Future devices may need more than 1. + */ +#define NUM_ALLOC_AT_TO_TRACK 1 +/* Static global to store the original vka_utspace_alloc_at function. It + * isn't expected for this to dynamically change after initialisation.*/ +static vka_utspace_alloc_at_fn vka_utspace_alloc_at_base; +/* State that serial_utspace_alloc_at_fn uses to determine whether to cache + * allocations. It is intended that this flag gets set before the serial device + * is initialised and then unset afterwards. */ +static bool serial_utspace_record = false; + +typedef struct uspace_alloc_at_args { + uintptr_t paddr; + seL4_Word type; + seL4_Word size_bits; + cspacepath_t dest; +} uspace_alloc_at_args_t; +/* This instance of vka_utspace_alloc_at_fn will keep a record of allocations up + * to NUM_ALLOC_AT_TO_TRACK while serial_utspace_record is set. When serial_utspace_record + * is unset, any allocations matching recorded allocations will instead copy the cap + * that was originally allocated. These subsequent allocations cannot be freed using + * vka_utspace_free and instead the caps would have to be manually deleted. + * Freeing these objects via vka_utspace_free would require also wrapping that function.*/ +static int serial_utspace_alloc_at_fn(void *data, const cspacepath_t *dest, seL4_Word type, seL4_Word size_bits, + uintptr_t paddr, seL4_Word *cookie) +{ + static uspace_alloc_at_args_t args_prev[NUM_ALLOC_AT_TO_TRACK] = {}; + static size_t num_alloc = 0; + + ZF_LOGF_IF(!vka_utspace_alloc_at_base, "vka_utspace_alloc_at_base not initialised."); + if (!serial_utspace_record) { + for (int i = 0; i < num_alloc; i++) { + if (paddr == args_prev[i].paddr && + type == args_prev[i].type && + size_bits == args_prev[i].size_bits) { + return vka_cnode_copy(dest, &args_prev[i].dest, seL4_AllRights); + } + } + return vka_utspace_alloc_at_base(data, dest, type, size_bits, paddr, cookie); + } else { + ZF_LOGF_IF(num_alloc >= NUM_ALLOC_AT_TO_TRACK, "Trying to allocate too many utspace objects"); + int ret = vka_utspace_alloc_at_base(data, dest, type, size_bits, paddr, cookie); + if (ret) { + return ret; + } + uspace_alloc_at_args_t a = {.paddr = paddr, .type = type, .size_bits = size_bits, .dest = *dest}; + args_prev[num_alloc] = a; + num_alloc++; + return ret; + + } +} + +static ps_irq_register_fn_t irq_register_fn_copy; + +static irq_id_t sel4test_timer_irq_register(UNUSED void *cookie, ps_irq_t irq, irq_callback_fn_t callback, + void *callback_data) +{ + static int num_timer_irqs = 0; + + int error; + + ZF_LOGF_IF(!callback, "Passed in a NULL callback"); + + ZF_LOGF_IF(num_timer_irqs >= MAX_TIMER_IRQS, "Trying to register too many timer IRQs"); + + /* Allocate the IRQ */ + error = sel4platsupport_copy_irq_cap(&env.vka, &env.simple, &irq, + &env.timer_irqs[num_timer_irqs].handler_path); + ZF_LOGF_IF(error, "Failed to allocate IRQ handler"); + + /* Allocate the root notifitcation if we haven't already done so */ + if (env.timer_notification.cptr == seL4_CapNull) { + error = vka_alloc_notification(&env.vka, &env.timer_notification); + ZF_LOGF_IF(error, "Failed to allocate notification object"); + } + + /* Mint a notification for the IRQ handler to pair with */ + error = vka_cspace_alloc_path(&env.vka, &env.badged_timer_notifications[num_timer_irqs]); + ZF_LOGF_IF(error, "Failed to allocate path for the badged notification"); + cspacepath_t root_notification_path = {0}; + vka_cspace_make_path(&env.vka, env.timer_notification.cptr, &root_notification_path); + error = vka_cnode_mint(&env.badged_timer_notifications[num_timer_irqs], &root_notification_path, + seL4_AllRights, BIT(num_timer_irqs)); + ZF_LOGF_IF(error, "Failed to mint notification for timer"); + + /* Pair the notification and the handler */ + error = seL4_IRQHandler_SetNotification(env.timer_irqs[num_timer_irqs].handler_path.capPtr, + env.badged_timer_notifications[num_timer_irqs].capPtr); + ZF_LOGF_IF(error, "Failed to pair the notification and handler together"); + + /* Ack the handler so interrupts can come in */ + error = seL4_IRQHandler_Ack(env.timer_irqs[num_timer_irqs].handler_path.capPtr); + ZF_LOGF_IF(error, "Failed to ack the IRQ handler"); + + /* Fill out information about the callbacks */ + env.timer_cbs[num_timer_irqs].callback = callback; + env.timer_cbs[num_timer_irqs].callback_data = callback_data; + + return num_timer_irqs++; +} + +/* When the root task exists, it should simply suspend itself */ +static void sel4test_exit(int code) +{ + seL4_TCB_Suspend(seL4_CapInitThreadTCB); +} + + +/* xmalloc_init provided by malloc_stub.c */ +void xmalloc_init(driver_env_t env); +/* test allocator API */ +void *xmalloc(size_t size); +void xfree(void *ptr); +/* minikv API */ +int minikv_init(driver_env_t env); +int minikv_set(const char *key, void *value); +void *minikv_get(const char *key); +int minikv_delete(const char *key); +void minikv_destroy(void); + + +// Testsuite +int test_malloc_free(driver_env_t env); +int test_minikv(driver_env_t env); +void sel4test_driver_init(void) +{ + printf("=== sel4test_driver_init started ===\n"); + + /* initialize the environment */ + init_env(&env); + + /* initialize timer */ + init_timer(); + + /* wrap the vka_utspace_alloc_at function to track serial device allocations */ + vka_utspace_alloc_at_base = env.vka.utspace_alloc_at; + env.vka.utspace_alloc_at = serial_utspace_alloc_at_fn; + + /* initialize xmalloc */ + xmalloc_init(&env); + + /* initialize minikv */ + // int error = minikv_init(&env); + // ZF_LOGF_IF(error, "Failed to initialize minikv"); + + printf("=== sel4test_driver_init completed ===\n"); +} diff --git a/projects/sel4test/apps/sel4test-driver/src/main.c b/projects/sel4test/apps/sel4test-driver/src/main.c index 64593f5..c9133a0 100755 --- a/projects/sel4test/apps/sel4test-driver/src/main.c +++ b/projects/sel4test/apps/sel4test-driver/src/main.c @@ -16,724 +16,9 @@ #include #include +#include "init.h" -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include - -#include -#include "test.h" -#include "timer.h" - -#include - -/* ammount of untyped memory to reserve for the driver (8mb) */ -#define DRIVER_UNTYPED_MEMORY (1 << 25) // 32MB for Phytium Pi -/* Number of untypeds to try and use to allocate the driver memory. - * if we cannot get 8mb with 16 untypeds then something is probably wrong */ -#define DRIVER_NUM_UNTYPEDS 16 - -/* dimensions of virtual memory for the allocator to use */ -#define ALLOCATOR_VIRTUAL_POOL_SIZE ((1 << seL4_PageBits) * 50) - -/* static memory for the allocator to bootstrap with */ -#define ALLOCATOR_STATIC_POOL_SIZE ((1 << seL4_PageBits) * 40) // 40 pages for Phytium Pi -static char allocator_mem_pool[ALLOCATOR_STATIC_POOL_SIZE]; - -/* static memory for virtual memory bootstrapping */ -static sel4utils_alloc_data_t data; - -/* environment encapsulating allocation interfaces etc */ -static struct driver_env env; -/* list of untypeds to give out to test processes */ -static vka_object_t untypeds[CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS]; -/* list of sizes (in bits) corresponding to untyped */ -static uint8_t untyped_size_bits_list[CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS]; - -extern char _cpio_archive[]; -extern char _cpio_archive_end[]; - -static elf_t tests_elf; - -/* initialise our runtime environment */ -static void init_env(driver_env_t env) -{ - allocman_t *allocman; - reservation_t virtual_reservation; - int error; - - printf("=== Starting init_env for Phytium Pi platform ==="); - - /* create an allocator */ - printf("Creating allocator with pool size: %d bytes\n", ALLOCATOR_STATIC_POOL_SIZE); - allocman = bootstrap_use_current_simple(&env->simple, ALLOCATOR_STATIC_POOL_SIZE, allocator_mem_pool); - if (allocman == NULL) { - ZF_LOGF("Failed to create allocman"); - } - printf("Allocator created successfully at %p\n", allocman); - - /* create a vka (interface for interacting with the underlying allocator) */ - printf("Creating VKA interface\n"); - allocman_make_vka(&env->vka, allocman); - printf("VKA interface created successfully\n"); - - /* create a vspace (virtual memory management interface). We pass - * boot info not because it will use capabilities from it, but so - * it knows the address and will add it as a reserved region */ - printf("Getting BootInfo for VSpace initialization\n"); - seL4_BootInfo *bootinfo = platsupport_get_bootinfo(); - if (bootinfo == NULL) { - ZF_LOGF("BootInfo is NULL - critical failure for Phytium Pi platform"); - } - printf("BootInfo at %p, userImageFrames: %lu-%lu, userImagePaging: %lu-%lu\n", - bootinfo, bootinfo->userImageFrames.start, bootinfo->userImageFrames.end, - bootinfo->userImagePaging.start, bootinfo->userImagePaging.end); - printf("BootInfo untypedList: %lu-%lu, initThreadCNodeSizeBits: %lu, initThreadDomain: %lu\n", - bootinfo->untyped.start, bootinfo->untyped.end, - bootinfo->initThreadCNodeSizeBits, bootinfo->initThreadDomain); - - printf("Getting page directory from simple interface\n"); - seL4_CPtr pd_cap = simple_get_pd(&env->simple); - if (pd_cap == seL4_CapNull) { - ZF_LOGF("Failed to get page directory capability from simple interface"); - } - printf("Page directory capability: %lu\n", pd_cap); - - printf("Bootstrapping VSpace with BootInfo\n"); - error = sel4utils_bootstrap_vspace_with_bootinfo_leaky(&env->vspace, - &data, pd_cap, - &env->vka, bootinfo); - if (error) { - printf("Failed to bootstrap vspace, error: %d\n", error); - ZF_LOGF("Failed to bootstrap vspace, error: %d", error); - } - printf("VSpace bootstrapped successfully\n"); - - /* fill the allocator with virtual memory */ - printf("Reserving virtual memory pool of size: %d bytes\n", ALLOCATOR_VIRTUAL_POOL_SIZE); - void *vaddr; - virtual_reservation = vspace_reserve_range(&env->vspace, - ALLOCATOR_VIRTUAL_POOL_SIZE, seL4_AllRights, 1, &vaddr); - if (virtual_reservation.res == 0) { - printf("Failed to provide virtual memory for allocator, vaddr: %p\n", vaddr); - ZF_LOGF("Failed to provide virtual memory for allocator, vaddr: %p", vaddr); - } - printf("Virtual memory reserved successfully at %p\n", vaddr); - - printf("Configuring virtual pool for allocator\n"); - bootstrap_configure_virtual_pool(allocman, vaddr, - ALLOCATOR_VIRTUAL_POOL_SIZE, pd_cap); - printf("Virtual pool configured successfully\n"); - - printf("Initializing IO operations\n"); - - /* WORKAROUND: Skip FDT processing in hypervisor environment to avoid MMIO faults - * Root cause: FDT parsing in simple_get_extended_bootinfo causes invalid memory access - * that triggers MMIO faults at addresses like 0x47f8xx in hypervisor environment */ - printf("WORKAROUND: Manually initializing IO ops without FDT for hypervisor compatibility\n"); - memset(&env->ops, 0, sizeof(env->ops)); - - /* Initialize malloc ops */ - error = sel4platsupport_new_malloc_ops(&env->ops.malloc_ops); - ZF_LOGF_IF(error, "Failed to initialise malloc ops, error: %d", error); - - /* Initialize io mapper */ - error = sel4platsupport_new_io_mapper(&env->vspace, &env->vka, &env->ops.io_mapper); - ZF_LOGF_IF(error, "Failed to initialise io mapper, error: %d", error); - - /* Skip FDT initialization - set to NULL for hypervisor compatibility */ - env->ops.io_fdt.cookie = NULL; - env->ops.io_fdt.get_fn = NULL; - - /* Initialize IRQ ops */ - error = sel4platsupport_new_irq_ops(&env->ops.irq_ops, &env->vka, &env->simple, - DEFAULT_IRQ_INTERFACE_CONFIG, &env->ops.malloc_ops); - ZF_LOGF_IF(error, "Failed to initialise IRQ ops, error: %d", error); - - printf("IO operations initialized successfully (without FDT)\n"); - - printf("=== init_env completed successfully ===\n"); -} - -/* Free a list of objects */ -static void free_objects(vka_object_t *objects, unsigned int num) -{ - for (unsigned int i = 0; i < num; i++) { - vka_free_object(&env.vka, &objects[i]); - } -} - -/* Allocate untypeds till either a certain number of bytes is allocated - * or a certain number of untyped objects */ -static unsigned int allocate_untypeds(vka_object_t *untypeds, size_t bytes, unsigned int max_untypeds) -{ - unsigned int num_untypeds = 0; - size_t allocated = 0; - - ZF_LOGI("=== Starting allocate_untypeds ==="); - ZF_LOGI("Target bytes: %zu, max_untypeds: %u", bytes, max_untypeds); - - /* try to allocate as many of each possible untyped size as possible */ - for (uint8_t size_bits = 28; size_bits > PAGE_BITS_4K; size_bits--) { - int allocations_this_size = 0; - /* keep allocating until we run out, or if allocating would - * cause us to allocate too much memory*/ - while (num_untypeds < max_untypeds && - allocated + BIT(size_bits) <= bytes && - vka_alloc_untyped(&env.vka, size_bits, &untypeds[num_untypeds]) == 0) { - allocated += BIT(size_bits); - allocations_this_size++; - num_untypeds++; - } - if (allocations_this_size > 0) { - ZF_LOGI("Allocated %d untypeds of size %u bits (%zu bytes each)", - allocations_this_size, size_bits, BIT(size_bits)); - } - } - - ZF_LOGI("Total allocated: %u untypeds, %zu bytes", num_untypeds, allocated); - ZF_LOGI("=== allocate_untypeds completed ==="); - return num_untypeds; -} - -/* extract a large number of untypeds from the allocator */ -static unsigned int populate_untypeds(vka_object_t *untypeds) -{ - ZF_LOGI("=== Starting populate_untypeds ==="); - - /* First reserve some memory for the driver */ - ZF_LOGI("Reserving %zu bytes (%u MB) for driver with max %u untypeds", - DRIVER_UNTYPED_MEMORY, DRIVER_UNTYPED_MEMORY >> 20, DRIVER_NUM_UNTYPEDS); - vka_object_t reserve[DRIVER_NUM_UNTYPEDS]; - unsigned int reserve_num = allocate_untypeds(reserve, DRIVER_UNTYPED_MEMORY, DRIVER_NUM_UNTYPEDS); - ZF_LOGI("Reserved %u untypeds for driver", reserve_num); - - /* Now allocate everything else for the tests */ - ZF_LOGI("Allocating untypeds for tests (max %zu bytes, max %zu objects)", - (size_t)(1 << 26), ARRAY_SIZE(untyped_size_bits_list)); - unsigned int num_untypeds = allocate_untypeds(untypeds, (1 << 26), ARRAY_SIZE(untyped_size_bits_list)); - ZF_LOGI("Allocated %u untypeds for tests", num_untypeds); - - /* Fill out the size_bits list */ - for (unsigned int i = 0; i < num_untypeds; i++) { - untyped_size_bits_list[i] = untypeds[i].size_bits; - } - - /* Return reserve memory */ - ZF_LOGI("Freeing %u reserved objects", reserve_num); - free_objects(reserve, reserve_num); - - /* Return number of untypeds for tests */ - if (num_untypeds == 0) { - ZF_LOGF("No untypeds for tests!"); - } - - ZF_LOGI("=== populate_untypeds completed: %u untypeds available ===", num_untypeds); - return num_untypeds; -} - -static void init_timer(void) -{ - if (config_set(CONFIG_HAVE_TIMER)) { - int error; - - /* setup the timers and have our wrapper around simple capture the IRQ caps */ - printf("Initializing ltimer_default_init...\n"); - error = ltimer_default_init(&env.ltimer, env.ops, NULL, NULL); - ZF_LOGF_IF(error, "Failed to setup the timers"); - printf("ltimer_default_init completed\n"); - - /* Check timer IRQ configuration */ - if (env.ltimer.get_num_irqs != NULL) { - size_t num_irqs = env.ltimer.get_num_irqs(env.ltimer.data); - printf("Timer IRQs configured: %zu\n", num_irqs); - } else { - printf("WARNING: Timer get_num_irqs function is NULL - no IRQ support\n"); - } - - error = vka_alloc_notification(&env.vka, &env.timer_notify_test); - ZF_LOGF_IF(error, "Failed to allocate notification object for tests"); - - /* Allocate the timer notification if not already done (for platforms without IRQs) */ - if (env.timer_notification.cptr == seL4_CapNull) { - error = vka_alloc_notification(&env.vka, &env.timer_notification); - ZF_LOGF_IF(error, "Failed to allocate timer notification object"); - } - - error = seL4_TCB_BindNotification(simple_get_tcb(&env.simple), env.timer_notification.cptr); - ZF_LOGF_IF(error, "Failed to bind timer notification to sel4test-driver"); - - /* set up the timer manager */ - tm_init(&env.tm, &env.ltimer, &env.ops, 1); - printf("Timer initialization completed\n"); - } -} - -void sel4test_start_suite(const char *name) -{ - if (config_set(CONFIG_PRINT_XML)) { - printf("\n"); - } else { - printf("Starting test suite %s\n", name); - } -} - -void sel4test_start_test(const char *name, int n) -{ - if (config_set(CONFIG_PRINT_XML)) { - printf("\t\n", "sel4test", name); - } else { - printf("Starting test %d: %s\n", n, name); - } - sel4test_reset(); - sel4test_start_printf_buffer(); -} - -void sel4test_end_test(test_result_t result) -{ - sel4test_end_printf_buffer(); - test_check(result == SUCCESS); - - if (config_set(CONFIG_PRINT_XML)) { - printf("\t\n"); - } - - if (config_set(CONFIG_HAVE_TIMER)) { - timer_reset(&env); - } -} - -void sel4test_end_suite(int num_tests, int num_tests_passed, int skipped_tests) -{ - if (config_set(CONFIG_PRINT_XML)) { - printf("\n"); - } else { - if (num_tests_passed != num_tests) { - printf("Test suite failed. %d/%d tests passed.\n", num_tests_passed, num_tests); - } else { - printf("Test suite passed. %d tests passed. %d tests disabled.\n", num_tests, skipped_tests); - } - } -} - -void sel4test_stop_tests(test_result_t result, int tests_done, int tests_failed, int num_tests, int skipped_tests) -{ - /* if its a special abort case, output why we are aborting */ - switch (result) { - case ABORT: - printf("Halting on fatal assertion...\n"); - break; - case FAILURE: - assert(config_set(CONFIG_TESTPRINTER_HALT_ON_TEST_FAILURE)); - printf("Halting on first test failure\n"); - break; - default: - /* nothing to output if its successful */ - break; - } - - /* last test - test all tests ran */ - sel4test_start_test("Test all tests ran", num_tests + 1); - /* Account for skipped tests: tests_done should equal num_tests (enabled tests) */ - printf("Debug: tests_done=%d, num_tests=%d, skipped_tests=%d\n", tests_done, num_tests, skipped_tests); - test_eq(tests_done, num_tests); - if (sel4test_get_result() != SUCCESS) { - tests_failed++; - } - tests_done++; - num_tests++; - sel4test_end_test(sel4test_get_result()); - - sel4test_end_suite(tests_done, tests_done - tests_failed, skipped_tests); - - if (tests_failed > 0) { - printf("*** FAILURES DETECTED ***\n"); - } else if (tests_done < num_tests) { - printf("*** ALL tests not run ***\n"); - } else { - printf("All is well in the universe\n"); - } - printf("\n"); -} - -static int collate_tests(testcase_t *tests_in, int n, testcase_t *tests_out[], int out_index, - regex_t *reg, int *skipped_tests) -{ - for (int i = 0; i < n; i++) { - /* make sure the string is null terminated */ - tests_in[i].name[TEST_NAME_MAX - 1] = '\0'; - if (regexec(reg, tests_in[i].name, 0, NULL, 0) == 0) { - if (tests_in[i].enabled) { - tests_out[out_index] = &tests_in[i]; - out_index++; - } else { - (*skipped_tests)++; - } - } - } - - return out_index; -} - -void sel4test_run_tests(struct driver_env *e) -{ - /* Iterate through test types. */ - int max_test_types = (int)(__stop__test_type - __start__test_type); - struct test_type *test_types[max_test_types]; - int num_test_types = 0; - for (struct test_type *i = __start__test_type; i < __stop__test_type; i++) { - test_types[num_test_types] = i; - num_test_types++; - } - - /* Ensure we iterate through test types in order of ID. */ - qsort(test_types, num_test_types, sizeof(struct test_type *), test_type_comparator); - - /* Count how many tests actually exist and allocate space for them */ - int driver_tests = (int)(__stop__test_case - __start__test_case); - uint64_t tc_size = 0; - testcase_t *sel4test_tests = (testcase_t *) sel4utils_elf_get_section(&tests_elf, "_test_case", &tc_size); - if (sel4test_tests == NULL) { - ZF_LOGF(TESTS_APP": Failed to find section: _test_case"); - } - int tc_tests = tc_size / sizeof(testcase_t); - int all_tests = driver_tests + tc_tests; - testcase_t *tests[all_tests]; - - /* Extract and filter the tests based on the regex */ - regex_t reg; - int error = regcomp(®, CONFIG_TESTPRINTER_REGEX, REG_EXTENDED | REG_NOSUB); - ZF_LOGF_IF(error, "Error compiling regex \"%s\"", CONFIG_TESTPRINTER_REGEX); - - int skipped_tests = 0; - /* get all the tests in the test case section in the driver */ - int num_tests = collate_tests(__start__test_case, driver_tests, tests, 0, ®, &skipped_tests); - /* get all the tests in the sel4test_tests app */ - num_tests = collate_tests(sel4test_tests, tc_tests, tests, num_tests, ®, &skipped_tests); - - /* finished with regex */ - regfree(®); - - /* Sort the tests to remove any non determinism in test ordering */ - qsort(tests, num_tests, sizeof(testcase_t *), test_comparator); - - /* Now that they are sorted we can easily ensure there are no duplicate tests. - * this just ensures some sanity as if there are duplicates, they could have some - * arbitrary ordering, which might result in difficulty reproducing test failures */ - for (int i = 1; i < num_tests; i++) { - ZF_LOGF_IF(strcmp(tests[i]->name, tests[i - 1]->name) == 0, "tests have no strict order! %s %s", - tests[i]->name, tests[i - 1]->name); - } - - /* Check that we don't miss any tests because of an undeclared test type */ - int tests_done = 0; - int tests_failed = 0; - - sel4test_start_suite("sel4test"); - /* First: test that there are tests to run */ - sel4test_start_test("Test that there are tests", tests_done); - test_gt(num_tests, 0); - sel4test_end_test(sel4test_get_result()); - tests_done++; - - /* Iterate through test types so that we run them in order of test type, then name. - * Test types are ordered by ID in test.h. */ - for (int tt = 0; tt < num_test_types; tt++) { - /* set up */ - if (test_types[tt]->set_up_test_type != NULL) { - test_types[tt]->set_up_test_type((uintptr_t)e); - } - - for (int i = 0; i < num_tests; i++) { - if (tests[i]->test_type == test_types[tt]->id) { - sel4test_start_test(tests[i]->name, tests_done); - if (test_types[tt]->set_up != NULL) { - test_types[tt]->set_up((uintptr_t)e); - } - - test_result_t result = test_types[tt]->run_test(tests[i], (uintptr_t)e); - - if (test_types[tt]->tear_down != NULL) { - test_types[tt]->tear_down((uintptr_t)e); - } - sel4test_end_test(result); - - if (result != SUCCESS) { - tests_failed++; - if (config_set(CONFIG_TESTPRINTER_HALT_ON_TEST_FAILURE) || result == ABORT) { - sel4test_stop_tests(result, tests_done + 1, tests_failed, num_tests + 1, skipped_tests); - return; - } - } - tests_done++; - } - } - - /* tear down */ - if (test_types[tt]->tear_down_test_type != NULL) { - test_types[tt]->tear_down_test_type((uintptr_t)e); - } - } - - /* and we're done */ - sel4test_stop_tests(SUCCESS, tests_done, tests_failed, num_tests + 1, skipped_tests); -} - -void *main_continued(void *arg UNUSED) -{ - - /* elf region data */ - int num_elf_regions; - sel4utils_elf_region_t elf_regions[MAX_REGIONS]; - - printf("=== main_continued started ===\n"); - printf("env.vspace pointer: %p\n", &env.vspace); - printf("env.vka pointer: %p\n", &env.vka); - printf("env.vspace content check:\n"); - printf(" env.vspace.data: %p\n", env.vspace.data); - printf(" env.vspace.new_pages: %p\n", env.vspace.new_pages); - printf(" env.vspace.reserve_range_at: %p\n", env.vspace.reserve_range_at); - printf("Validating vspace structure...\n"); - if (env.vspace.data == NULL) { - printf("ERROR: env.vspace.data is NULL!\n"); - } - if (env.vspace.new_pages == NULL) { - printf("ERROR: env.vspace.new_pages function pointer is NULL!\n"); - } - if (env.vspace.reserve_range_at == NULL) { - printf("ERROR: env.vspace.reserve_range_at function pointer is NULL!\n"); - } - - unsigned long elf_size; - unsigned long cpio_len = _cpio_archive_end - _cpio_archive; - const void *elf_file = cpio_get_file(_cpio_archive, cpio_len, TESTS_APP, &elf_size); - ZF_LOGF_IF(elf_file == NULL, "Error: failed to lookup ELF file"); - int status = elf_newFile(elf_file, elf_size, &tests_elf); - ZF_LOGF_IF(status, "Error: invalid ELF file"); - - /* Print welcome banner. */ - printf("\n"); - printf("seL4 Test\n"); - printf("=========\n"); - printf("\n"); - - int error; - - /* allocate a piece of device untyped memory for the frame tests, - * note that spike doesn't have any device untypes so the tests that require device untypes are turned off */ - if (!config_set(CONFIG_PLAT_SPIKE)) { - bool allocated = false; - int untyped_count = simple_get_untyped_count(&env.simple); - for (int i = 0; i < untyped_count; i++) { - bool device = false; - uintptr_t ut_paddr = 0; - size_t ut_size_bits = 0; - seL4_CPtr ut_cptr = simple_get_nth_untyped(&env.simple, i, &ut_size_bits, &ut_paddr, &device); - if (device) { - error = vka_alloc_frame_at(&env.vka, seL4_PageBits, ut_paddr, &env.device_obj); - if (!error) { - allocated = true; - /* we've allocated a single device frame and that's all we need */ - break; - } - } - } - ZF_LOGF_IF(allocated == false, "Failed to allocate a device frame for the frame tests"); - } - - /* allocate lots of untyped memory for tests to use */ - printf("About to populate untypeds...\n"); - env.num_untypeds = populate_untypeds(untypeds); - env.untypeds = untypeds; - printf("Untypeds populated: %u\n", env.num_untypeds); - - /* create a frame that will act as the init data, we can then map that - * in to target processes */ - printf("About to allocate init data frame, vspace at: %p\n", &env.vspace); - printf("Before vspace_new_pages call:\n"); - printf(" env.vspace.data: %p\n", env.vspace.data); - printf(" env.vspace.new_pages: %p\n", env.vspace.new_pages); - printf(" Calling vspace_new_pages(&env.vspace=%p, seL4_AllRights, 1, PAGE_BITS_4K)\n", &env.vspace); - - if (&env.vspace == NULL) { - printf("ERROR: env.vspace is NULL!\n"); - ZF_LOGF("env.vspace is NULL!"); - } - if (env.vspace.data == NULL) { - printf("ERROR: env.vspace.data is NULL before vspace_new_pages!\n"); - ZF_LOGF("env.vspace.data is NULL before vspace_new_pages!"); - } - - env.init = (test_init_data_t *) vspace_new_pages(&env.vspace, seL4_AllRights, 1, PAGE_BITS_4K); - assert(env.init != NULL); - printf("Init data frame allocated at: %p\n", env.init); - - /* copy the untyped size bits list across to the init frame */ - memcpy(env.init->untyped_size_bits_list, untyped_size_bits_list, sizeof(uint8_t) * env.num_untypeds); - - /* parse elf region data about the test image to pass to the tests app */ - // printf("About to parse ELF regions...\n"); - // num_elf_regions = sel4utils_elf_num_regions(&tests_elf); - // assert(num_elf_regions <= MAX_REGIONS); - // printf("TEST 0001\n"); - // sel4utils_elf_reserve(NULL, &tests_elf, elf_regions); - // printf("TEST 0002\n"); - - // /* copy the region list for the process to clone itself */ - // memcpy(env.init->elf_regions, elf_regions, sizeof(sel4utils_elf_region_t) * num_elf_regions); - // env.init->num_elf_regions = num_elf_regions; - // printf("env num_elf_regions: %d\n", num_elf_regions); - - /* setup init data that won't change test-to-test */ - env.init->priority = seL4_MaxPrio - 1; - if (plat_init) { - plat_init(&env); - } - - /* Allocate a reply object for the RT kernel. */ - if (config_set(CONFIG_KERNEL_MCS)) { - error = vka_alloc_reply(&env.vka, &env.reply); - ZF_LOGF_IF(error, "Failed to allocate reply"); - } - - /* now run the tests */ - sel4test_run_tests(&env); - - return NULL; -} - -/* Note that the following globals are place here because it is not expected that - * this function be refactored out of sel4test-driver in its current form. */ -/* Number of objects to track allocation of. Currently all serial devices are - * initialised with a single Frame object. Future devices may need more than 1. - */ -#define NUM_ALLOC_AT_TO_TRACK 1 -/* Static global to store the original vka_utspace_alloc_at function. It - * isn't expected for this to dynamically change after initialisation.*/ -static vka_utspace_alloc_at_fn vka_utspace_alloc_at_base; -/* State that serial_utspace_alloc_at_fn uses to determine whether to cache - * allocations. It is intended that this flag gets set before the serial device - * is initialised and then unset afterwards. */ -static bool serial_utspace_record = false; - -typedef struct uspace_alloc_at_args { - uintptr_t paddr; - seL4_Word type; - seL4_Word size_bits; - cspacepath_t dest; -} uspace_alloc_at_args_t; -/* This instance of vka_utspace_alloc_at_fn will keep a record of allocations up - * to NUM_ALLOC_AT_TO_TRACK while serial_utspace_record is set. When serial_utspace_record - * is unset, any allocations matching recorded allocations will instead copy the cap - * that was originally allocated. These subsequent allocations cannot be freed using - * vka_utspace_free and instead the caps would have to be manually deleted. - * Freeing these objects via vka_utspace_free would require also wrapping that function.*/ -static int serial_utspace_alloc_at_fn(void *data, const cspacepath_t *dest, seL4_Word type, seL4_Word size_bits, - uintptr_t paddr, seL4_Word *cookie) -{ - static uspace_alloc_at_args_t args_prev[NUM_ALLOC_AT_TO_TRACK] = {}; - static size_t num_alloc = 0; - - ZF_LOGF_IF(!vka_utspace_alloc_at_base, "vka_utspace_alloc_at_base not initialised."); - if (!serial_utspace_record) { - for (int i = 0; i < num_alloc; i++) { - if (paddr == args_prev[i].paddr && - type == args_prev[i].type && - size_bits == args_prev[i].size_bits) { - return vka_cnode_copy(dest, &args_prev[i].dest, seL4_AllRights); - } - } - return vka_utspace_alloc_at_base(data, dest, type, size_bits, paddr, cookie); - } else { - ZF_LOGF_IF(num_alloc >= NUM_ALLOC_AT_TO_TRACK, "Trying to allocate too many utspace objects"); - int ret = vka_utspace_alloc_at_base(data, dest, type, size_bits, paddr, cookie); - if (ret) { - return ret; - } - uspace_alloc_at_args_t a = {.paddr = paddr, .type = type, .size_bits = size_bits, .dest = *dest}; - args_prev[num_alloc] = a; - num_alloc++; - return ret; - - } -} - -static ps_irq_register_fn_t irq_register_fn_copy; - -static irq_id_t sel4test_timer_irq_register(UNUSED void *cookie, ps_irq_t irq, irq_callback_fn_t callback, - void *callback_data) -{ - static int num_timer_irqs = 0; - - int error; - - ZF_LOGF_IF(!callback, "Passed in a NULL callback"); - - ZF_LOGF_IF(num_timer_irqs >= MAX_TIMER_IRQS, "Trying to register too many timer IRQs"); - - /* Allocate the IRQ */ - error = sel4platsupport_copy_irq_cap(&env.vka, &env.simple, &irq, - &env.timer_irqs[num_timer_irqs].handler_path); - ZF_LOGF_IF(error, "Failed to allocate IRQ handler"); - - /* Allocate the root notifitcation if we haven't already done so */ - if (env.timer_notification.cptr == seL4_CapNull) { - error = vka_alloc_notification(&env.vka, &env.timer_notification); - ZF_LOGF_IF(error, "Failed to allocate notification object"); - } - - /* Mint a notification for the IRQ handler to pair with */ - error = vka_cspace_alloc_path(&env.vka, &env.badged_timer_notifications[num_timer_irqs]); - ZF_LOGF_IF(error, "Failed to allocate path for the badged notification"); - cspacepath_t root_notification_path = {0}; - vka_cspace_make_path(&env.vka, env.timer_notification.cptr, &root_notification_path); - error = vka_cnode_mint(&env.badged_timer_notifications[num_timer_irqs], &root_notification_path, - seL4_AllRights, BIT(num_timer_irqs)); - ZF_LOGF_IF(error, "Failed to mint notification for timer"); - - /* Pair the notification and the handler */ - error = seL4_IRQHandler_SetNotification(env.timer_irqs[num_timer_irqs].handler_path.capPtr, - env.badged_timer_notifications[num_timer_irqs].capPtr); - ZF_LOGF_IF(error, "Failed to pair the notification and handler together"); - - /* Ack the handler so interrupts can come in */ - error = seL4_IRQHandler_Ack(env.timer_irqs[num_timer_irqs].handler_path.capPtr); - ZF_LOGF_IF(error, "Failed to ack the IRQ handler"); - - /* Fill out information about the callbacks */ - env.timer_cbs[num_timer_irqs].callback = callback; - env.timer_cbs[num_timer_irqs].callback_data = callback_data; - - return num_timer_irqs++; -} - -/* When the root task exists, it should simply suspend itself */ -static void sel4test_exit(int code) -{ - seL4_TCB_Suspend(seL4_CapInitThreadTCB); -} void printRawString(char* str) { @@ -761,7 +46,6 @@ int main(void) int error; seL4_BootInfo *info = platsupport_get_bootinfo(); - ZF_LOGI("=== seL4 Test Driver Starting on Phytium Pi ==="); ZF_LOGI("BootInfo pointer: %p", info); if (info) { ZF_LOGI("Initial BootInfo - nodeID: %u, numNodes: %u, numIOPTLevels: %u", @@ -837,7 +121,7 @@ int main(void) /* switch to a bigger, safer stack with a guard page * before starting the tests */ - printf("Switching to a safer, bigger stack... "); + printf("Switching to a safer, bigger stack...\n"); fflush(stdout); void *res; @@ -849,6 +133,9 @@ int main(void) ZF_LOGI("=== seL4 Test Driver completed ==="); + printf("----------------MKUSERLIBS--------------------\n"); + test_malloc_free(&env); + printf("----------------ROOTSERVER--------------------\n"); seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); unsigned long long *vaddrs=(unsigned long long *)*Ptr2ShmemCommBuff; diff --git a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c new file mode 100644 index 0000000..843d1e8 --- /dev/null +++ b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c @@ -0,0 +1,201 @@ +/* Mixed allocator: small allocations served from local static slab pools + * (array-based), large allocations served by mapping pages using the vspace + * (via vspace_new_pages). This implementation keeps slab storage in static + * arrays (no dynamic memory) and records page-backed allocations so they + * can be unmapped on free. */ + +#include +#include +#include +#include + +#include "test.h" /* driver_env_t */ +#include + +#define MAX_SMALL_ALLOC 256 +#define SLOTS_PER_CLASS 64 + +/* slab classes */ +static const size_t slab_sizes[] = {16, 32, 64, 128, 256}; +#define NSLAB (sizeof(slab_sizes) / sizeof(slab_sizes[0])) + +/* static slab storage: NSLAB classes x SLOTS_PER_CLASS slots x MAX_SMALL_ALLOC bytes */ +static uint8_t slab_storage[NSLAB][SLOTS_PER_CLASS][MAX_SMALL_ALLOC]; +/* per-class free stacks (indices) */ +static int16_t slab_free_stack_top[NSLAB]; +static int16_t slab_free_stack[NSLAB][SLOTS_PER_CLASS]; + +/* page-backed allocation records */ +#define MAX_PAGE_ALLOCS 128 +typedef struct page_alloc_rec { + void *vaddr; + size_t num_pages; +} page_alloc_rec_t; +static page_alloc_rec_t page_allocs[MAX_PAGE_ALLOCS]; +static int page_allocs_count = 0; + +/* allocator env */ +static driver_env_t alloc_env = NULL; +static int alloc_initted = 0; + +/* Initialize allocator (must be called from test) */ +void xmalloc_init(driver_env_t env) +{ + if (alloc_initted) return; + /* allow slab-only mode when env == NULL (no page-backed allocations) */ + alloc_env = env; + + /* initialise slab free stacks */ + for (int c = 0; c < (int)NSLAB; c++) { + slab_free_stack_top[c] = 0; + for (int i = 0; i < SLOTS_PER_CLASS; i++) { + slab_free_stack[c][i] = SLOTS_PER_CLASS - 1 - i; /* fill with indices */ + } + } + + page_allocs_count = 0; + alloc_initted = 1; +} + +/* helper: pick slab class for size, or -1 if too large */ +static int slab_class_for_size(size_t size) +{ + for (int i = 0; i < (int)NSLAB; i++) { + if (size <= slab_sizes[i]) return i; + } + return -1; +} + +/* allocate pages via vspace_new_pages; record for free */ +static void *alloc_pages(size_t num_pages) +{ + /* page allocation requires a valid env; return NULL if not available */ + if (!alloc_initted || !alloc_env) return NULL; + if (num_pages == 0) return NULL; + void *vaddr = vspace_new_pages(&alloc_env->vspace, seL4_AllRights, num_pages, seL4_PageBits); + if (!vaddr) return NULL; + if (page_allocs_count < MAX_PAGE_ALLOCS) { + page_allocs[page_allocs_count].vaddr = vaddr; + page_allocs[page_allocs_count].num_pages = num_pages; + page_allocs_count++; + } + return vaddr; +} + +/* free page-backed region (unmap + let vspace free the frames) */ +static void free_pages(void *ptr) +{ + if (!alloc_initted || !alloc_env || ptr == NULL) return; + for (int i = 0; i < page_allocs_count; i++) { + if (page_allocs[i].vaddr == ptr) { + vspace_unmap_pages(&alloc_env->vspace, ptr, page_allocs[i].num_pages, seL4_PageBits, VSPACE_FREE); + /* compact array */ + page_allocs[i] = page_allocs[page_allocs_count - 1]; + page_allocs_count--; + return; + } + } +} + +/* xmalloc: slab for small sizes, pages for large sizes */ +void *xmalloc(size_t size) +{ + if (size == 0) return NULL; + if (!alloc_initted) return NULL; + + if (size <= MAX_SMALL_ALLOC) { + int cls = slab_class_for_size(size); + if (cls >= 0) { + /* have a slab class */ + if (slab_free_stack_top[cls] < SLOTS_PER_CLASS) { + int idx = slab_free_stack[cls][slab_free_stack_top[cls]++]; + return (void *)&slab_storage[cls][idx][0]; + } + /* no free slot in this class; fallthrough to page alloc */ + } + } + + /* page-based allocation */ + size_t page_size = (size_t)1 << seL4_PageBits; + size_t num_pages = (size + page_size - 1) / page_size; + void *v = alloc_pages(num_pages); + return v; +} + +/* xfree: return to slab pool if pointer is within slab arrays, otherwise free pages */ +void xfree(void *ptr) +{ + if (!ptr || !alloc_initted) return; + + /* check slab pools */ + for (int c = 0; c < (int)NSLAB; c++) { + uint8_t *base = (uint8_t *)&slab_storage[c][0][0]; + size_t class_block = SLOTS_PER_CLASS * MAX_SMALL_ALLOC; + if ((uint8_t *)ptr >= base && (uint8_t *)ptr < base + class_block) { + size_t offset = (uint8_t *)ptr - base; + int idx = offset / MAX_SMALL_ALLOC; + /* push back */ + if (slab_free_stack_top[c] > 0) { + slab_free_stack[c][--slab_free_stack_top[c]] = idx; + } else { + /* stack is full, shouldn't happen */ + } + return; + } + } + + /* otherwise must be page-backed allocation; free if we recorded it */ + free_pages(ptr); +} + + + +int test_malloc_free(driver_env_t env) +{ + /* initialise allocator heap backed by vspace pages */ + xmalloc_init(env); + + /* 1) Exercise slab allocations: allocate many small blocks until slabs + * are exhausted and some allocations fall back to page-backed. */ + const int NUM_SMALL = 70; /* should exceed per-class slab slots (default 64) */ + void *small_ptrs[NUM_SMALL]; + for (int i = 0; i < NUM_SMALL; i++) { + small_ptrs[i] = xmalloc(16); + test_assert(small_ptrs[i] != NULL); + memset(small_ptrs[i], 0xA5, 16); + for (int j = 0; j < 16; j++) { + test_assert(((unsigned char *)small_ptrs[i])[j] == 0xA5); + } + } + + /* free a couple of small slots and reallocate to test reuse */ + xfree(small_ptrs[10]); + xfree(small_ptrs[20]); + void *r1 = xmalloc(16); + test_assert(r1 != NULL); + void *r2 = xmalloc(16); + test_assert(r2 != NULL); + + /* free them */ + xfree(r1); + xfree(r2); + + /* 2) Allocate a large, page-backed allocation and test read/write */ + void *big = xmalloc(4096); + test_assert(big != NULL); + memset(big, 0x5A, 4096); + for (int i = 0; i < 4096; i++) { + test_assert(((unsigned char *)big)[i] == 0x5A); + } + xfree(big); + + /* 3) Clean up remaining small allocations */ + for (int i = 0; i < NUM_SMALL; i++) { + if (small_ptrs[i]) { + xfree(small_ptrs[i]); + } + } + + printf("malloc/free test completed successfully\n"); +} + -- Gitee From aca1aa334026089bf8b1d65effc4be963a950047 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Fri, 12 Dec 2025 00:51:30 +0800 Subject: [PATCH 016/133] integrate thread and memory test --- .../apps/sel4test-driver/CMakeLists.txt | 2 +- .../sel4test/apps/sel4test-driver/src/init.h | 1 + .../sel4test/apps/sel4test-driver/src/main.c | 10 +- .../apps/sel4test-driver/src/malloc_stub.c | 15 ++- .../apps/sel4test-driver/src/wpool_stub.c | 105 ++++++++++++++++++ 5 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 projects/sel4test/apps/sel4test-driver/src/wpool_stub.c diff --git a/projects/sel4test/apps/sel4test-driver/CMakeLists.txt b/projects/sel4test/apps/sel4test-driver/CMakeLists.txt index 21a7742..fe4553b 100755 --- a/projects/sel4test/apps/sel4test-driver/CMakeLists.txt +++ b/projects/sel4test/apps/sel4test-driver/CMakeLists.txt @@ -82,7 +82,7 @@ add_subdirectory(../sel4test-tests sel4test-tests) include(cpio) MakeCPIO(archive.o "$") -add_executable(sel4test-driver EXCLUDE_FROM_ALL ${static} src/malloc_stub.c archive.o) +add_executable(sel4test-driver EXCLUDE_FROM_ALL ${static} src/malloc_stub.c src/wpool_stub.c archive.o) target_include_directories(sel4test-driver PRIVATE "include") target_link_libraries( sel4test-driver diff --git a/projects/sel4test/apps/sel4test-driver/src/init.h b/projects/sel4test/apps/sel4test-driver/src/init.h index b9addb3..7f79d5c 100644 --- a/projects/sel4test/apps/sel4test-driver/src/init.h +++ b/projects/sel4test/apps/sel4test-driver/src/init.h @@ -743,6 +743,7 @@ void minikv_destroy(void); // Testsuite int test_malloc_free(driver_env_t env); int test_minikv(driver_env_t env); +int test_wpool(driver_env_t env); void sel4test_driver_init(void) { printf("=== sel4test_driver_init started ===\n"); diff --git a/projects/sel4test/apps/sel4test-driver/src/main.c b/projects/sel4test/apps/sel4test-driver/src/main.c index c9133a0..92bd9b6 100755 --- a/projects/sel4test/apps/sel4test-driver/src/main.c +++ b/projects/sel4test/apps/sel4test-driver/src/main.c @@ -133,9 +133,6 @@ int main(void) ZF_LOGI("=== seL4 Test Driver completed ==="); - printf("----------------MKUSERLIBS--------------------\n"); - test_malloc_free(&env); - printf("----------------ROOTSERVER--------------------\n"); seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); unsigned long long *vaddrs=(unsigned long long *)*Ptr2ShmemCommBuff; @@ -168,5 +165,12 @@ int main(void) // === 边界测试结束 === printRawString((char*)(vaddrs[1])); + + printf("----------------MKUSERLIBS--------------------\n"); + test_malloc_free(&env); + /* Test worker pool */ + test_wpool(&env); + + return 0; } diff --git a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c index 843d1e8..88c7bb3 100644 --- a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c +++ b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c @@ -154,9 +154,11 @@ int test_malloc_free(driver_env_t env) { /* initialise allocator heap backed by vspace pages */ xmalloc_init(env); + printf("Initializing allocator\n"); /* 1) Exercise slab allocations: allocate many small blocks until slabs * are exhausted and some allocations fall back to page-backed. */ + printf("Starting slab allocation test\n"); const int NUM_SMALL = 70; /* should exceed per-class slab slots (default 64) */ void *small_ptrs[NUM_SMALL]; for (int i = 0; i < NUM_SMALL; i++) { @@ -167,35 +169,46 @@ int test_malloc_free(driver_env_t env) test_assert(((unsigned char *)small_ptrs[i])[j] == 0xA5); } } + printf("Slab allocation test done\n"); /* free a couple of small slots and reallocate to test reuse */ + printf("Testing reuse\n"); xfree(small_ptrs[10]); xfree(small_ptrs[20]); + printf("Freed small block 20\n"); void *r1 = xmalloc(16); test_assert(r1 != NULL); void *r2 = xmalloc(16); test_assert(r2 != NULL); + printf("Reallocated block 2 at %p\n", r2); /* free them */ xfree(r1); xfree(r2); + printf("Reuse test done\n"); /* 2) Allocate a large, page-backed allocation and test read/write */ + printf("Testing large allocation\n"); void *big = xmalloc(4096); test_assert(big != NULL); + printf("Allocated large block at %p\n", big); memset(big, 0x5A, 4096); for (int i = 0; i < 4096; i++) { test_assert(((unsigned char *)big)[i] == 0x5A); } xfree(big); + printf("Freed large block\n"); + printf("Large allocation test done\n"); /* 3) Clean up remaining small allocations */ + printf("Cleaning up remaining allocations\n"); for (int i = 0; i < NUM_SMALL; i++) { if (small_ptrs[i]) { xfree(small_ptrs[i]); } } + printf("Cleanup done\n"); - printf("malloc/free test completed successfully\n"); + printf("[Lib: xmalloc] malloc/free test completed successfully\n"); } diff --git a/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c b/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c new file mode 100644 index 0000000..b974e6b --- /dev/null +++ b/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c @@ -0,0 +1,105 @@ +/* Simple worker pool using sel4utils threads. Spawns N workers that + * receive an integer job on a shared endpoint and reply with +1. + */ + +#include +#include +#include +#include + +#include "test.h" /* driver_env_t */ +#include +#include +#include + +/* Worker thread entry: arg0=endpoint, arg1=reply (MCS only) */ +static int worker_entry(seL4_Word ep_word, seL4_Word reply_word, seL4_Word a2, seL4_Word a3) +{ + seL4_CPtr ep = (seL4_CPtr)ep_word; + seL4_CPtr reply = (seL4_CPtr)reply_word; + +#ifdef CONFIG_KERNEL_MCS + while (1) { + seL4_Word badge = 0; + seL4_MessageInfo_t tag = api_recv(ep, &badge, reply); + int len = (int)seL4_MessageInfo_get_length(tag); + if (len == 0) { + /* graceful exit: park thread to avoid returning */ + for (;;) { seL4_Yield(); } + } + seL4_Word job = seL4_GetMR(0); + seL4_SetMR(0, job + 1); + printf("Worker processed job %d, result %d\n", (int)job, (int)(job + 1)); + api_reply(reply, seL4_MessageInfo_new(0, 0, 0, 1)); + } +#else + while (1) { + seL4_Word badge = 0; + seL4_MessageInfo_t tag = seL4_Recv(ep, &badge); + int len = (int)seL4_MessageInfo_get_length(tag); + if (len == 0) { + for (;;) { seL4_Yield(); } + } + seL4_Word job = seL4_GetMR(0); + seL4_SetMR(0, job + 1); + printf("Worker processed job %d, result %d\n", (int)job, (int)(job + 1)); + seL4_Reply(seL4_MessageInfo_new(0, 0, 0, 1)); + } +#endif + return 0; +} + +int test_wpool(driver_env_t env) +{ + const int N_WORKERS = 2; + const int N_JOBS = 4; + + /* allocate shared endpoint */ + seL4_CPtr ep = vka_alloc_endpoint_leaky(&env->vka); + test_assert(ep != 0); + + /* create workers using sel4utils */ + sel4utils_thread_t threads[N_WORKERS]; + seL4_CPtr replies[N_WORKERS]; + for (int i = 0; i < N_WORKERS; i++) { + seL4_CPtr cnode = simple_get_cnode(&env->simple); + seL4_Word data = api_make_guard_skip_word(seL4_WordBits - simple_get_cnode_size_bits(&env->simple)); + sel4utils_thread_config_t cfg = thread_config_default(&env->simple, cnode, data, 0, seL4_MaxPrio - 1); + int err = sel4utils_configure_thread_config(&env->vka, &env->vspace, &env->vspace, cfg, &threads[i]); + test_assert(err == 0); +#ifdef CONFIG_KERNEL_MCS + replies[i] = vka_alloc_reply_leaky(&env->vka); + test_assert(replies[i] != 0); +#else + replies[i] = 0; +#endif + err = sel4utils_start_thread(&threads[i], (sel4utils_thread_entry_fn)worker_entry, + (void *)(uintptr_t)ep, (void *)(uintptr_t)replies[i], 1); + test_assert(err == 0); + } + + /* dispatch jobs */ + for (int i = 0; i < N_JOBS; i++) { + printf("Dispatching job %d\n", i); + seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 1); + seL4_SetMR(0, i); + tag = seL4_Call(ep, tag); + seL4_Word ret = seL4_GetMR(0); + printf("Received result %d\n", (int)ret); + test_assert(ret == (seL4_Word)(i + 1)); + } + + /* shutdown */ + for (int i = 0; i < N_WORKERS; i++) { + seL4_Send(ep, seL4_MessageInfo_new(0, 0, 0, 0)); + } + + /* join and destroy threads */ + /* cleanup threads */ + for (int i = 0; i < N_WORKERS; i++) { + sel4utils_clean_up_thread(&env->vka, &env->vspace, &threads[i]); + } + + printf("[Lib: wpool] worker pool test completed successfully\n"); + return sel4test_get_result(); +} -- Gitee From 377beedba825ed6e3bc5fdb13202360298d6d9cc Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Tue, 23 Dec 2025 22:14:03 +0800 Subject: [PATCH 017/133] add uart getchar for modularized test --- .../sel4test/apps/sel4test-driver/src/main.c | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/projects/sel4test/apps/sel4test-driver/src/main.c b/projects/sel4test/apps/sel4test-driver/src/main.c index 92bd9b6..f93d375 100755 --- a/projects/sel4test/apps/sel4test-driver/src/main.c +++ b/projects/sel4test/apps/sel4test-driver/src/main.c @@ -14,8 +14,10 @@ #include #include #include +#include #include +#include #include "init.h" @@ -38,6 +40,45 @@ void printRawString(char* str) putchar('\n'); } +static ps_chardevice_t uart_console; +static bool uart_console_ready = false; + +static ps_chardevice_t *get_uart_console(void) +{ + if (!uart_console_ready) { + ps_chardevice_t *dev = ps_cdev_init(PS_SERIAL_DEFAULT, &env.ops, &uart_console); + if (dev == NULL) { + ZF_LOGE("Failed to initialise UART console device for input"); + return NULL; + } + uart_console_ready = true; + } + return &uart_console; +} + +static void wait_for_uart_keypress(void) +{ + ps_chardevice_t *dev = get_uart_console(); + if (dev == NULL) { + printf("UART input unavailable; continuing without wait.\n"); + return; + } + + printf("Press any key on the serial console to continue...\n"); + fflush(stdout); + + int ch = EOF; + while ((ch = ps_cdev_getchar(dev)) == EOF) { + /* busy-wait until input is available */ + } + + if (ch >= 32 && ch < 127) { + printf("Received '%c' (0x%x)\n", ch, ch); + } else { + printf("Received byte 0x%x\n", ch & 0xff); + } +} + int main(void) { /* Set exit handler */ @@ -165,9 +206,13 @@ int main(void) // === 边界测试结束 === printRawString((char*)(vaddrs[1])); + wait_for_uart_keypress(); + printf("----------------MKUSERLIBS--------------------\n"); test_malloc_free(&env); + + wait_for_uart_keypress(); /* Test worker pool */ test_wpool(&env); -- Gitee From 32f061ee17f53191ebd832ac2458b75c3335a060 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Wed, 24 Dec 2025 22:05:47 +0800 Subject: [PATCH 018/133] change config to enable pmu counter, add timing for base and huge pages --- README.md | 9 ++ .../apps/sel4test-driver/CMakeLists.txt | 1 + .../apps/sel4test-driver/src/malloc_stub.c | 119 +++++++++++++++++- 3 files changed, 123 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d5ebdca..8eac63c 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,12 @@ cd build ninja ./simulate ``` + +The init config should be changed to this: +``` +../init-build.sh -DPLATFORM=qemu-arm-virt -DSIMULATION=TRUE -DKernelArmExportPMUUser=TRUE +``` +to make sure that: + - it run in sel4test-driver, + - thread init success, + - and cycle counters available diff --git a/projects/sel4test/apps/sel4test-driver/CMakeLists.txt b/projects/sel4test/apps/sel4test-driver/CMakeLists.txt index fe4553b..6e73878 100755 --- a/projects/sel4test/apps/sel4test-driver/CMakeLists.txt +++ b/projects/sel4test/apps/sel4test-driver/CMakeLists.txt @@ -99,6 +99,7 @@ target_link_libraries( sel4platsupport sel4muslcsys sel4testsupport + sel4bench PRIVATE sel4test-driver_Config ) target_compile_options(sel4test-driver PRIVATE -Werror -g) diff --git a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c index 88c7bb3..6af202a 100644 --- a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c +++ b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c @@ -6,11 +6,14 @@ #include #include +#include #include #include #include "test.h" /* driver_env_t */ #include +#include + #define MAX_SMALL_ALLOC 256 #define SLOTS_PER_CLASS 64 @@ -30,6 +33,7 @@ static int16_t slab_free_stack[NSLAB][SLOTS_PER_CLASS]; typedef struct page_alloc_rec { void *vaddr; size_t num_pages; + size_t page_bits; } page_alloc_rec_t; static page_alloc_rec_t page_allocs[MAX_PAGE_ALLOCS]; static int page_allocs_count = 0; @@ -67,16 +71,18 @@ static int slab_class_for_size(size_t size) } /* allocate pages via vspace_new_pages; record for free */ -static void *alloc_pages(size_t num_pages) +static void *alloc_pages(size_t num_pages, int hugepage) { /* page allocation requires a valid env; return NULL if not available */ if (!alloc_initted || !alloc_env) return NULL; if (num_pages == 0) return NULL; - void *vaddr = vspace_new_pages(&alloc_env->vspace, seL4_AllRights, num_pages, seL4_PageBits); + size_t page_bits = hugepage ? seL4_LargePageBits : seL4_PageBits; + void *vaddr = vspace_new_pages(&alloc_env->vspace, seL4_AllRights, num_pages, page_bits); if (!vaddr) return NULL; if (page_allocs_count < MAX_PAGE_ALLOCS) { page_allocs[page_allocs_count].vaddr = vaddr; page_allocs[page_allocs_count].num_pages = num_pages; + page_allocs[page_allocs_count].page_bits = page_bits; page_allocs_count++; } return vaddr; @@ -88,7 +94,7 @@ static void free_pages(void *ptr) if (!alloc_initted || !alloc_env || ptr == NULL) return; for (int i = 0; i < page_allocs_count; i++) { if (page_allocs[i].vaddr == ptr) { - vspace_unmap_pages(&alloc_env->vspace, ptr, page_allocs[i].num_pages, seL4_PageBits, VSPACE_FREE); + vspace_unmap_pages(&alloc_env->vspace, ptr, page_allocs[i].num_pages, page_allocs[i].page_bits, VSPACE_FREE); /* compact array */ page_allocs[i] = page_allocs[page_allocs_count - 1]; page_allocs_count--; @@ -116,9 +122,10 @@ void *xmalloc(size_t size) } /* page-based allocation */ - size_t page_size = (size_t)1 << seL4_PageBits; + int use_huge_pages = size >= (1 << seL4_LargePageBits); + size_t page_size = use_huge_pages ? (1 << seL4_LargePageBits) : (1 << seL4_PageBits); size_t num_pages = (size + page_size - 1) / page_size; - void *v = alloc_pages(num_pages); + void *v = alloc_pages(num_pages, use_huge_pages); return v; } @@ -148,7 +155,101 @@ void xfree(void *ptr) free_pages(ptr); } +/* Is a usable time source available? */ +static inline int time_source_available(void) +{ +#ifdef CONFIG_EXPORT_PMU_USER + return 1; /* PMU is user-accessible */ +#else + return alloc_env && alloc_env->ltimer.get_time != NULL; +#endif +} + +/* Timestamp helper: prefer PMU cycles when exported to user, otherwise ltimer */ +static inline uint64_t get_cycles(void) +{ +#ifdef CONFIG_EXPORT_PMU_USER + static int bench_started = 0; + if (!bench_started) { + sel4bench_init(); + bench_started = 1; + } + return (uint64_t)sel4bench_get_cycle_count(); +#else + uint64_t t = 0; + if (alloc_env && alloc_env->ltimer.get_time && alloc_env->ltimer.get_time(alloc_env->ltimer.data, &t) == 0) { + return t; /* typically nanoseconds */ + } + /* Should not be reached if time_source_available() was checked */ + static uint64_t fallback = 0; + printf("Warning: get_cycles fallback used\n"); + return ++fallback; +#endif +} + +int test_allocation_performance(driver_env_t env) +{ + printf("Starting allocation performance test\n"); + if (!time_source_available()) { + printf("Timing source unavailable (no PMU export, no ltimer); skipping perf timings\n"); + return 0; + } + + const size_t total_alloc_size = 32 * 1024 * 1024; /* 32MB */ + const int num_small_pages = total_alloc_size / (4 * 1024); + const int num_huge_pages = total_alloc_size / (2 * 1024 * 1024); + const int batch_size = 128; /* Process in batches to avoid resource exhaustion */ + void *ptrs[batch_size]; + + /* Time 4KB page allocations */ + printf("Getting cycles count before starting...\n"); + uint64_t start_4k = get_cycles(); + for (int i = 0; i < num_small_pages; i += batch_size) { + int current_batch_size = (i + batch_size <= num_small_pages) ? batch_size : num_small_pages - i; + for (int j = 0; j < current_batch_size; j++) { + ptrs[j] = alloc_pages(1, 0); /* 1 page, not hugepage */ + test_assert(ptrs[j] != NULL); + } + for (int j = 0; j < current_batch_size; j++) { + free_pages(ptrs[j]); + } + } + uint64_t end_4k = get_cycles(); + printf("4KB page allocation time: %" PRIu64 " cycles\n", end_4k - start_4k); + + /* Time 2MB page allocations */ + uint64_t start_2m = get_cycles(); + for (int i = 0; i < num_huge_pages; i++) { + ptrs[i] = alloc_pages(1, 1); /* 1 page, hugepage */ + test_assert(ptrs[i] != NULL); + } + uint64_t end_2m = get_cycles(); + for (int i = 0; i < num_huge_pages; i++) { + free_pages(ptrs[i]); + } + printf("2MB page allocation time: %" PRIu64 " cycles\n", end_2m - start_2m); + + printf("Allocation performance test done\n"); + return 0; +} + +int test_huge_page_allocation(driver_env_t env) +{ + printf("Testing huge page allocation\n"); + size_t huge_page_size = 1 << seL4_LargePageBits; + void *huge = xmalloc(huge_page_size); + test_assert(huge != NULL); + printf("Allocated huge block at %p\n", huge); + memset(huge, 0xAB, huge_page_size); + for (int i = 0; i < huge_page_size; i++) { + test_assert(((unsigned char *)huge)[i] == 0xAB); + } + xfree(huge); + printf("Freed huge block\n"); + printf("Huge page allocation test done\n"); + return 0; +} int test_malloc_free(driver_env_t env) { @@ -200,7 +301,13 @@ int test_malloc_free(driver_env_t env) printf("Freed large block\n"); printf("Large allocation test done\n"); - /* 3) Clean up remaining small allocations */ + /* 3) Test huge page allocation */ + test_huge_page_allocation(env); + + /* 4) Test allocation performance */ + test_allocation_performance(env); + + /* 5) Clean up remaining small allocations */ printf("Cleaning up remaining allocations\n"); for (int i = 0; i < NUM_SMALL; i++) { if (small_ptrs[i]) { -- Gitee From 6876ff8dbf5ac993f6e87bb91e21720cef6431ce Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Thu, 25 Dec 2025 16:54:53 +0800 Subject: [PATCH 019/133] add swap function demo and refine logs --- .../apps/sel4test-driver/CMakeLists.txt | 2 +- .../sel4test/apps/sel4test-driver/src/immfs.c | 498 ++++++++++++++++++ .../sel4test/apps/sel4test-driver/src/init.h | 11 + .../sel4test/apps/sel4test-driver/src/main.c | 10 +- .../apps/sel4test-driver/src/malloc_stub.c | 53 +- .../sel4test/apps/sel4test-driver/src/test.h | 5 + .../apps/sel4test-driver/src/wpool_stub.c | 11 +- .../sel4test/apps/sel4test-tests/src/main.c | 4 +- 8 files changed, 559 insertions(+), 35 deletions(-) create mode 100644 projects/sel4test/apps/sel4test-driver/src/immfs.c diff --git a/projects/sel4test/apps/sel4test-driver/CMakeLists.txt b/projects/sel4test/apps/sel4test-driver/CMakeLists.txt index 6e73878..1523d09 100755 --- a/projects/sel4test/apps/sel4test-driver/CMakeLists.txt +++ b/projects/sel4test/apps/sel4test-driver/CMakeLists.txt @@ -82,7 +82,7 @@ add_subdirectory(../sel4test-tests sel4test-tests) include(cpio) MakeCPIO(archive.o "$") -add_executable(sel4test-driver EXCLUDE_FROM_ALL ${static} src/malloc_stub.c src/wpool_stub.c archive.o) +add_executable(sel4test-driver EXCLUDE_FROM_ALL ${static} src/malloc_stub.c src/wpool_stub.c src/immfs.c archive.o) target_include_directories(sel4test-driver PRIVATE "include") target_link_libraries( sel4test-driver diff --git a/projects/sel4test/apps/sel4test-driver/src/immfs.c b/projects/sel4test/apps/sel4test-driver/src/immfs.c new file mode 100644 index 0000000..de74eda --- /dev/null +++ b/projects/sel4test/apps/sel4test-driver/src/immfs.c @@ -0,0 +1,498 @@ +/* immfs: minimal in-memory filesystem with basic path operations. */ + +#include +#include +#include +#include +#include + +#include "test.h" + +/* xmalloc symbols are provided by malloc_stub.c; forward declare to avoid pulling init.h */ +void xmalloc_init(driver_env_t env); +void *xmalloc(size_t size); +void xfree(void *ptr); + +#define IMMFS_NAME_MAX 32 +#define IMMFS_MAX_COMPONENTS 32 + +typedef enum { + IMMFS_NODE_DIR, + IMMFS_NODE_FILE, +} immfs_node_type_t; + +typedef struct immfs_node { + char name[IMMFS_NAME_MAX]; + immfs_node_type_t type; + struct immfs_node *parent; + struct immfs_node *sibling; + struct immfs_node *child; + uint8_t *data; + size_t size; + size_t capacity; +} immfs_node_t; + +static int ensure_capacity(immfs_node_t *file, size_t needed); + +static immfs_node_t root = { + .name = "/", + .type = IMMFS_NODE_DIR, + .parent = NULL, + .sibling = NULL, + .child = NULL, + .data = NULL, + .size = 0, + .capacity = 0, +}; +static int fs_ready; + +static immfs_node_t *alloc_node(const char *name, immfs_node_type_t type) +{ + immfs_node_t *n = (immfs_node_t *)xmalloc(sizeof(*n)); + if (!n) { + return NULL; + } + memset(n, 0, sizeof(*n)); + n->type = type; + strncpy(n->name, name, IMMFS_NAME_MAX - 1); + n->name[IMMFS_NAME_MAX - 1] = '\0'; + return n; +} + +static void free_subtree(immfs_node_t *node) +{ + while (node) { + immfs_node_t *next = node->sibling; + if (node->child) { + free_subtree(node->child); + } + if (node->data) { + xfree(node->data); + } + xfree(node); + node = next; + } +} + +static void reset_root(void) +{ + if (root.child) { + free_subtree(root.child); + } + root.child = NULL; + root.size = 0; + root.capacity = 0; + root.data = NULL; +} + +static immfs_node_t *find_child(immfs_node_t *dir, const char *name) +{ + for (immfs_node_t *c = dir->child; c; c = c->sibling) { + if (strncmp(c->name, name, IMMFS_NAME_MAX) == 0) { + return c; + } + } + return NULL; +} + +static int split_path(const char *path, char parts[][IMMFS_NAME_MAX]) +{ + if (!path) { + return -EINVAL; + } + int count = 0; + const char *p = path; + while (*p == '/') { + p++; + } + while (*p) { + if (count >= IMMFS_MAX_COMPONENTS) { + return -ENAMETOOLONG; + } + const char *start = p; + while (*p && *p != '/') { + p++; + } + size_t len = (size_t)(p - start); + if (len == 0 || len >= IMMFS_NAME_MAX) { + return -ENAMETOOLONG; + } + memcpy(parts[count], start, len); + parts[count][len] = '\0'; + count++; + while (*p == '/') { + p++; + } + } + return count; +} + +static immfs_node_t *walk_dirs(char parts[][IMMFS_NAME_MAX], int count, int upto, int create_missing) +{ + immfs_node_t *node = &root; + for (int i = 0; i < upto; i++) { + immfs_node_t *child = find_child(node, parts[i]); + if (!child) { + if (!create_missing) { + return NULL; + } + child = alloc_node(parts[i], IMMFS_NODE_DIR); + if (!child) { + return NULL; + } + child->parent = node; + child->sibling = node->child; + node->child = child; + } + if (child->type != IMMFS_NODE_DIR) { + return NULL; + } + node = child; + } + return node; +} + +/* Move a page buffer (allocated via xmalloc) into a new file path and free the buffer. */ +int immfs_swap(void *page_buf, const char *dst_path) +{ + if (!fs_ready) { + return -ENODEV; + } + if (!page_buf) { + return -EINVAL; + } + char dparts[IMMFS_MAX_COMPONENTS][IMMFS_NAME_MAX]; + int dcount = split_path(dst_path, dparts); + if (dcount <= 0) { + return -EINVAL; + } + + immfs_node_t *dparent = walk_dirs(dparts, dcount, dcount - 1, 0); + if (!dparent) { + return -ENOENT; + } + immfs_node_t *existing = find_child(dparent, dparts[dcount - 1]); + if (existing) { + return existing->type == IMMFS_NODE_DIR ? -EISDIR : -EEXIST; + } + + immfs_node_t *dst = alloc_node(dparts[dcount - 1], IMMFS_NODE_FILE); + if (!dst) { + return -ENOMEM; + } + dst->parent = dparent; + dst->sibling = dparent->child; + dparent->child = dst; + + /* attach provided page buffer */ + size_t page_size = 1u << seL4_PageBits; + dst->data = (uint8_t *)page_buf; + dst->capacity = page_size; + dst->size = page_size; + + /* page_buf is now owned by the file; nothing else to free */ + return 0; +} + +int immfs_init(driver_env_t env) +{ + xmalloc_init(env); + reset_root(); + fs_ready = 1; + return 0; +} + +int immfs_mkdir(const char *path) +{ + if (!fs_ready) { + return -ENODEV; + } + char parts[IMMFS_MAX_COMPONENTS][IMMFS_NAME_MAX]; + int count = split_path(path, parts); + if (count < 0) { + return count; + } + if (count == 0) { + return 0; + } + immfs_node_t *parent = walk_dirs(parts, count, count - 1, 1); + if (!parent) { + return -ENOENT; + } + immfs_node_t *existing = find_child(parent, parts[count - 1]); + if (existing) { + return existing->type == IMMFS_NODE_DIR ? 0 : -EEXIST; + } + immfs_node_t *dir = alloc_node(parts[count - 1], IMMFS_NODE_DIR); + if (!dir) { + return -ENOMEM; + } + dir->parent = parent; + dir->sibling = parent->child; + parent->child = dir; + return 0; +} + +int immfs_create(const char *path) +{ + if (!fs_ready) { + return -ENODEV; + } + char parts[IMMFS_MAX_COMPONENTS][IMMFS_NAME_MAX]; + int count = split_path(path, parts); + if (count <= 0) { + return count < 0 ? count : -EINVAL; + } + immfs_node_t *parent = walk_dirs(parts, count, count - 1, 0); + if (!parent) { + return -ENOENT; + } + immfs_node_t *existing = find_child(parent, parts[count - 1]); + if (existing) { + return existing->type == IMMFS_NODE_FILE ? -EEXIST : -EISDIR; + } + immfs_node_t *file = alloc_node(parts[count - 1], IMMFS_NODE_FILE); + if (!file) { + return -ENOMEM; + } + file->parent = parent; + file->sibling = parent->child; + parent->child = file; + return 0; +} + +static int ensure_capacity(immfs_node_t *file, size_t needed) +{ + if (file->capacity >= needed) { + return 0; + } + size_t newcap = file->capacity ? file->capacity : 128; + while (newcap < needed) { + if (newcap > (SIZE_MAX / 2)) { + newcap = needed; + break; + } + newcap *= 2; + } + uint8_t *buf = (uint8_t *)xmalloc(newcap); + if (!buf) { + return -ENOMEM; + } + if (file->data && file->size) { + memcpy(buf, file->data, file->size); + } + if (file->data) { + xfree(file->data); + } + file->data = buf; + file->capacity = newcap; + return 0; +} + +ssize_t immfs_write(const char *path, off_t offset, const void *buf, size_t len) +{ + if (!fs_ready) { + return -ENODEV; + } + if (offset < 0) { + return -EINVAL; + } + if (!buf && len > 0) { + return -EINVAL; + } + char parts[IMMFS_MAX_COMPONENTS][IMMFS_NAME_MAX]; + int count = split_path(path, parts); + if (count <= 0) { + return count < 0 ? count : -EINVAL; + } + immfs_node_t *parent = walk_dirs(parts, count, count - 1, 0); + if (!parent) { + return -ENOENT; + } + immfs_node_t *file = find_child(parent, parts[count - 1]); + if (!file) { + return -ENOENT; + } + if (file->type != IMMFS_NODE_FILE) { + return -EISDIR; + } + size_t end = (size_t)offset + len; + if ((off_t)end < offset) { + return -EOVERFLOW; + } + int err = ensure_capacity(file, end); + if (err) { + return err; + } + if ((size_t)offset > file->size) { + memset(file->data + file->size, 0, (size_t)offset - file->size); + } + if (len > 0) { + memcpy(file->data + offset, buf, len); + } + if (end > file->size) { + file->size = end; + } + return (ssize_t)len; +} + +ssize_t immfs_read(const char *path, off_t offset, void *buf, size_t len) +{ + if (!fs_ready) { + return -ENODEV; + } + if (offset < 0) { + return -EINVAL; + } + if (!buf && len > 0) { + return -EINVAL; + } + char parts[IMMFS_MAX_COMPONENTS][IMMFS_NAME_MAX]; + int count = split_path(path, parts); + if (count <= 0) { + return count < 0 ? count : -EINVAL; + } + immfs_node_t *parent = walk_dirs(parts, count, count - 1, 0); + if (!parent) { + return -ENOENT; + } + immfs_node_t *file = find_child(parent, parts[count - 1]); + if (!file) { + return -ENOENT; + } + if (file->type != IMMFS_NODE_FILE) { + return -EISDIR; + } + if ((size_t)offset >= file->size) { + return 0; + } + size_t available = file->size - (size_t)offset; + size_t to_copy = available < len ? available : len; + if (to_copy > 0) { + memcpy(buf, file->data + offset, to_copy); + } + return (ssize_t)to_copy; +} + +int immfs_unlink(const char *path) +{ + if (!fs_ready) { + return -ENODEV; + } + char parts[IMMFS_MAX_COMPONENTS][IMMFS_NAME_MAX]; + int count = split_path(path, parts); + if (count <= 0) { + return count < 0 ? count : -EINVAL; + } + immfs_node_t *parent = walk_dirs(parts, count, count - 1, 0); + if (!parent) { + return -ENOENT; + } + immfs_node_t *prev = NULL; + immfs_node_t *cur = parent->child; + while (cur && strncmp(cur->name, parts[count - 1], IMMFS_NAME_MAX) != 0) { + prev = cur; + cur = cur->sibling; + } + if (!cur) { + return -ENOENT; + } + if (cur->type == IMMFS_NODE_DIR && cur->child) { + return -ENOTEMPTY; + } + if (prev) { + prev->sibling = cur->sibling; + } else { + parent->child = cur->sibling; + } + if (cur->child) { + free_subtree(cur->child); + } + if (cur->data) { + xfree(cur->data); + } + xfree(cur); + return 0; +} + +int test_immfs(driver_env_t env) +{ + printf("[immfs] starting test suite\n"); + int err = immfs_init(env); + test_assert(err == 0); + + printf("[immfs] create dirs /etc and /var/log\n"); + + err = immfs_mkdir("/etc"); + test_assert(err == 0); + err = immfs_mkdir("/var/log"); + test_assert(err == 0); + + printf("[immfs] create /etc/config and write payload\n"); + err = immfs_create("/etc/config"); + test_assert(err == 0); + + const char *payload = "root=config"; + ssize_t written = immfs_write("/etc/config", 0, payload, strlen(payload)); + test_assert(written == (ssize_t)strlen(payload)); + + char buf[64] = {0}; + ssize_t read = immfs_read("/etc/config", 0, buf, sizeof(buf) - 1); + test_assert(read == written); + test_assert(strncmp(buf, payload, (size_t)written) == 0); + + printf("[immfs] append suffix to /etc/config\n"); + const char *suffix = ";mode=debug"; + written = immfs_write("/etc/config", read, suffix, strlen(suffix)); + test_assert(written == (ssize_t)strlen(suffix)); + memset(buf, 0, sizeof(buf)); + read = immfs_read("/etc/config", 0, buf, sizeof(buf) - 1); + test_assert(read == (ssize_t)(strlen(payload) + strlen(suffix))); + + wait_for_uart_keypress(); + printf("[immfs] swapping memory into /etc/swap.bak\n"); + size_t page_size = 1u << seL4_PageBits; + uint8_t *page = (uint8_t *)xmalloc(page_size); + test_assert(page != NULL); + printf("[immfs] prepared page buffer at %p\n", page); + /* seed page with config contents and a pattern */ + memset(page, 0xCC, page_size); + memcpy(page, payload, strlen(payload)); + memcpy(page + strlen(payload), suffix, strlen(suffix)); + err = immfs_swap(page, "/etc/swap.bak"); + test_assert(err == 0); + printf("[immfs] swapped page buffer into /etc/swap.bak\n"); + uint8_t *page_read = (uint8_t *)xmalloc(page_size); + test_assert(page_read != NULL); + read = immfs_read("/etc/swap.bak", 0, page_read, page_size); + test_assert(read == (ssize_t)page_size); + test_assert(memcmp(page_read, page, page_size) == 0); + printf("[immfs] verified /etc/swap.bak contents\n"); + xfree(page_read); + + // printf("[immfs] write and read /var/log/boot.log\n"); + err = immfs_create("/var/log/boot.log"); + test_assert(err == 0); + const char *log1 = "boot start\n"; + const char *log2 = "boot ok\n"; + test_assert(immfs_write("/var/log/boot.log", 0, log1, strlen(log1)) == (ssize_t)strlen(log1)); + test_assert(immfs_write("/var/log/boot.log", strlen(log1), log2, strlen(log2)) == (ssize_t)strlen(log2)); + char logbuf[64] = {0}; + read = immfs_read("/var/log/boot.log", 0, logbuf, sizeof(logbuf) - 1); + test_assert(read == (ssize_t)(strlen(log1) + strlen(log2))); + + // printf("[immfs] unlink files and directories\n"); + + err = immfs_unlink("/etc/config"); + test_assert(err == 0); + test_assert(immfs_read("/etc/config", 0, buf, sizeof(buf)) == -ENOENT); + + err = immfs_unlink("/var"); + test_assert(err == -ENOTEMPTY); + test_assert(immfs_unlink("/var/log/boot.log") == 0); + test_assert(immfs_unlink("/var/log") == 0); + test_assert(immfs_unlink("/var") == 0); + + printf("[Lib: immfs] file system test completed successfully\n"); + return sel4test_get_result(); +} diff --git a/projects/sel4test/apps/sel4test-driver/src/init.h b/projects/sel4test/apps/sel4test-driver/src/init.h index 7f79d5c..68ad878 100644 --- a/projects/sel4test/apps/sel4test-driver/src/init.h +++ b/projects/sel4test/apps/sel4test-driver/src/init.h @@ -8,6 +8,8 @@ #include #include +#include + #include #include @@ -739,11 +741,20 @@ void *minikv_get(const char *key); int minikv_delete(const char *key); void minikv_destroy(void); +/* immfs: simple in-memory filesystem */ +int immfs_init(driver_env_t env); +int immfs_mkdir(const char *path); +int immfs_create(const char *path); +ssize_t immfs_write(const char *path, off_t offset, const void *buf, size_t len); +ssize_t immfs_read(const char *path, off_t offset, void *buf, size_t len); +int immfs_unlink(const char *path); + // Testsuite int test_malloc_free(driver_env_t env); int test_minikv(driver_env_t env); int test_wpool(driver_env_t env); +int test_immfs(driver_env_t env); void sel4test_driver_init(void) { printf("=== sel4test_driver_init started ===\n"); diff --git a/projects/sel4test/apps/sel4test-driver/src/main.c b/projects/sel4test/apps/sel4test-driver/src/main.c index f93d375..d7fdef2 100755 --- a/projects/sel4test/apps/sel4test-driver/src/main.c +++ b/projects/sel4test/apps/sel4test-driver/src/main.c @@ -19,6 +19,7 @@ #include #include #include "init.h" +#include "test.h" @@ -56,7 +57,7 @@ static ps_chardevice_t *get_uart_console(void) return &uart_console; } -static void wait_for_uart_keypress(void) +void wait_for_uart_keypress(void) { ps_chardevice_t *dev = get_uart_console(); if (dev == NULL) { @@ -75,7 +76,7 @@ static void wait_for_uart_keypress(void) if (ch >= 32 && ch < 127) { printf("Received '%c' (0x%x)\n", ch, ch); } else { - printf("Received byte 0x%x\n", ch & 0xff); + // printf("Received byte 0x%x\n", ch & 0xff); } } @@ -173,6 +174,7 @@ int main(void) test_assert_fatal(res == 0); ZF_LOGI("=== seL4 Test Driver completed ==="); + wait_for_uart_keypress(); printf("----------------ROOTSERVER--------------------\n"); seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); @@ -211,9 +213,11 @@ int main(void) printf("----------------MKUSERLIBS--------------------\n"); test_malloc_free(&env); - wait_for_uart_keypress(); + test_immfs(&env); + /* Test worker pool */ + wait_for_uart_keypress(); test_wpool(&env); diff --git a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c index 6af202a..91afa23 100644 --- a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c +++ b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c @@ -182,17 +182,17 @@ static inline uint64_t get_cycles(void) } /* Should not be reached if time_source_available() was checked */ static uint64_t fallback = 0; - printf("Warning: get_cycles fallback used\n"); + printf("[xmalloc] Warning: get_cycles fallback used\n"); return ++fallback; #endif } int test_allocation_performance(driver_env_t env) { - printf("Starting allocation performance test\n"); + printf("[xmalloc] starting allocation performance test\n"); if (!time_source_available()) { - printf("Timing source unavailable (no PMU export, no ltimer); skipping perf timings\n"); + printf("[xmalloc] timing source unavailable (no PMU export, no ltimer); skipping perf timings\n"); return 0; } @@ -203,7 +203,7 @@ int test_allocation_performance(driver_env_t env) void *ptrs[batch_size]; /* Time 4KB page allocations */ - printf("Getting cycles count before starting...\n"); + printf("[xmalloc] getting cycles count before starting...\n"); uint64_t start_4k = get_cycles(); for (int i = 0; i < num_small_pages; i += batch_size) { int current_batch_size = (i + batch_size <= num_small_pages) ? batch_size : num_small_pages - i; @@ -216,7 +216,7 @@ int test_allocation_performance(driver_env_t env) } } uint64_t end_4k = get_cycles(); - printf("4KB page allocation time: %" PRIu64 " cycles\n", end_4k - start_4k); + printf("[xmalloc] 4KB page allocation time: %" PRIu64 " cycles\n", end_4k - start_4k); /* Time 2MB page allocations */ uint64_t start_2m = get_cycles(); @@ -228,26 +228,26 @@ int test_allocation_performance(driver_env_t env) for (int i = 0; i < num_huge_pages; i++) { free_pages(ptrs[i]); } - printf("2MB page allocation time: %" PRIu64 " cycles\n", end_2m - start_2m); - - printf("Allocation performance test done\n"); + printf("[xmalloc] 2MB page allocation time: %" PRIu64 " cycles\n", end_2m - start_2m); + + printf("[xmalloc] allocation performance test done\n"); return 0; } int test_huge_page_allocation(driver_env_t env) { - printf("Testing huge page allocation\n"); + printf("[xmalloc] testing huge page allocation\n"); size_t huge_page_size = 1 << seL4_LargePageBits; void *huge = xmalloc(huge_page_size); test_assert(huge != NULL); - printf("Allocated huge block at %p\n", huge); + printf("[xmalloc] allocated huge block at %p\n", huge); memset(huge, 0xAB, huge_page_size); for (int i = 0; i < huge_page_size; i++) { test_assert(((unsigned char *)huge)[i] == 0xAB); } xfree(huge); - printf("Freed huge block\n"); - printf("Huge page allocation test done\n"); + printf("[xmalloc] freed huge block\n"); + printf("[xmalloc] huge page allocation test done\n"); return 0; } @@ -255,11 +255,12 @@ int test_malloc_free(driver_env_t env) { /* initialise allocator heap backed by vspace pages */ xmalloc_init(env); - printf("Initializing allocator\n"); + printf("[xmalloc] initializing allocator\n"); + wait_for_uart_keypress(); /* 1) Exercise slab allocations: allocate many small blocks until slabs * are exhausted and some allocations fall back to page-backed. */ - printf("Starting slab allocation test\n"); + printf("[xmalloc] starting slab allocation test\n"); const int NUM_SMALL = 70; /* should exceed per-class slab slots (default 64) */ void *small_ptrs[NUM_SMALL]; for (int i = 0; i < NUM_SMALL; i++) { @@ -270,36 +271,38 @@ int test_malloc_free(driver_env_t env) test_assert(((unsigned char *)small_ptrs[i])[j] == 0xA5); } } - printf("Slab allocation test done\n"); + printf("[xmalloc] slab allocation test done\n"); /* free a couple of small slots and reallocate to test reuse */ - printf("Testing reuse\n"); + printf("[xmalloc] testing reuse\n"); xfree(small_ptrs[10]); xfree(small_ptrs[20]); - printf("Freed small block 20\n"); + printf("[xmalloc] freed small block 20\n"); void *r1 = xmalloc(16); test_assert(r1 != NULL); void *r2 = xmalloc(16); test_assert(r2 != NULL); - printf("Reallocated block 2 at %p\n", r2); + printf("[xmalloc] reallocated block 2 at %p\n", r2); /* free them */ xfree(r1); xfree(r2); - printf("Reuse test done\n"); + printf("[xmalloc] reuse test done\n"); + wait_for_uart_keypress(); /* 2) Allocate a large, page-backed allocation and test read/write */ - printf("Testing large allocation\n"); + printf("[xmalloc] testing large allocation\n"); void *big = xmalloc(4096); test_assert(big != NULL); - printf("Allocated large block at %p\n", big); + printf("[xmalloc] allocated large block at %p\n", big); memset(big, 0x5A, 4096); for (int i = 0; i < 4096; i++) { test_assert(((unsigned char *)big)[i] == 0x5A); } xfree(big); - printf("Freed large block\n"); - printf("Large allocation test done\n"); + printf("[xmalloc] freed large block\n"); + printf("[xmalloc] large allocation test done\n"); + wait_for_uart_keypress(); /* 3) Test huge page allocation */ test_huge_page_allocation(env); @@ -308,13 +311,13 @@ int test_malloc_free(driver_env_t env) test_allocation_performance(env); /* 5) Clean up remaining small allocations */ - printf("Cleaning up remaining allocations\n"); + printf("[xmalloc] cleaning up remaining allocations\n"); for (int i = 0; i < NUM_SMALL; i++) { if (small_ptrs[i]) { xfree(small_ptrs[i]); } } - printf("Cleanup done\n"); + printf("[xmalloc] cleanup done\n"); printf("[Lib: xmalloc] malloc/free test completed successfully\n"); } diff --git a/projects/sel4test/apps/sel4test-driver/src/test.h b/projects/sel4test/apps/sel4test-driver/src/test.h index a2e5d9a..40865ce 100755 --- a/projects/sel4test/apps/sel4test-driver/src/test.h +++ b/projects/sel4test/apps/sel4test-driver/src/test.h @@ -11,6 +11,8 @@ #include #include +#include + #include #include #include @@ -91,6 +93,9 @@ typedef struct driver_env *driver_env_t; void plat_init(driver_env_t env) WEAK; +/* Expose UART keypress wait helper to other modules */ +void wait_for_uart_keypress(void); + #ifdef CONFIG_TK1_SMMU seL4_SlotRegion arch_copy_iospace_caps_to_process(sel4utils_process_t *process, driver_env_t env); #endif diff --git a/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c b/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c index b974e6b..13f9c59 100644 --- a/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c +++ b/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c @@ -29,7 +29,7 @@ static int worker_entry(seL4_Word ep_word, seL4_Word reply_word, seL4_Word a2, s } seL4_Word job = seL4_GetMR(0); seL4_SetMR(0, job + 1); - printf("Worker processed job %d, result %d\n", (int)job, (int)(job + 1)); + printf("[wpool] worker processed job %d, result %d\n", (int)job, (int)(job + 1)); api_reply(reply, seL4_MessageInfo_new(0, 0, 0, 1)); } #else @@ -42,7 +42,7 @@ static int worker_entry(seL4_Word ep_word, seL4_Word reply_word, seL4_Word a2, s } seL4_Word job = seL4_GetMR(0); seL4_SetMR(0, job + 1); - printf("Worker processed job %d, result %d\n", (int)job, (int)(job + 1)); + printf("[wpool] worker processed job %d, result %d\n", (int)job, (int)(job + 1)); seL4_Reply(seL4_MessageInfo_new(0, 0, 0, 1)); } #endif @@ -57,6 +57,7 @@ int test_wpool(driver_env_t env) /* allocate shared endpoint */ seL4_CPtr ep = vka_alloc_endpoint_leaky(&env->vka); test_assert(ep != 0); + printf("[wpool] allocated shared endpoint %d\n", (int)ep); /* create workers using sel4utils */ sel4utils_thread_t threads[N_WORKERS]; @@ -77,15 +78,17 @@ int test_wpool(driver_env_t env) (void *)(uintptr_t)ep, (void *)(uintptr_t)replies[i], 1); test_assert(err == 0); } + printf("[wpool] started %d worker threads\n", N_WORKERS); + wait_for_uart_keypress(); /* dispatch jobs */ for (int i = 0; i < N_JOBS; i++) { - printf("Dispatching job %d\n", i); + printf("[wpool] dispatching job %d\n", i); seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 1); seL4_SetMR(0, i); tag = seL4_Call(ep, tag); seL4_Word ret = seL4_GetMR(0); - printf("Received result %d\n", (int)ret); + printf("[wpool] received result %d\n", (int)ret); test_assert(ret == (seL4_Word)(i + 1)); } diff --git a/projects/sel4test/apps/sel4test-tests/src/main.c b/projects/sel4test/apps/sel4test-tests/src/main.c index a6e3cb2..776f6eb 100755 --- a/projects/sel4test/apps/sel4test-tests/src/main.c +++ b/projects/sel4test/apps/sel4test-tests/src/main.c @@ -231,14 +231,14 @@ int main(int argc, char **argv) sel4test_reset(); test_result_t result = SUCCESS; if (test) { - printf("Running test %s (%s)\n", test->name, test->description); + printf("[seL4-Libs] Running test %s (%s)\n", test->name, test->description); result = test->function((uintptr_t)&env); } else { result = FAILURE; ZF_LOGF("Cannot find test %s", init_data->name); } - printf("Test %s %s\n", init_data->name, result == SUCCESS ? "passed" : "failed"); + printf("[seL4-Libs] Test %s %s\n", init_data->name, result == SUCCESS ? "passed" : "failed"); /* send our result back */ seL4_MessageInfo_t info = seL4_MessageInfo_new(seL4_Fault_NullFault, 0, 0, 1); seL4_SetMR(0, result); -- Gitee From ef7373b82caccf3deb5127757d5edb509741fa00 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Mon, 29 Dec 2025 02:11:47 +0800 Subject: [PATCH 020/133] wpool balancing and remote stat integration --- .../sel4test/apps/sel4test-driver/src/init.h | 1 + .../sel4test/apps/sel4test-driver/src/main.c | 4 + .../apps/sel4test-driver/src/wpool_stub.c | 208 +++++++++++++----- 3 files changed, 163 insertions(+), 50 deletions(-) diff --git a/projects/sel4test/apps/sel4test-driver/src/init.h b/projects/sel4test/apps/sel4test-driver/src/init.h index 68ad878..257b03c 100644 --- a/projects/sel4test/apps/sel4test-driver/src/init.h +++ b/projects/sel4test/apps/sel4test-driver/src/init.h @@ -754,6 +754,7 @@ int immfs_unlink(const char *path); int test_malloc_free(driver_env_t env); int test_minikv(driver_env_t env); int test_wpool(driver_env_t env); +int test_wpool_remote(driver_env_t env, void *data_vaddr); int test_immfs(driver_env_t env); void sel4test_driver_init(void) { diff --git a/projects/sel4test/apps/sel4test-driver/src/main.c b/projects/sel4test/apps/sel4test-driver/src/main.c index d7fdef2..b6eee65 100755 --- a/projects/sel4test/apps/sel4test-driver/src/main.c +++ b/projects/sel4test/apps/sel4test-driver/src/main.c @@ -219,6 +219,10 @@ int main(void) /* Test worker pool */ wait_for_uart_keypress(); test_wpool(&env); + + /* Test remote worker pool load info */ + wait_for_uart_keypress(); + test_wpool_remote(&env, (void*)vaddrs[2]); return 0; diff --git a/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c b/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c index 13f9c59..640c889 100644 --- a/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c +++ b/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c @@ -1,104 +1,171 @@ -/* Simple worker pool using sel4utils threads. Spawns N workers that - * receive an integer job on a shared endpoint and reply with +1. +/* + * Simple worker pool with user-space load balancing. Workers block on + * per-thread endpoints and a dispatcher assigns each incoming job to the + * least-loaded worker. If a caller keeps targeting the same thread (e.g. + * two back-to-back jobs aimed at thread 1), the dispatcher will rebalance + * and hand the second job to an idle worker instead. */ #include #include +#include #include +#include #include +#include +#include #include "test.h" /* driver_env_t */ #include #include #include -/* Worker thread entry: arg0=endpoint, arg1=reply (MCS only) */ -static int worker_entry(seL4_Word ep_word, seL4_Word reply_word, seL4_Word a2, seL4_Word a3) +struct worker_ctx { + seL4_CPtr job_ep; + seL4_CPtr completion_ep; + seL4_Word worker_id; +}; + +/* Worker thread entry: arg0=worker_ctx* */ +static int worker_entry(seL4_Word ctx_word, seL4_Word unused1, seL4_Word unused2, seL4_Word unused3) { - seL4_CPtr ep = (seL4_CPtr)ep_word; - seL4_CPtr reply = (seL4_CPtr)reply_word; + struct worker_ctx *ctx = (struct worker_ctx *)ctx_word; + seL4_CPtr job_ep = ctx->job_ep; + seL4_CPtr completion_ep = ctx->completion_ep; + seL4_Word worker_id = ctx->worker_id; -#ifdef CONFIG_KERNEL_MCS while (1) { seL4_Word badge = 0; - seL4_MessageInfo_t tag = api_recv(ep, &badge, reply); + seL4_MessageInfo_t tag = seL4_Recv(job_ep, &badge); int len = (int)seL4_MessageInfo_get_length(tag); if (len == 0) { /* graceful exit: park thread to avoid returning */ for (;;) { seL4_Yield(); } } seL4_Word job = seL4_GetMR(0); - seL4_SetMR(0, job + 1); - printf("[wpool] worker processed job %d, result %d\n", (int)job, (int)(job + 1)); - api_reply(reply, seL4_MessageInfo_new(0, 0, 0, 1)); - } -#else - while (1) { - seL4_Word badge = 0; - seL4_MessageInfo_t tag = seL4_Recv(ep, &badge); - int len = (int)seL4_MessageInfo_get_length(tag); - if (len == 0) { - for (;;) { seL4_Yield(); } - } - seL4_Word job = seL4_GetMR(0); - seL4_SetMR(0, job + 1); - printf("[wpool] worker processed job %d, result %d\n", (int)job, (int)(job + 1)); - seL4_Reply(seL4_MessageInfo_new(0, 0, 0, 1)); + seL4_Word result = job + 1; + printf("[wpool] worker %ld processed job %ld, result %ld\n", + (long)worker_id, (long)job, (long)result); + + seL4_SetMR(0, result); + seL4_SetMR(1, worker_id); + seL4_SetMR(2, job); + seL4_Send(completion_ep, seL4_MessageInfo_new(0, 0, 0, 3)); } -#endif return 0; } int test_wpool(driver_env_t env) { - const int N_WORKERS = 2; - const int N_JOBS = 4; + const int N_WORKERS = 4; + /* Simulate two jobs intentionally aimed at worker 0 to show balancing */ + const struct { + int job_id; + int preferred_worker; + } jobs[] = { + {0, 0}, + {1, 0}, /* this would overload worker 0 without balancing */ + {2, 1}, + {3, 2}, + {4, 3}, + {5, 0}, + }; + const int N_JOBS = (int)(sizeof(jobs) / sizeof(jobs[0])); + + /* allocate completion endpoint shared by all workers */ + seL4_CPtr completion_ep = vka_alloc_endpoint_leaky(&env->vka); + test_assert(completion_ep != 0); + printf("[wpool] allocated completion endpoint %d\n", (int)completion_ep); - /* allocate shared endpoint */ - seL4_CPtr ep = vka_alloc_endpoint_leaky(&env->vka); - test_assert(ep != 0); - printf("[wpool] allocated shared endpoint %d\n", (int)ep); + /* allocate per-worker job endpoints */ + seL4_CPtr worker_eps[N_WORKERS]; + for (int i = 0; i < N_WORKERS; i++) { + worker_eps[i] = vka_alloc_endpoint_leaky(&env->vka); + test_assert(worker_eps[i] != 0); + printf("[wpool] worker %d job endpoint %d\n", i, (int)worker_eps[i]); + } /* create workers using sel4utils */ sel4utils_thread_t threads[N_WORKERS]; - seL4_CPtr replies[N_WORKERS]; + struct worker_ctx ctxs[N_WORKERS]; + for (int i = 0; i < N_WORKERS; i++) { + ctxs[i].job_ep = worker_eps[i]; + ctxs[i].completion_ep = completion_ep; + ctxs[i].worker_id = (seL4_Word)i; seL4_CPtr cnode = simple_get_cnode(&env->simple); seL4_Word data = api_make_guard_skip_word(seL4_WordBits - simple_get_cnode_size_bits(&env->simple)); sel4utils_thread_config_t cfg = thread_config_default(&env->simple, cnode, data, 0, seL4_MaxPrio - 1); int err = sel4utils_configure_thread_config(&env->vka, &env->vspace, &env->vspace, cfg, &threads[i]); test_assert(err == 0); -#ifdef CONFIG_KERNEL_MCS - replies[i] = vka_alloc_reply_leaky(&env->vka); - test_assert(replies[i] != 0); -#else - replies[i] = 0; -#endif err = sel4utils_start_thread(&threads[i], (sel4utils_thread_entry_fn)worker_entry, - (void *)(uintptr_t)ep, (void *)(uintptr_t)replies[i], 1); + (void *)&ctxs[i], NULL, 1); test_assert(err == 0); } printf("[wpool] started %d worker threads\n", N_WORKERS); wait_for_uart_keypress(); - /* dispatch jobs */ - for (int i = 0; i < N_JOBS; i++) { - printf("[wpool] dispatching job %d\n", i); - seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 1); - seL4_SetMR(0, i); - tag = seL4_Call(ep, tag); - seL4_Word ret = seL4_GetMR(0); - printf("[wpool] received result %d\n", (int)ret); - test_assert(ret == (seL4_Word)(i + 1)); + /* dispatch jobs with a simple least-loaded balancer */ + int in_flight[N_WORKERS]; + memset(in_flight, 0, sizeof(in_flight)); + + int dispatched = 0; + int completed = 0; + while (completed < N_JOBS) { + /* pick the least loaded worker */ + int chosen = 0; + int min_load = in_flight[0]; + for (int w = 1; w < N_WORKERS; w++) { + if (in_flight[w] < min_load) { + min_load = in_flight[w]; + chosen = w; + } + } + + /* seL4_Send is synchronous, so only dispatch to an idle worker. If + * everyone is busy (min_load > 0) or no jobs remain, block waiting + * for a completion to free a worker and to unblock any workers + * stuck sending to the completion endpoint. */ + bool can_dispatch = dispatched < N_JOBS && min_load == 0; + + if (can_dispatch) { + int preferred = jobs[dispatched].preferred_worker; + if (preferred != chosen) { + printf("[wpool] rebalanced job %d from preferred worker %d to worker %d (load %d vs %d)\n", + jobs[dispatched].job_id, preferred, chosen, in_flight[preferred], in_flight[chosen]); + } else { + printf("[wpool] assigning job %d to preferred worker %d (load %d)\n", + jobs[dispatched].job_id, chosen, in_flight[chosen]); + } + + seL4_SetMR(0, jobs[dispatched].job_id); + seL4_Send(worker_eps[chosen], seL4_MessageInfo_new(0, 0, 0, 1)); + in_flight[chosen]++; + dispatched++; + /* loop to allow more dispatching until we need to wait for completion */ + continue; + } + + /* wait for a completion before dispatching more */ + seL4_Word badge = 0; + seL4_MessageInfo_t tag = seL4_Recv(completion_ep, &badge); + test_assert(seL4_MessageInfo_get_length(tag) == 3); + seL4_Word result = seL4_GetMR(0); + seL4_Word worker = seL4_GetMR(1); + seL4_Word job = seL4_GetMR(2); + printf("[wpool] received result %ld for job %ld from worker %ld\n", (long)result, (long)job, (long)worker); + test_assert(result == (seL4_Word)(job + 1)); + in_flight[worker]--; + completed++; } /* shutdown */ for (int i = 0; i < N_WORKERS; i++) { - seL4_Send(ep, seL4_MessageInfo_new(0, 0, 0, 0)); + seL4_Send(worker_eps[i], seL4_MessageInfo_new(0, 0, 0, 0)); } /* join and destroy threads */ - /* cleanup threads */ + /* cleanup threads */ for (int i = 0; i < N_WORKERS; i++) { sel4utils_clean_up_thread(&env->vka, &env->vspace, &threads[i]); } @@ -106,3 +173,44 @@ int test_wpool(driver_env_t env) printf("[Lib: wpool] worker pool test completed successfully\n"); return sel4test_get_result(); } + +int test_wpool_remote(driver_env_t env, void *data_vaddr) +{ + uint64_t seed = 0; +#ifdef CONFIG_EXPORT_PMU_USER + sel4bench_init(); + seed = sel4bench_get_cycle_count(); +#else + if (env->ltimer.get_time) { + if (env->ltimer.get_time(env->ltimer.data, &seed) != 0) { + seed = 0; + } + } +#endif + if (seed == 0) { + printf("[wpool] Warning: using fixed seed for random generation\n"); + seed = 123456789; + } + srand((unsigned int)seed); + + const size_t OFFSET_2MB = 2 * 1024 * 1024; + volatile uint64_t *load_info = (volatile uint64_t *)((char *)data_vaddr + OFFSET_2MB); + const int NUM_NODES = 3; + const int CPU_LOW = 29; + const int CPU_HIGH = 72; + + /* Generate random load info within a fixed range [0, 100] */ + for (int i = 0; i < NUM_NODES; i++) { + load_info[i] = (uint64_t)(rand() % (CPU_HIGH - CPU_LOW + 1)) + CPU_LOW; + } + + printf("[wpool] Remote CPU load info at %p (offset 0x%lx):\n", load_info, (long)OFFSET_2MB); + for (int i = 0; i < NUM_NODES; i++) { + printf(" Compute Node %d load: %llu\n", i, (unsigned long long)load_info[i]); + } + + printf("[Lib: wpool] remote worker pool load info test completed successfully\n"); + + return 0; +} + -- Gitee From 9c5c6128040eb03fa50114d4cf5bf022adf5568f Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Mon, 29 Dec 2025 21:53:53 +0800 Subject: [PATCH 021/133] add explanation for init command in README --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8eac63c..f104ce2 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ ninja ./simulate ``` -The init config should be changed to this: +**For app test**, the init config should be changed to this: ``` ../init-build.sh -DPLATFORM=qemu-arm-virt -DSIMULATION=TRUE -DKernelArmExportPMUUser=TRUE ``` @@ -31,3 +31,9 @@ to make sure that: - it run in sel4test-driver, - thread init success, - and cycle counters available + +**For other platform**, e.g., IMX8MP, using: +``` +../init-build.sh -DPLATFORM=imx8mp-evk -DKernelArmExportPMUUser=TRUE +``` +or search "declare_platform" for more options -- Gitee From 0651c94eba783a77691eb7fb702b470a7e4ecb6f Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Mon, 29 Dec 2025 22:03:00 +0800 Subject: [PATCH 022/133] simulate a remote job dispatch procedure --- .../apps/sel4test-driver/src/wpool_stub.c | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c b/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c index 640c889..e218df3 100644 --- a/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c +++ b/projects/sel4test/apps/sel4test-driver/src/wpool_stub.c @@ -174,6 +174,10 @@ int test_wpool(driver_env_t env) return sel4test_get_result(); } +/* Remote load balancing test: + * Each compute node writes its current CPU load (0-100) to a shared + * memory region. + */ int test_wpool_remote(driver_env_t env, void *data_vaddr) { uint64_t seed = 0; @@ -208,6 +212,44 @@ int test_wpool_remote(driver_env_t env, void *data_vaddr) for (int i = 0; i < NUM_NODES; i++) { printf(" Compute Node %d load: %llu\n", i, (unsigned long long)load_info[i]); } + printf("[wpool] Concatenated load info: "); + for (int i = 0; i < NUM_NODES; i++) { + printf("%llu ", (unsigned long long)load_info[i]); + } + printf("\n"); + wait_for_uart_keypress(); + + /* Select the least loaded node */ + int least_loaded_node = -1; + uint64_t min_load = CPU_HIGH + 1; + for (int i = 0; i < NUM_NODES; i++) { + if (load_info[i] < min_load) { + min_load = load_info[i]; + least_loaded_node = i; + } + } + + if (least_loaded_node != -1) { + printf("[wpool] Selected least loaded node: %d with load %llu\n", + least_loaded_node, (unsigned long long)min_load); + + /* Push a remote call to the least loaded node */ + const size_t OFFSET_3MB = 3 * 1024 * 1024; + struct remote_call { + int target_node; + int job_param; + }; + volatile struct remote_call *call_info = + (volatile struct remote_call *)((char *)data_vaddr + OFFSET_3MB); + + call_info->target_node = least_loaded_node; + call_info->job_param = rand() % 1000; /* simulated job parameter */ + + printf("[wpool] Pushing remote call to node %d with job param %d at address %p\n", + call_info->target_node, call_info->job_param, call_info); + } else { + printf("[wpool] Could not determine least loaded node.\n"); + } printf("[Lib: wpool] remote worker pool load info test completed successfully\n"); -- Gitee From 3f9bde6494f7f100b94157b8ad829152ba0f2697 Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Tue, 30 Dec 2025 22:28:25 +0800 Subject: [PATCH 023/133] vritual and physical pages management shown --- .../libsel4utils/src/vspace/vspace.c | 1 + projects/seL4_libs/libsel4vka/CMakeLists.txt | 6 ++---- .../seL4_libs/libsel4vka/include/vka/object.h | 6 ++++++ projects/seL4_libs/libsel4vka/src/vka_debug.c | 3 +++ .../apps/sel4test-driver/src/malloc_stub.c | 19 ++++++++++++++++--- 5 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 projects/seL4_libs/libsel4vka/src/vka_debug.c diff --git a/projects/seL4_libs/libsel4utils/src/vspace/vspace.c b/projects/seL4_libs/libsel4utils/src/vspace/vspace.c index dfccc14..fb96baf 100755 --- a/projects/seL4_libs/libsel4utils/src/vspace/vspace.c +++ b/projects/seL4_libs/libsel4utils/src/vspace/vspace.c @@ -297,6 +297,7 @@ static int map_pages_at_vaddr(vspace_t *vspace, seL4_CPtr caps[], uintptr_t cook return error; } +// implementation of vspace_new_pages() static int new_pages_at_vaddr(vspace_t *vspace, void *vaddr, size_t num_pages, size_t size_bits, seL4_CapRights_t rights, int cacheable, bool can_use_dev) { diff --git a/projects/seL4_libs/libsel4vka/CMakeLists.txt b/projects/seL4_libs/libsel4vka/CMakeLists.txt index ef44463..b05d158 100755 --- a/projects/seL4_libs/libsel4vka/CMakeLists.txt +++ b/projects/seL4_libs/libsel4vka/CMakeLists.txt @@ -45,11 +45,9 @@ config_string( mark_as_advanced(LibVKAAllowMemoryLeaks LibVKADebugLiveSlotsSZ LibVKADebugLiveObjsSZ) add_config_library(sel4vka "${configure_string}") -file(GLOB deps src/*.c) +file(GLOB libsel4vka_sources "src/*.c") -list(SORT deps) - -add_library(sel4vka STATIC EXCLUDE_FROM_ALL ${deps}) +add_library(sel4vka STATIC EXCLUDE_FROM_ALL ${libsel4vka_sources}) target_include_directories( sel4vka PUBLIC include "sel4_arch_include/${KernelSel4Arch}" "arch_include/${KernelArch}" diff --git a/projects/seL4_libs/libsel4vka/include/vka/object.h b/projects/seL4_libs/libsel4vka/include/vka/object.h index 994e26d..5712547 100755 --- a/projects/seL4_libs/libsel4vka/include/vka/object.h +++ b/projects/seL4_libs/libsel4vka/include/vka/object.h @@ -28,6 +28,8 @@ typedef struct vka_object { seL4_Word size_bits; } vka_object_t; +extern int vka_debug_print_paddr; + /* * Generic object allocator used by functions below, can also be used directly */ @@ -69,6 +71,10 @@ static inline int vka_alloc_object_at_maybe_dev(vka_t *vka, seL4_Word type, seL4 result->type = type; result->size_bits = size_bits; + if (vka_debug_print_paddr) { + uintptr_t p_addr = vka_utspace_paddr(vka, result->ut, result->type, result->size_bits); + printf("VKA: allocated object type %lu, size %lu bits, at paddr %p\n", (unsigned long)type, (unsigned long)size_bits, (void*)p_addr); + } return 0; error: diff --git a/projects/seL4_libs/libsel4vka/src/vka_debug.c b/projects/seL4_libs/libsel4vka/src/vka_debug.c new file mode 100644 index 0000000..f1e4ec5 --- /dev/null +++ b/projects/seL4_libs/libsel4vka/src/vka_debug.c @@ -0,0 +1,3 @@ +#include + +int vka_debug_print_paddr = 0; diff --git a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c index 91afa23..8360647 100644 --- a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c +++ b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c @@ -251,6 +251,8 @@ int test_huge_page_allocation(driver_env_t env) return 0; } +extern int vka_debug_print_paddr; + int test_malloc_free(driver_env_t env) { /* initialise allocator heap backed by vspace pages */ @@ -304,13 +306,24 @@ int test_malloc_free(driver_env_t env) printf("[xmalloc] large allocation test done\n"); wait_for_uart_keypress(); - /* 3) Test huge page allocation */ + /* 3) test physical address printing */ + printf("[xmalloc] testing physical address management\n"); + vka_debug_print_paddr = 1; + void *ppage = xmalloc(4096); + vka_debug_print_paddr = 0; + printf("[xmalloc] allocated page-backed block at %p\n", ppage); + test_assert(ppage != NULL); + xfree(ppage); + printf("[xmalloc] physical address management test done\n"); + wait_for_uart_keypress(); + + /* 4) Test huge page allocation */ test_huge_page_allocation(env); - /* 4) Test allocation performance */ + /* 5) Test allocation performance */ test_allocation_performance(env); - /* 5) Clean up remaining small allocations */ + /* 6) Clean up remaining small allocations */ printf("[xmalloc] cleaning up remaining allocations\n"); for (int i = 0; i < NUM_SMALL; i++) { if (small_ptrs[i]) { -- Gitee From c40f998be636e28c03a0750dfcd94a36e8f0797f Mon Sep 17 00:00:00 2001 From: Zhenlin Date: Sun, 4 Jan 2026 23:06:59 +0800 Subject: [PATCH 024/133] alloc slab performance compared to linux --- .../apps/sel4test-driver/src/malloc_stub.c | 58 +++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c index 8360647..b4a2a35 100644 --- a/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c +++ b/projects/sel4test/apps/sel4test-driver/src/malloc_stub.c @@ -234,6 +234,51 @@ int test_allocation_performance(driver_env_t env) return 0; } +int test_slab_performance(driver_env_t env) +{ + /* ensure allocator is ready even if called standalone */ + if (!alloc_initted) { + xmalloc_init(env); + } + + printf("[xmalloc] starting slab performance test\n"); + + if (!time_source_available()) { + printf("[xmalloc] timing source unavailable (no PMU export, no ltimer); skipping slab perf timings\n"); + return 0; + } + + const uint64_t cycles_per_second = 1000ULL * 1000ULL * 1000ULL; /* assume 1GHz for throughput estimate */ + const size_t sizes[] = {16, 32, 64, 128, 256}; + const int iterations = 5000000; + const int nsizes = (int)(sizeof(sizes) / sizeof(sizes[0])); + + uint64_t ave_ops_per_sec = 0; + + for (int s = 0; s < nsizes; s++) { + size_t size = sizes[s]; + uint64_t start = get_cycles(); + for (int i = 0; i < iterations; i++) { + void *p = xmalloc(size); + test_assert(p != NULL); + xfree(p); + } + uint64_t end = get_cycles(); + uint64_t delta = end - start; + uint64_t ops_per_sec = delta ? ((uint64_t)iterations * cycles_per_second) / delta : 0; + uint64_t bytes_per_sec = delta ? ((uint64_t)iterations * size * cycles_per_second) / delta : 0; + + printf("[xmalloc] slab size %zu: %" PRIu64 " cycles, %" PRIu64 " ops/s\n", + size, delta, ops_per_sec); + ave_ops_per_sec += ops_per_sec; + } + ave_ops_per_sec /= nsizes; + printf("[xmalloc] average slab ops/s: %" PRIu64 "\n", ave_ops_per_sec); + + printf("[xmalloc] slab performance test done\n"); + return 0; +} + int test_huge_page_allocation(driver_env_t env) { printf("[xmalloc] testing huge page allocation\n"); @@ -317,20 +362,25 @@ int test_malloc_free(driver_env_t env) printf("[xmalloc] physical address management test done\n"); wait_for_uart_keypress(); - /* 4) Test huge page allocation */ + + /* 4) Slab-only performance measurements (small sizes under 512B) */ + test_slab_performance(env); + wait_for_uart_keypress(); + + /* 5) Test huge page allocation */ test_huge_page_allocation(env); - /* 5) Test allocation performance */ + /* 6) Test allocation performance */ test_allocation_performance(env); - /* 6) Clean up remaining small allocations */ + /* 7) Clean up remaining small allocations */ printf("[xmalloc] cleaning up remaining allocations\n"); for (int i = 0; i < NUM_SMALL; i++) { if (small_ptrs[i]) { xfree(small_ptrs[i]); } } - printf("[xmalloc] cleanup done\n"); + printf("[xmalloc] slab cleanup done\n"); printf("[Lib: xmalloc] malloc/free test completed successfully\n"); } -- Gitee From 1b97fc78bd60f974c4ced3bd56b282c858b4d529 Mon Sep 17 00:00:00 2001 From: Flower Black Date: Wed, 7 Jan 2026 16:12:02 +0800 Subject: [PATCH 025/133] monkey mnemosyne: memory block --- .../apps/monkey-mnemosyne/src/Block.h | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/Block.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/Block.h b/projects/sel4test/apps/monkey-mnemosyne/src/Block.h new file mode 100644 index 0000000..b239cfa --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/Block.h @@ -0,0 +1,47 @@ +/* + + Memory Block + + Created on 2025.2.7 at Xiangzhou, Zhuhai, Guangdong + + gongty [at] tongji [dot] edu [dot] cn + +*/ + + +#pragma once + +#include +#include + +struct Block { + adl::size_t size = 0; + char* data = nullptr; + + adl::int64_t id = 0; + + struct { + adl::int64_t readonly; + adl::int64_t readwrite; + } accessKey; + + + enum class ReferenceType : adl::int8_t { + READ_ONLY, + READ_WRITE + }; + + + adl::HashMap references; // app id -> whether this app has referenced this block. + adl::int64_t version = 0; // Version of this block's data. Incremented when data is written. + + bool isReferenced() { + return references.size() > 0; + } + + adl::size_t getReferenceCount() { + return references.size(); + } + +}; + -- Gitee From 025b26a78ac099adfcc0e36f38876d3ebb1318a5 Mon Sep 17 00:00:00 2001 From: Flower Black Date: Wed, 7 Jan 2026 16:12:10 +0800 Subject: [PATCH 026/133] monkey mnemosyne: config.h --- .../apps/monkey-mnemosyne/src/config.h | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/config.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/config.h b/projects/sel4test/apps/monkey-mnemosyne/src/config.h new file mode 100644 index 0000000..5719284 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/config.h @@ -0,0 +1,33 @@ +/* + * Monkey Mnemosyne Config + * + * Created on 2025.2.14 at Yushan, Shangrao + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + + +#pragma once + +#define MONKEY_MNEMOSYNE_DEBUG 1 // 1 or 0 + +#define MONKEY_MNEMOSYNE_HEAP_MEMORY_RESERVED (2 * 1024 * 1024) // in bytes + + +struct { + const char* conciergeIp; + const int conciergePort; + const char* ip; + const int port; + const int listenPort; + const char* key; +} static const MnemosyneRunConfig = { + .conciergeIp = "127.0.0.1", + .conciergePort = 5555, + .ip = "10.0.2.2", + .port = 10100, + .listenPort = 10100, + .key = "41f413a9-2c21-4721-9f0d-d8ee9b4961f1" +}; -- Gitee From 73d09b076d795a61a28088a85792450ef45f3b07 Mon Sep 17 00:00:00 2001 From: Flower Black Date: Wed, 7 Jan 2026 16:42:38 +0800 Subject: [PATCH 027/133] monkey mnemosyne: global memory manager. --- .../src/GlobalMemoryManager.h | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/GlobalMemoryManager.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/GlobalMemoryManager.h b/projects/sel4test/apps/monkey-mnemosyne/src/GlobalMemoryManager.h new file mode 100644 index 0000000..59b95c3 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/GlobalMemoryManager.h @@ -0,0 +1,162 @@ +/* + Mnemosyne Global Memory Manager + + By + gongty [at] alumni [dot] tongji [dot] edu [dot] cn + + Created on 2025.6.15 at Minhang, Shanghai + +*/ + +#pragma once + +#include + +#include + +#include +#include + +#include "./Block.h" +#include "./config.h" +#include + + +// Directed by Monkey Protocol V2 +class GlobalMemoryManager +{ +protected: + + + // block id -> block struct + // TODO: consider using wayland-style linked list so we can find apps' block faster by app id. + adl::HashMap memoryBlocks; + + adl::int64_t nextMemoryBlockId = 5000000001; + adl::int64_t nextAccessKey = 10000001; + + + enum { + LOCK_BLOCK_MAP = true, + DONT_LOCK_BLOCK_MAP = false, + }; + + void freeMemoryBlock(Block* b, bool lockBlockMap = LOCK_BLOCK_MAP) { + free(b->data); + + adl::defaultAllocator.free(b); + MONKEY_LOG_INFO("Released Block id: ", b->id, ", size ", b->size); + } +public: + GlobalMemoryManager() + { + + } + + virtual ~GlobalMemoryManager() {} + + + /** + * For app lounge. + * @param nodeId the node id of this block, used to generate access key. + * @return alloced block pointer if success + */ + Block* allocMemoryBlock(adl::int64_t appId, adl::int64_t nodeId) { + const adl::size_t size = 4096; + + const adl::size_t finalSize = size; + + Block* b = adl::defaultAllocator.alloc(); + b->data = (char*) malloc(finalSize); + if (!b->data) { + Genode::error("Failed to allocate memory block. Memory is not enough !!!"); + adl::defaultAllocator.free(b); + return nullptr; + } + + b->id = nextMemoryBlockId++; + b->size = finalSize; + + // only use 16 bit of node_id, 48 bits of nextAccessKey , we assume node_id + // is less than 65536. + b->accessKey.readonly = ((nodeId & 0xffff) << 48 | (nextAccessKey++ & 0xFFFFFFFFFFFF)); + b->accessKey.readwrite = ((nodeId & 0xffff) << 48 | (nextAccessKey++ & 0xFFFFFFFFFFFF)); + b->version = 0; + + + this->memoryBlocks[b->id] = b; + + b->references[appId] = Block::ReferenceType::READ_WRITE; // The creator of this block is the first one to reference it. + + return b; + } + + + /** + * Block's creator should call this since it has been + * seen as normal client as other who ref this block. + * + * @param accessKey + * @return -1 for failure, otherwise the block id. + */ + adl::int64_t refMemoryBlock(adl::int64_t appId, adl::int64_t accessKey) { + for (auto it : memoryBlocks) { + Block& block = *it.second; + bool canRead = (block.accessKey.readonly == accessKey); + bool canWrite = (block.accessKey.readwrite == accessKey); + if (canRead || canWrite) { + block.references[appId] = (canWrite) ? Block::ReferenceType::READ_WRITE : Block::ReferenceType::READ_ONLY; + return block.id; + } + } + + return -1; + } + + void unrefMemoryBlock(adl::int64_t appId, adl::int64_t blockId) { + + if (!memoryBlocks.contains(blockId)) { + return; + } + + Block* b = memoryBlocks[blockId]; + + // remove using noExcept mode. So nothing happens if appId is not found. + b->references.removeKey(appId, true); + + if (b->references.size() == 0) { + // No one references this block, we can free it. + freeMemoryBlock(b, DONT_LOCK_BLOCK_MAP); + } + } + + + enum GET_BLOCK_PERMISSION { + READ = 0, + WRITE = 1 + }; + + Block* getMemoryBlock(adl::int64_t appId, adl::int64_t blockId, adl::int8_t permission) { + + if (!memoryBlocks.contains(blockId)) { + Genode::warning("[GlobalMemoryManager] No such block. Block id: ", blockId); + return nullptr; // No such block. + } + + Block& block = *memoryBlocks[blockId]; + if (!block.references.contains(appId)) { + Genode::warning("[GlobalMemoryManager] No permission for app ", appId, " to access block ", blockId); + return nullptr; // No any permission. + } + + // Check permission. + + if (permission == WRITE && block.references[appId] != Block::ReferenceType::READ_WRITE) { + Genode::warning("[GlobalMemoryManager] App ", appId, " tries to write block ", blockId, + " but it is not writable. Permission: ", (int) block.references[appId]); + return nullptr; // You don't have write permission. + } + return █ + } + +}; -- Gitee From ac0df7f1aaa9c1778d02b9d7117b767fe20cfd8e Mon Sep 17 00:00:00 2001 From: Flower Black Date: Wed, 7 Jan 2026 16:43:43 +0800 Subject: [PATCH 028/133] monkey mnemosyne: App Lounge --- .../apps/monkey-mnemosyne/src/AppLounge.cc | 218 ++++++++++++++++++ .../apps/monkey-mnemosyne/src/AppLounge.h | 41 ++++ 2 files changed, 259 insertions(+) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc b/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc new file mode 100644 index 0000000..ce5da6c --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc @@ -0,0 +1,218 @@ +/* + * + * gongty [at] tongji [dot] edu [dot] cn + * Created on 2025.2.10 at Yushan, Shangrao + */ + +#include "./AppLounge.h" + +using namespace monkey; + +using monkey::net::IP4Addr; +using monkey::net::protocol::Msg; +using monkey::net::protocol::MsgType; +using monkey::net::protocol::Header; + +Status AppLounge::processTryAlloc() { + + Block* b = context.globalMemoryManager.allocMemoryBlock(client.appId, this->context.nodeId); + if (b == nullptr) { + client.sendResponse(2, "Failed to alloc. Maybe out of ram."); + return Status::SUCCESS; + } + + return client.replyTryAlloc( + b->id, + b->version, + b->accessKey.readonly, + b->accessKey.readwrite + ); +} + + +Status AppLounge::processReadBlock(adl::int64_t blockId) { + auto* b = context.globalMemoryManager.getMemoryBlock(client.appId, blockId, GlobalMemoryManager::READ); + + if (b == nullptr) { + Genode::warning("[AppLounge] But block not found or not accessible."); + client.sendResponse(1, "Block not found or not readable."); + return Status::INVALID_PARAMETERS; + } + return client.replyReadBlock(b->version, b->data); +} + + +Status AppLounge::processWriteBlock(adl::int64_t blockId, const adl::ByteArray& data) { + auto* b = context.globalMemoryManager.getMemoryBlock(client.appId, blockId, GlobalMemoryManager::WRITE); + + + if (b == nullptr) { + client.sendResponse(1, "Block not found or not writable."); + return Status::INVALID_PARAMETERS; + } + + adl::memcpy(b->data, data.data(), b->size); + + adl::int64_t newVersion = ++b->version; + adl::int64_t newVerNetOrder = adl::ntohq(newVersion); + return client.sendResponse(0, sizeof(newVerNetOrder), &newVerNetOrder); +} + + +Status AppLounge::processCheckAvailMem() { + return client.replyCheckAvailMem(1024 * 1024); // just set it to fixed value... +} + + +Status AppLounge::processFreeBlock(adl::int64_t blockId) { + context.globalMemoryManager.unrefMemoryBlock(client.appId, blockId); + return client.sendResponse(0); +} + + +Status AppLounge::processRefBlock(adl::int64_t accessKey) { + adl::int64_t bid = context.globalMemoryManager.refMemoryBlock(client.appId, accessKey); + + if (bid == -1) { + return client.sendResponse(1); + } + + adl::int64_t bidNetOrder = adl::htonq(bid); + return client.sendResponse(0, sizeof(bidNetOrder), &bidNetOrder); +} + + +Status AppLounge::processUnrefBlock(adl::int64_t blockId) { + context.globalMemoryManager.unrefMemoryBlock(client.appId, blockId); + return client.sendResponse(0); +} + + +Status AppLounge::processGetBlockDataVersion(adl::int64_t blockId) { + auto* b = context.globalMemoryManager.getMemoryBlock(client.appId, blockId, GlobalMemoryManager::READ); + if (!b) + return client.sendResponse(1); + + adl::int64_t dataVerNetOrder = adl::htonq(b->version); + return client.sendResponse(0, sizeof(dataVerNetOrder), &dataVerNetOrder); +} + + +#undef GET_AND_VERIFY_BLOCK + +Status AppLounge::serve() { + MONKEY_LOG_ERROR("Deprecated in monkey mnemosyne. Use serveOnce() instead."); +} + + +Status AppLounge::serveOnce() { + Status status = Status::SUCCESS; + + Msg* msg = nullptr; + status = client.recvMsg(&msg); + if (status != Status::SUCCESS) { + return status; + } + + + switch ((MsgType) msg->header.type) { + case MsgType::TryAlloc: { + // Generated by Google Gemini 2.0 Flash. Checked by GTY. + + status = processTryAlloc(); + + break; + } + + case MsgType::FreeBlock: { + // Generated by Google Gemini 2.0 Flash. Checked by GTY. + + adl::int64_t blockId; + if ((status = client.decodeFreeBlock(msg, &blockId)) != Status::SUCCESS) { + client.sendResponse(1, "Bad request."); + break; + } + status = processFreeBlock(blockId); + break; + } + + case MsgType::ReadBlock: { + // Generated by Google Gemini 2.0 Flash. Checked by GTY. + + adl::int64_t blockId; + if ((status = client.decodeReadBlock(msg, &blockId)) != Status::SUCCESS) { + client.sendResponse(1, "Bad request."); + break; + } + status = processReadBlock(blockId); + break; + } + + case MsgType::WriteBlock: { + // Generated by Google Gemini 2.0 Flash. + + adl::int64_t blockId; + adl::ByteArray data; + if ((status = client.decodeWriteBlock(msg, &blockId, data)) != Status::SUCCESS) { + client.sendResponse(1, "Bad request."); + break; + } + status = processWriteBlock(blockId, data); + break; + } + + case MsgType::CheckAvailMem: { + processCheckAvailMem(); + break; + } + + case MsgType::RefBlock: { + adl::int64_t accessKey; + if ((status = client.decodeRefBlock(msg, &accessKey)) != Status::SUCCESS) { + client.sendResponse(1, "Bad request."); + break; + } + status = processRefBlock(accessKey); + if (status != Status::SUCCESS) { + Genode::warning("Failed to ref block with access key ", accessKey); + } + break; + } + + case MsgType::UnrefBlock: { + adl::int64_t blockId; + if ((status = client.decodeUnrefBlock(msg, &blockId)) != Status::SUCCESS) { + client.sendResponse(1, "Bad request."); + break; + } + status = processUnrefBlock(blockId); + break; + } + + case MsgType::GetBlockDataVersion: { + adl::int64_t blockId; + if ((status = client.decodeGetBlockDataVersion(msg, &blockId)) != Status::SUCCESS) { + client.sendResponse(1, "Bad request."); + break; + } + status = processGetBlockDataVersion(blockId); + break; + } + + default: { + Genode::warning("> Message Type NOT SUPPORTED"); + status = Status::PROTOCOL_ERROR; + client.sendResponse(1, "Msg Type not supported."); + break; + } + } + + + client.freeMsg(msg); + if (status != Status::SUCCESS) { + Genode::warning("AppLounge::serve() failed with status: ", adl::int32_t(status), "."); + } + + + return status; +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.h b/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.h new file mode 100644 index 0000000..4a9042c --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.h @@ -0,0 +1,41 @@ +/* + * + * gongty [at] tongji [dot] edu [dot] cn + * Created on 2025.2.10 at Yushan, Shangrao + */ + + +#pragma once + +#include +#include + +#include "./main.h" +#include "./Block.h" + +struct AppLounge : monkey::net::SunflowerLounge +{ + AppLounge( + MnemosyneMain& main, + monkey::net::Protocol2Connection& client + ) + : SunflowerLounge(main, client) + {} + + // block id -> block + adl::HashMap memoryBlocks; + + monkey::Status processTryAlloc(); + monkey::Status processReadBlock(adl::int64_t blockId); + monkey::Status processWriteBlock(adl::int64_t blockId, const adl::ByteArray& data); + monkey::Status processCheckAvailMem(); + monkey::Status processFreeBlock(adl::int64_t blockId); + + monkey::Status processRefBlock(adl::int64_t accessKey); + monkey::Status processUnrefBlock(adl::int64_t blockId); + monkey::Status processGetBlockDataVersion(adl::int64_t blockId); + + virtual monkey::Status serve() override; + monkey::Status serveOnce(); +}; + -- Gitee From e5904c4c888eb838cb19cef669d37f65bb97d91f Mon Sep 17 00:00:00 2001 From: Flower Black Date: Wed, 7 Jan 2026 17:02:33 +0800 Subject: [PATCH 029/133] monkey mnemosyne: main.cc --- .../apps/monkey-mnemosyne/src/AppLounge.cc | 1 + .../apps/monkey-mnemosyne/src/main.cc | 423 ++++++++++++++++++ .../sel4test/apps/monkey-mnemosyne/src/main.h | 75 ++++ 3 files changed, 499 insertions(+) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/main.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/main.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc b/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc index ce5da6c..1c84b12 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc +++ b/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc @@ -102,6 +102,7 @@ Status AppLounge::processGetBlockDataVersion(adl::int64_t blockId) { Status AppLounge::serve() { MONKEY_LOG_ERROR("Deprecated in monkey mnemosyne. Use serveOnce() instead."); + return Status::FAILED; } diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/main.cc b/projects/sel4test/apps/monkey-mnemosyne/src/main.cc new file mode 100644 index 0000000..31befa4 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/main.cc @@ -0,0 +1,423 @@ +/* + * Monkey Mnemosyne : Memory provider. + * + * Created on 2025.2.4 at Xiangzhou, Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + + +#include "./main.h" + +#include + +#include + +#include + +#include +#include +#include + +#include + +#include "./AppLounge.h" + +using namespace monkey; + + +using HelloMode = net::ProtocolConnection::HelloMode; + + +static const adl::TString memSizeToHumanReadable(adl::size_t size) { + const char* levels[] = { + " B", " KB", " MB", " GB", " TB" + }; + + for (adl::size_t i = 0; i < sizeof(levels) / sizeof(levels[0]); i++) { + if (size < 8192) + return adl::TString::to_string(size) + levels[i]; + size /= 1024; + } + + return "+oo"; // Treat as infinity. +} + + +static void initAdlAlloc() { + + static struct { + + } adlAllocData; + + + adl::defaultAllocator.init({ + .alloc = [] (adl::size_t size, void* data) { + return malloc(size); + }, + + .free = [] (void* addr, adl::size_t size, void* data) { + free(addr); + }, + + .data = &adlAllocData + }); +} + + +Status MnemosyneMain::loadConfig() { + Status status = Status::SUCCESS; + +#define IF_STATUS_NOT_SUCCESS_THEN_RETURN() do { if (status != Status::SUCCESS) return status; } while (0) + + // concierge + { + status = config.concierge.ip.set(MnemosyneRunConfig.conciergeIp); + IF_STATUS_NOT_SUCCESS_THEN_RETURN(); + config.concierge.port = (adl::uint16_t) MnemosyneRunConfig.conciergePort; + } + + // mnemosyne + { + + status = config.mnemosyne.ip.set(MnemosyneRunConfig.ip); + IF_STATUS_NOT_SUCCESS_THEN_RETURN(); + config.mnemosyne.port = MnemosyneRunConfig.port; + config.mnemosyne.listenPort = MnemosyneRunConfig.listenPort; + config.mnemosyne.key = MnemosyneRunConfig.key; + } + + +#undef IF_STATUS_NOT_SUCCESS_THEN_RETURN + + Genode::log("--- mnemosyne config begin ---"); + Genode::log("> mnemosyne ip : ", config.mnemosyne.ip.toString().c_str()); + Genode::log("> mnemosyne port : ", config.mnemosyne.port); + Genode::log("> mnemosyne listen: ", config.mnemosyne.listenPort); + Genode::log("> concierge ip : ", config.concierge.ip.toString().c_str()); + Genode::log("> concierge port : ", config.concierge.port); + Genode::log("--- mnemosyne config end ---"); + + return status; +} + + +Status MnemosyneMain::init() { + initAdlAlloc(); + Status status = Status::SUCCESS; + + status = loadConfig(); + if (status != monkey::Status::SUCCESS) { + Genode::error("Exception on loading config. Check your .run file."); + return status; + } + + return status; +} + + +Status MnemosyneMain::clockIn() { // register self to monkey concierge. + Genode::log("Trying to clock in."); + + net::protocol::Response* response = nullptr; + + net::Protocol2Connection client; + client.ip = config.concierge.ip; + client.port = config.concierge.port; + + while (client.connect() != Status::SUCCESS) { + Genode::error("Failed to connect with server."); + Genode::error("> Retrying..."); + } + + Status status; + + // Open connection using latest protocol. + + if ((status = client.hello(net::protocol::LATEST_VERSION, HelloMode::CLIENT)) != Status::SUCCESS) { + Genode::error("(Clock In) Failed on [Hello]."); + goto END; + } + + if ((status = client.auth(config.mnemosyne.key)) != Status::SUCCESS) { + Genode::error("(Clock In) Failed on auth."); + goto END; + } + + + // Clock in. + + adl::int64_t id; + status = client.memoryNodeClockIn(&id, config.mnemosyne.ip, config.mnemosyne.port); + if (status != Status::SUCCESS) { + Genode::error("(Clock In) Failed on doing Memory Node Clock In."); + goto END; + } + Genode::log("(Clock In) Node ID is: ", id); + this->nodeId = id; + + // Get identity keys. + + if ((status = client.sendGetIdentityKeys()) != Status::SUCCESS) + goto END; + + if ((status = client.recvResponse(&response)) != Status::SUCCESS) + goto END; + + if (response->code != 0) { + adl::ByteArray bMsg {response->msg, response->msgLen}; + Genode::error(bMsg.toString().c_str()); + goto END; + } + + + // Appreciate identity keys. + + { + struct { + MnemosyneMain* main; + } data; + + data.main = this; + + status = client.appreciateGetIdentityKeys( + *response, + &data, + [] ( + net::Protocol1Connection::ReplyGetIdentityKeysParams::NodeType nodeType, + net::Protocol1Connection::ReplyGetIdentityKeysParams::KeyType keyType, + const adl::ByteArray& key, + adl::int64_t id, + void* rawData + ) { + + const auto AppType = net::Protocol1Connection::ReplyGetIdentityKeysParams::NodeType::App; + const auto RC4Type = net::Protocol1Connection::ReplyGetIdentityKeysParams::KeyType::RC4; + + auto typedData = (decltype(data) *) rawData; + + // We don't care about memory nodes' keys. We can't deal algorithms other than RC4. + + if (nodeType != AppType || keyType != RC4Type) { + Genode::log( + "Key ignored: (", + adl::int8_t(nodeType), + ", ", + adl::int8_t(keyType), + ") ", + key.toString().c_str() + ); + return; + } + + typedData->main->appKeys[id] = key; + Genode::log("From Concierge: App key: [", id, "] [", key.toString().c_str(), "]"); + } + + ); + + if (status != Status::SUCCESS) { + Genode::error("Error on loading identity keys from Concierge."); + goto END; + } + } + + + // Cleanup. + +END: + client.close(); + if (response) { + client.freeMsg(response); + response = nullptr; + } + return status; +} + + + +AppLounge* MnemosyneMain::serveClient(net::Socket4& conn) { + Genode::log("Client connected: ", conn.ip.toString().c_str(), " [", conn.port, "]"); + + Status status; + + net::Protocol2Connection client; + client.socketFd = conn.socketFd; + client.ip = conn.ip; + client.port = conn.port; + + // Force use protocol v2. + if (client.hello(net::protocol::LATEST_VERSION, HelloMode::SERVER) != Status::SUCCESS) + return nullptr; + Genode::log("> Using protocol version ", net::protocol::LATEST_VERSION, "."); + + // Auth + + if ((status = client.auth(&appKeys, nullptr)) != Status::SUCCESS) { + Genode::error("Failed on auth. Status: ", adl::int32_t(status)); + return nullptr; + } + + if (client.nodeType != net::Protocol1Connection::NodeType::App) { + Genode::error("Client is not App node!"); + Genode::error("> Connection between memory nodes is not supported yet."); + return nullptr; + } + + // Now, we can say client is an authenticated App node. + + // Enter lounge. + + if (client.nodeType == net::Protocol1Connection::NodeType::App) + { + + auto lounge = new AppLounge {*this, client}; + if (lounge == nullptr) { + Genode::error("Failed to create lounge thread. Out of memory."); + client.close(); + return nullptr; + } + + return lounge; + } + + return nullptr; +} + + +Status MnemosyneMain::runServer() { + Genode::log("Starting server.."); + + net::Socket4 server; + server.port = config.mnemosyne.listenPort; + server.ip = INADDR_ANY; + + Status status = server.start(); + if (status != Status::SUCCESS) { + return status; + } + + + // client listen fd -> app lounge + adl::HashMap lounges; + + + while (true) { + fd_set readFds; + FD_ZERO(&readFds); + + int maxFd = server.socketFd; + + FD_SET(server.socketFd, &readFds); + for (auto it : lounges) { + if (clientLoungeRecycleBin.contains(it.second)) + continue; + + FD_SET(it.first, &readFds); + if (it.first > maxFd) + maxFd = it.first; + } + + int selectResult = select(maxFd + 1, &readFds, nullptr, nullptr, nullptr); + if (selectResult < 0) { + MONKEY_LOG_ERROR("Select failed."); + break; + } + + for (auto it : clientLoungeRecycleBin) { + lounges.removeKey(it.second); + adl::defaultAllocator.free(it.first); + } + + clientLoungeRecycleBin.clear(); + + if (FD_ISSET(server.socketFd, &readFds)) { + auto client = server.accept(true); + auto lounge = serveClient(client); + + if (lounge) + lounges[client.socketFd] = lounge; + else { + client.close(); + Genode::warning("Client rejected: ", client.ip.toString().c_str(), " [", client.port, "]"); + } + + } + + + for (auto it : lounges) { + auto client = it.second->client; + auto sockFd = client.socketFd; + if (!FD_ISSET(it.first, &readFds)) + continue; + + Status status = it.second->serveOnce(); + if (status != Status::SUCCESS) { + Genode::log("Client disconnected: ", client.ip.toString().c_str(), " [", client.port, "]"); + client.close(); + clientLoungeRecycleBin[it.second] = sockFd; + } + + } + + + } + + + for (auto it : lounges) { + it.second->client.close(); + adl::defaultAllocator.free(it.second); + } + + server.close(); + return Status::SUCCESS; +} + + + +Status MnemosyneMain::run() { + Status status = clockIn(); + if (status != Status::SUCCESS) { + Genode::error("Something went wrong during clock-in. Status: ", adl::int32_t(status)); + return status; + } + + status = runServer(); + + return status; +} + + +void MnemosyneMain::cleanup() { + +} + + +MnemosyneMain::MnemosyneMain() { + + Genode::log("Welcome to Monkey Mnemosyne."); + + + Status status = init(); + if (status != Status::SUCCESS) { + Genode::error("Failed on init."); + cleanup(); + return; + } + + status = run(); + if (status != Status::SUCCESS) { + Genode::warning("Something went wrong on runtime."); + } + + + cleanup(); + Genode::log("Bye :D"); +} + + +int main() { + static MnemosyneMain main; +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/main.h b/projects/sel4test/apps/monkey-mnemosyne/src/main.h new file mode 100644 index 0000000..47558b6 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/main.h @@ -0,0 +1,75 @@ +/* + * Monkey Mnemosyne : Memory provider. + * + * Created on 2025.2.4 at Xiangzhou, Zhuhai, Guangdong + * + * + * gongty [at] tongji [dot] edu [dot] cn + * + */ + + +#pragma once + +#include "./config.h" + +#include +#include +#include + + +#include +#include +#include + +#include "./Block.h" + + +#include "./GlobalMemoryManager.h" + + +struct AppLounge; + +struct MnemosyneMain { + + adl::int64_t nodeId = 0; + + + GlobalMemoryManager globalMemoryManager; + + adl::HashMap appKeys; + + // key: AppLounge. value: network socket fd. + adl::HashMap clientLoungeRecycleBin; + + struct { + struct { + monkey::net::IP4Addr ip; + adl::uint16_t port; + } concierge; + + struct { + monkey::net::IP4Addr ip; + adl::uint16_t port; + adl::uint16_t listenPort; + adl::ByteArray key; + } mnemosyne; + } config; + + MnemosyneMain(); + + + monkey::Status loadConfig(); + monkey::Status init(); + + monkey::Status clockIn(); + AppLounge* serveClient(monkey::net::Socket4& conn); + monkey::Status runServer(); + + monkey::Status run(); + void cleanup(); + + +}; + + -- Gitee From 95554a54190a4d8610143f8699ddfebca88c54a8 Mon Sep 17 00:00:00 2001 From: Flower Black Date: Wed, 7 Jan 2026 17:04:29 +0800 Subject: [PATCH 030/133] omit one error. --- .../apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp index de06f5f..0865069 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp +++ b/projects/sel4test/apps/monkey-mnemosyne/src/adl/collections/RedBlackTree.hpp @@ -923,7 +923,7 @@ template adl::size_t RedBlackTree::size() { adl::size_t count = 0; - for (const auto& it : *this) { + for (const auto& _ : *this) { count++; } -- Gitee From 0be6c42376fcdb5e237a612d70ce4266c4697e22 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:12:12 +0800 Subject: [PATCH 031/133] upload source codes --- .../apps/front/include/common_utils.h | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 projects/sel4test/apps/front/include/common_utils.h diff --git a/projects/sel4test/apps/front/include/common_utils.h b/projects/sel4test/apps/front/include/common_utils.h new file mode 100644 index 0000000..f833833 --- /dev/null +++ b/projects/sel4test/apps/front/include/common_utils.h @@ -0,0 +1,51 @@ +#ifndef COMMON_UTILS_H_ +#define COMMON_UTILS_H_ + +#include +#include +#include + +#define UTILS_ENABLE_DEBUG 1 + +void error_print(char *error); + +int debug_print(const char *format, ...); + +#if UTILS_ENABLE_DEBUG +#define utils_print(...) printf(__VA_ARGS__) +#else +#define utils_print(...) do {} while(0) +#endif + + +/** + * @brief Print the content of a uint8_t array in the specified format + * @param BUF Pointer to the start of the uint8_t array (must be a valid pointer) + * @param SIZE Number of elements in the array (must be a non-negative integer, preferably of type size_t) + * @param FORMAT printf-style format string (must match the uint8_t type, e.g., "%02hhx ", "%hhu ", "%hho ", "%c") + * + * Notes: + * 1. The format specifier must use the "hh" length modifier for uint8_t (to avoid sign extension issues): + * - Decimal: %hhu (unsigned) + * - Hexadecimal: %hhx (lowercase), %hhX (uppercase) + * - Octal: %hho + * - ASCII: %c (Note: Non-printable characters may display abnormally; handle them as needed) + * 2. Iterates from index 0 to SIZE-1, outputting each element in the specified FORMAT + * 3. Automatically adds a newline after output to separate different buffer contents + */ +#define DUMP_BUFFER_CONTENT(BUF, SIZE, FORMAT) do { \ + /* Boundary check: Return directly if SIZE is 0 to avoid invalid loops */ \ + if ((SIZE) == 0) { \ + printf("Buffer is empty (size = 0)\n"); \ + break; \ + } \ + /* Traverse the array and output each element in the specified format */ \ + for (size_t i = 0; i < (SIZE); ++i) { \ + /* Force cast to uint8_t* to ensure type correctness and avoid pointer type mismatch */ \ + printf(FORMAT, ((const uint8_t*)(BUF))[i]); \ + } \ + /* Add a newline after output to distinguish between different buffer outputs */ \ + printf("\n"); \ +} while (0) + +#endif \ No newline at end of file -- Gitee From d4ef015d7c8bed77c0610b0b9266fcaaec4710f9 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:24:05 +0800 Subject: [PATCH 032/133] upload source codes --- projects/sel4test/apps/front/include/dev.h | 59 + projects/sel4test/apps/front/include/engine.h | 280 ++++ .../apps/front/include/frontend_api.h | 23 + .../apps/front/include/frontend_proto.h | 80 ++ .../sel4test/apps/front/include/message.h | 609 +++++++++ projects/sel4test/apps/front/include/queue.h | 596 +++++++++ .../apps/front/include/senario_test.h | 34 + .../sel4test/apps/front/include/session.h | 343 +++++ .../apps/front/include/session_pool.h | 63 + .../apps/front/include/shared_mem_io.h | 567 ++++++++ projects/sel4test/apps/front/include/uthash.h | 1137 +++++++++++++++++ 11 files changed, 3791 insertions(+) create mode 100644 projects/sel4test/apps/front/include/dev.h create mode 100644 projects/sel4test/apps/front/include/engine.h create mode 100644 projects/sel4test/apps/front/include/frontend_api.h create mode 100644 projects/sel4test/apps/front/include/frontend_proto.h create mode 100644 projects/sel4test/apps/front/include/message.h create mode 100644 projects/sel4test/apps/front/include/queue.h create mode 100644 projects/sel4test/apps/front/include/senario_test.h create mode 100644 projects/sel4test/apps/front/include/session.h create mode 100644 projects/sel4test/apps/front/include/session_pool.h create mode 100644 projects/sel4test/apps/front/include/shared_mem_io.h create mode 100644 projects/sel4test/apps/front/include/uthash.h diff --git a/projects/sel4test/apps/front/include/dev.h b/projects/sel4test/apps/front/include/dev.h new file mode 100644 index 0000000..bd002e1 --- /dev/null +++ b/projects/sel4test/apps/front/include/dev.h @@ -0,0 +1,59 @@ +#ifndef DEV_H_ +#define DEV_H_ + +#define MAX_DEV_NAME 32 +#define MAX_HS_DEV_NUM 16 +#define MIN_HS_DEV_NUM 1 + + +/* + * High Speed Network device type enumeration + * Contains five common types of network devices + */ +typedef enum { + TRADITIONAL_ETHERNET = 0, // Traditional Ethernet device + TSN, // Time-Sensitive Networking device + WIFI, // WiFi wireless network device + LTE_MODULE, // 4G module (LTE technology standard) + NR_MODULE // 5G module (NR technology standard) +} HSNetDevType; + + +/** + * @brief Frontend device basic information structure + * @details This structure stores the core configuration and status parameters of a single frontend device, + * including unique identifier, type, running status and human-readable device name. + * It is used as the basic data unit in the device list management module. + * @note The name array is a fixed-length character buffer, and the valid string must be terminated with a null character ('\0'). + * @note The values of dev_type and dev_status are usually defined by enumerated types or global macros for unified management. + */ +typedef struct FrontendDevInfo_{ + int dev_id; + int dev_type; + int dev_status; + char name[MAX_DEV_NAME]; +}FrontendDevInfo; + + +/** + * @brief Configuration structure for frontend device list management + * @details This structure aggregates the global configuration data for a set of frontend devices. It defines the total number of devices and references a contiguous array + * that stores the detailed configuration of each individual device. + * @note The memory of the array pointed by @p dev_info can be allocated statically, globally, on the stack, or dynamically. Memory management (allocation and deallocation) + * is the responsibility of the caller. The number of valid array elements must match the value of @p dev_num to avoid out-of-bounds access. + */ +typedef struct FrontendDevListCfg_{ + int dev_num; + FrontendDevInfo *dev_info; +}FrontendDevListCfg; + + +typedef struct FrontendHighSpeedNetDeviceSet_ { + FrontendDevInfo hs_net_dev[MAX_HS_DEV_NUM]; +}FrontendHighSpeedNetDeviceSet; + +FrontendDevListCfg *p_global_dev_list_cfg; + +void frontend_init_dev_list(); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h new file mode 100644 index 0000000..f299d05 --- /dev/null +++ b/projects/sel4test/apps/front/include/engine.h @@ -0,0 +1,280 @@ +#ifndef ENGINE_H +#define ENGINE_H + +// #include +// #include + +#include "dev.h" +#include "session_pool.h" +#include "shared_mem_io.h" +#include "common_utils.h" + + +#define HS_NET_DEV_CFG "hs_net_dev.ini" +/* + * Check if the string is a valid IPv4 address + * Parameter: ip_str - string to check + * Returns: 1 - valid IPv4, 0 - invalid + */ +#define DEV_IS_IPV4(ip_str) ({ \ + struct in_addr addr; \ + inet_pton(AF_INET, (ip_str), &addr) == 1; \ +}) + +/* + * Check if the string is a valid IPv6 address + * Parameter: ip_str - string to check + * Returns: 1 - valid IPv4, 0 - invalid + */ +#define DEV_IS_IPV6(ip_str) ({ \ + struct in6_addr addr; \ + inet_pton(AF_INET6, (ip_str), &addr) == 1; \ +}) + +/* + * Check if the string is a valid IP address (IPv4 or IPv6) + * Parameter: ip_str - string to check + * Returns: 1 - valid IP, 0 - invalid + */ +#define DEV_IS_VALID_IP(ip_str) (DEV_IS_IPV4(ip_str) || DEV_IS_IPV6(ip_str)) + +/* + * Get the IP address type + * Parameter: ip_str - string to check + * Returns: SESS_NON_IP_PROTO - invalid, SESS_IPV4_PROTO - IPv4, SESS_IPV6_PROTO - IPv6 + */ +#define DEV_IP_TYPE(ip_str) ({ \ + int type = SESS_NON_IP_PROTO; \ + if (DEV_IS_IPV4(ip_str)) { \ + type = SESS_IPV4_PROTO; \ + } else if (DEV_IS_IPV6(ip_str)) { \ + type = SESS_IPV6_PROTO; \ + } \ + type; \ +}) + + +#define DEV_IS_VALID_IP(ip_str) (DEV_IS_IPV4(ip_str) || DEV_IS_IPV6(ip_str)) + +struct FrontendEngOps; + +/** + * @brief Frontend engine command state enumeration + * + * Enumerates all possible states of device (dev) and strategy (strgy) message transmission + * between frontend engine and backend. + */ +typedef enum { + FRONTEND_CMD_READY, ///< Ready for transmission (no pending dev/strgy messages) + FRONTEND_CMD_WAITTING, ///< Dev/strgy message is being constructed, waiting for backend processing + FRONTEND_CMD_OK, ///< Dev/strgy message processed successfully by backend + FRONTEND_CMD_ERROR ///< Dev/strgy message processing failed by backend +} FrontendCmdState; + +typedef struct FrontendEngine_{ + uint16_t dev_num; + FrontendHighSpeedNetDeviceSet *dev_set; // High speed network device set + uint16_t sel_id; + uint16_t active_mask; // Show the positions of all the active high-speed network devices as a mask. + uint16_t eng_cmd_id; // Used when constructing device (dev) messages and strategy (strgy) messages, filled into the msg_id member of these messages. +/* + * Maintains the state of dev message and strgy message transmitted by the engine. Supported states: FRONTEND_CMD_READY (transmittable), FRONTEND_CMD_WAITTING (dev/strgy message + * is being constructed, waiting for backend processing), FRONTEND_CMD_OK (dev/strgy message processed successfully by backend), FRONTEND_CMD_ERROR (dev/strgy message processing + * failed by backend). The state will be restored to FRONTEND_CMD_READY after proper handling of FRONTEND_CMD_OK and FRONTEND_CMD_ERROR. + */ + FrontendCmdState eng_cmd_state; + struct FrontendSessionPool *sess_pool; // Session pool + struct SharedMemoryPool *mem_pool; // Shared memory pool + struct SharedMemoryPoolLock *mem_pool_lock; // Shared memory pool lock + struct SharedMemoryPoolQueue *rx_queue; // RX queue + struct SharedMemoryPoolQueue *tx_queue; // TX queue + struct FrontendEngOps *ops; +} FrontendEngine; + + +struct FrontendEngOps { + int (*enable_dev)(FrontendEngine *eng, uint16_t mask); // Enable devices in the high-speed network device set + int (*disable_dev)(FrontendEngine *eng, uint16_t mask); // Disable devices in the high-speed network device set + int (*query_dev)(FrontendEngine *eng, uint16_t *mask); // Query information/status of devices in the high-speed network device set + // int (*choose_dev)(FrontendEngine *eng, uint16_t *dev_id); // Choose the most appropriate high-speed network device over which to establish a session, and record its device ID + int (*conf_dev_selector)(FrontendEngine *eng, uint16_t sel_id); // Configure the device selection function/strategy for the high-speed network device set + int (*query_dev_sel_id)(FrontendEngine *eng, uint16_t *sel_id); // Query the ID of the current device selector in the backend engine +}; + +#define HS_DEV_SELECTOR_NAME_LEN 30 +#define HS_DEV_SELECTOR_NUM 3 + + +typedef struct{ + char sel_name[HS_DEV_SELECTOR_NAME_LEN]; + int (*choose_dev)(FrontendEngine *eng, uint16_t *dev_id); +} HSDevSelector; + + +extern FrontendEngine *p_g_fr_eng; + + +void frontend_engine_init(); +void frontend_engine_run(); +void frontend_engine_destory(); + +FrontendEngine *frontend_get_global_engine(); + +int frontend_engine_init_eng_ops(FrontendEngine *eng); +int engine_init_selector(FrontendEngine *eng); + + +int engine_init_hs_net_dev(FrontendEngine *eng); +int engine_init_sess_pool(FrontendEngine *eng); +int engine_init_shared_mem_pool(FrontendEngine *eng); +int engine_init_shared_mem_pool_lock(FrontendEngine *eng); +int engine_init_shared_mem_queue(FrontendEngine *eng); + + +void engine_destory_hs_net_dev(FrontendEngine *eng); +void engine_destory_sess_pool(FrontendEngine *eng); +void engine_destory_mem_pool(FrontendEngine *eng); +void engine_destory_mem_pool_lock(FrontendEngine *eng); + +int engine_choose_hs_net(FrontendEngine *eng, int *selected_dev_id); + + +/** + * @brief Get the f2b queue from FrontendEngine's session pool + * + * This macro retrieves the f2b queue from the FrontendSessionPool associated with a FrontendEngine. + * It includes null pointer checks for the engine and its session pool. Error messages are printed + * via error_print() when checks fail, with the macro name included for debugging. + * + * @param engine Pointer to a FrontendEngine structure; must not be NULL for valid queue retrieval + * @param result_var Variable to store the result (pointer to FrontendSessionQueue or NULL) + * + * @note The result is stored in the provided result_var, which should be of type + * 'struct FrontendSessionQueue *' + */ +#define FRONTEND_ENGINE_GET_F2B_QUEUE(engine, result_var) \ + do { \ + /* Initialize result to NULL by default */ \ + (result_var) = NULL; \ + \ + /* Check if FrontendEngine pointer is NULL */ \ + utils_print("In FRONTEND_ENGINE_GET_F2B_QUEUE, address of engine is %p \n", engine); \ + if (!(engine)) { \ + error_print("[FRONTEND_ENGINE_GET_F2B_QUEUE] Error:FrontendEngine pointer is NULL when getting f2b queue"); \ + } \ + /* Check if session pool within frontEngine is NULL */ \ + else if (!(engine)->sess_pool) { \ + error_print("[FRONTEND_ENGINE_GET_F2B_QUEUE] Error: FrontendSessionPool in frontEngine is NULL when getting f2b queue"); \ + } \ + /* All checks passed - retrieve the f2b queue */ \ + else { \ + (result_var) = &(engine)->sess_pool->queue_f2b; \ + } \ + } while (0) + +/** + * @brief Get the b2f queue from FrontendEngine's session pool + * + * This macro retrieves the b2f queue from the FrontendSessionPool associated with a FrontendEngine. + * It includes null pointer checks for the engine and its session pool. Error messages are printed + * via error_print() when checks fail, with the macro name included for debugging. + * + * @param engine Pointer to a FrontendEngine structure; must not be NULL for valid queue retrieval + * @param result_var Variable to store the result (pointer to FrontendSessionQueue or NULL) + * + * @note The result is stored in the provided result_var, which should be of type + * 'struct FrontendSessionQueue *' + */ +#define FRONTEND_ENGINE_GET_B2F_QUEUE(engine, result_var) \ + do { \ + /* Initialize result to NULL by default */ \ + (result_var) = NULL; \ + \ + /* Check if FrontendEngine pointer is NULL */ \ + if (!(engine)) { \ + error_print("[FRONTEND_ENGINE_GET_B2F_QUEUE] Error: FrontendEngine pointer is NULL when getting b2f queue"); \ + } \ + /* Check if session pool within FrontendEngine is NULL */ \ + else if (!(engine)->sess_pool) { \ + error_print("[FRONTEND_ENGINE_GET_B2F_QUEUE] Error: FrontendSessionPool in FrontendEngine is NULL when getting b2f queue"); \ + } \ + /* All checks passed - retrieve the b2f queue */ \ + else { \ + (result_var) = &(engine)->sess_pool->queue_b2f; \ + } \ + } while (0) + + +/** + * @brief Get data from the specified RX queue (residing in shared memory) + * @param queue Pointer to the SharedMemoryPoolQueue (RX queue) to operate on + * @param[out] buf_ptr Double pointer to store the address of data in shared memory + * (points to actual data location in shared memory on success) + * @param buf_max_len Maximum allowed length of data that can be retrieved (in bytes) + * @param[out] out_len Pointer to store the actual length of obtained data (in bytes) + * @return FRONTEND_PROXY_PROCESS_OK if data is retrieved successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., invalid queue handle); + * FRONTEND_PROXY_PROCESS_AGAIN if data is temporarily unavailable (e.g., queue is empty) + * @note The caller is responsible for managing the lock of the shared memory pool + * (lock once before multiple calls to reduce overhead) + */ +int frontend_engine_rx_queue_get(struct SharedMemoryPoolQueue *queue, void **buf_ptr, + size_t buf_max_len, size_t *out_len); + +/** + * @brief Send data through the specified TX queue (residing in shared memory) + * @param queue Pointer to the SharedMemoryPoolQueue (TX queue) to operate on + * @param[in] data_ptr Double pointer to the data in shared memory to be sent + * (points to actual data location in shared memory) + * @param data_len Length of the data to be sent (in bytes) + * @param[out] sent_len Pointer to store the actual length of data sent (in bytes) + * @return FRONTEND_PROXY_PROCESS_OK if data is sent successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., queue access violation); + * FRONTEND_PROXY_PROCESS_AGAIN if data cannot be sent temporarily (e.g., queue is full) + * @note The caller is responsible for managing the lock of the shared memory pool + * (lock once before multiple calls to reduce overhead) + */ +int frontend_engine_tx_queue_send(struct SharedMemoryPoolQueue *queue, const void **data_ptr, + size_t data_len, size_t *sent_len); + + + +/** + * @brief Retrieves a message from the shared memory pool queue. + * + * @details This function fetches message data from the specified SharedMemoryPoolQueue instance. + * It returns a pointer to the message buffer if data is available, and outputs the + * operation status and actual message length through output parameters. + * The caller should check the status code stored in @p ret before using the returned buffer + * and message length. + * + * @param[in] queue Pointer to the SharedMemoryPoolQueue instance, cannot be NULL + * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow + * @param[out] ret Pointer to an integer variable that stores the operation status code + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * + * @return Pointer to the uint8_t message data buffer on successful data retrieval; + * NULL if the operation fails, the queue is empty, or a system error occurs + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., invalid queue handle, NULL input pointers), + * the returned pointer is NULL and @p out_len is undefined + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message data is temporarily unavailable (e.g., queue is empty), + * the returned pointer is NULL and @p out_len is undefined + * + * @note The ownership and release mechanism of the returned data buffer depend on the implementation + * of the SharedMemoryPoolQueue module; refer to the module's documentation for memory management rules. + */ +uint8_t *frontend_engine_rx_queue_get_msg(struct SharedMemoryPoolQueue *queue, size_t max_msg_len, + int *ret, size_t *out_len); + +struct FrontendEngOps *get_hs_frontend_engine_ops(); + + +void frontend_engine_init(); + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/frontend_api.h b/projects/sel4test/apps/front/include/frontend_api.h new file mode 100644 index 0000000..1f321ef --- /dev/null +++ b/projects/sel4test/apps/front/include/frontend_api.h @@ -0,0 +1,23 @@ +#ifndef FRONTEND_API_H_ +#define FRONTEND_API_H_ + +#include "engine.h" +#include "session.h" +#include "session_pool.h" +#include "message.h" +#include "frontend_proto.h" +#include "common_utils.h" + + +int frontend_eng_command(struct FrontendEngine_ *eng, GeneralProxyMsgHeader *hdr, uint8_t *data, uint32_t size); + +struct FrontendSession *frontend_sess_new(struct FrontendEngine_ *eng); +int frontend_sess_connect_by_addrstr(struct FrontendSession *sess, int proto, const char *addr_str); +int frontend_sess_connect(struct FrontendSession *sess, struct SessMsgPara *para); +int frontend_sess_close(struct FrontendSession *sess); +int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t size); +int frontend_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t size); +int frontend_sess_bind_callback(struct FrontendSession *sess, SESS_EVENT_CALLBACK event_callback); + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/frontend_proto.h b/projects/sel4test/apps/front/include/frontend_proto.h new file mode 100644 index 0000000..bbaba3a --- /dev/null +++ b/projects/sel4test/apps/front/include/frontend_proto.h @@ -0,0 +1,80 @@ +#ifndef FRONTEND_PROTO_H_ +#define FRONTEND_PROTO_H_ + +#include "message.h" +#include "session.h" +#include "session_pool.h" +#include "common_utils.h" + + +#define PROXY_MSG_TYPE_VALID(x) (((x) == PROXY_MSG_TYPE_DEV) || \ + ((x) == PROXY_MSG_TYPE_STRGY) || \ + ((x) == PROXY_MSG_TYPE_SESS) || \ + ((x) == PROXY_MSG_TYPE_DATA)) + +#define PROXY_MSG_LEN_VALID(x) (((x) >= PROXY_MSG_MIN_SIZE) || \ + ((x) <= PROXY_MSG_MAX_SIZE)) + +#define PROXY_ADMIN_SESSION_ID 0 +#define FRONTEND_ADMIN_SESSION_ID PROXY_ADMIN_SESSION_ID +#define BACKEND_ADMIN_SESSION_ID PROXY_ADMIN_SESSION_ID + +#define PROXY_HANDOVER_SESSION_ID 0xFFFF +#define FRONTEND_HANDOVER_SESSION_ID PROXY_HANDOVER_SESSION_ID +#define BACKEND_HANDOVER_SESSION_ID PROXY_HANDOVER_SESSION_ID + +#define APP_SESSION_ID_VALID(x) (((x) != PROXY_ADMIN_SESSION_ID) || \ + ((x) != PROXY_HANDOVER_SESSION_ID)) + +#define DEV_ID_AUTO_HANDOVER 0xFF + + +int frontend_proxy_msg_process(uint8_t *msg); + +int frontend_proxy_dev_msg_command(uint8_t *msg); +int frontend_proxy_dev_msg_process(uint8_t *msg); +int frontend_proxy_dev_msg_process_ver1(uint16_t msg_type, uint16_t msg_id, uint16_t action_type, uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_dev_msg_process_disable_ver1(uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_dev_msg_process_enable_ver1(uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_dev_msg_process_query_ver1(uint16_t payload_len, uint8_t *msg_payload); + +int frontend_proxy_strgy_msg_process(uint8_t *msg); +int frontend_proxy_strgy_msg_response(uint8_t *msg); +int frontend_proxy_strgy_msg_process_ver1(uint16_t msg_type, uint16_t msg_id, uint16_t action_type, uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_strgy_msg_process_set_ver1(uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_strgy_msg_process_query_ver1(uint16_t payload_len, uint8_t *msg_payload); + + + +int frontend_proxy_sess_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint8_t *msg); +int frontend_proxy_sess_msg_response(uint8_t *msg); +int frontend_proxy_sess_msg_process_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t msg_type, + uint16_t action_type, uint16_t ip_version, uint16_t payload_len, + uint8_t *msg_payload); + + +int frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_sess_msg_process_close_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t payload_len, uint8_t *msg_payload); + + +int __frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload); +int __frontend_proxy_sess_msg_process_close_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t payload_len, uint8_t *msg_payload); + + +int frontend_proxy_sess_msg_process_passive_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload); +int __frontend_proxy_sess_msg_process_passive_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload); + + + +int frontend_proxy_data_msg_prosess(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t data_len, uint8_t *msg); +int frontend_proxy_data_msg_recv(struct FrontendSession *sess, uint8_t *msg); +int frontend_proxy_data_msg_send(struct FrontendSession *sess, uint8_t *msg); + + + +int frontend_proxy_shmem_data_msg_recv(struct FrontendSession *sess, uint8_t **msg); +int frontend_proxy_shmem_data_msg_send(struct FrontendSession *sess, const uint8_t **msg); +int frontend_proxy_sock_data_msg_recv(struct FrontendSession *sess, uint8_t *msg); +int frontend_proxy_sock_data_msg_send(struct FrontendSession *sess, uint8_t *msg); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h new file mode 100644 index 0000000..29c9d43 --- /dev/null +++ b/projects/sel4test/apps/front/include/message.h @@ -0,0 +1,609 @@ +#ifndef MESSAGE_H +#define MESSAGE_H + +#include +#include +#include +#include "common_utils.h" + +#define PROXY_PROTO_VERSION_1 1 +// #define PROXY_MSG_TYPE_DEV 0 +// #define PROXY_MSG_TYPE_STRGY 1 +// #define PROXY_MSG_TYPE_SESS 2 +// #define PROXY_MSG_TYPE_DATA 3 + + +typedef enum { + PROXY_MSG_TYPE_DEV = 0, // Device message + PROXY_MSG_TYPE_STRGY, // Strategy message + PROXY_MSG_TYPE_SESS, // Session message + PROXY_MSG_TYPE_DATA // Data message +} ProxyMsgType; + + +#define PROXY_PROTO_DEV_VERSION_1 1 +#define PROXY_PROTO_STRGY_VERSION_1 1 +#define PROXY_PROTO_SESS_VERSION_1 1 + + + +#define PROXY_MSG_HDR_SIZE 8 +#define PROXY_MSG_MIN_SIZE 1 +#define PROXY_MSG_MAX_SIZE 4088 +// Sum of header size and maximum message size (total maximum size including header, in bytes) +#define PROXY_MSG_HDR_PLUS_MAX_SIZE (PROXY_MSG_HDR_SIZE + PROXY_MSG_MAX_SIZE) +#define PROXY_MSG_INVALID_LEN -1 + +struct IPv4Address { + uint8_t data[4]; +}__attribute__((packed)); + +struct IPv6Address { + uint8_t data[16]; +}; + +union IPAddress { + struct IPv4Address ipv4_addr; + struct IPv6Address ipv6_addr; +}__attribute__((packed)); + + +/* + * Macro: Copy IPv4 address from struct in_addr to custom struct IPv4Address + * Parameters: + * dest - Destination structure pointer (struct IPv4Address*) + * src - Source structure pointer (const struct in_addr*) + * Notes: + * 1. Converts 32-bit network byte order address to 4-byte array in host order + * 2. Includes null pointer check to prevent invalid memory access + */ +#define COPY_IN_TO_IPV4(dest, src) do { \ + if ((dest) != NULL && (src) != NULL) { \ + uint32_t addr = ntohl((src)->s_addr); \ + (dest)->data[0] = (addr >> 24) & 0xFF; \ + (dest)->data[1] = (addr >> 16) & 0xFF; \ + (dest)->data[2] = (addr >> 8) & 0xFF; \ + (dest)->data[3] = addr & 0xFF; \ + } \ +} while (0) + +/* + * Macro: Copy data from custom struct IPv4Address to struct in_addr + * Parameters: + * dest - Destination structure pointer (struct in_addr*) + * src - Source structure pointer (const struct IPv4Address*) + * Notes: + * 1. Combines 4-byte array into 32-bit value in network byte order + * 2. Reverse operation of COPY_IN_TO_IPV4 macro + */ +#define COPY_IPV4_TO_IN(dest, src) do { \ + if ((dest) != NULL && (src) != NULL) { \ + uint32_t addr = ((uint32_t)(src)->data[0] << 24) | \ + ((uint32_t)(src)->data[1] << 16) | \ + ((uint32_t)(src)->data[2] << 8) | \ + (uint32_t)(src)->data[3]; \ + (dest)->s_addr = htonl(addr); \ + } \ +} while (0) + + +/* + * Macro: Copy IPv6 address from struct in6_addr to custom struct IPv6Address + * Parameters: + * dest - Destination structure pointer (struct IPv6Address*) + * src - Source structure pointer (const struct in6_addr*) + * Notes: + * 1. Internally uses memcpy to copy 16 bytes of data (their memory layouts are compatible) + * 2. Includes null pointer check to avoid accessing null pointers + */ +#define COPY_IN6_TO_IPV6(dest, src) do { \ + if ((dest) != NULL && (src) != NULL) { \ + memcpy((dest)->data, (src)->s6_addr, 16); \ + } \ +} while (0) + +/* + * Macro: Copy data from custom struct IPv6Address to struct in6_addr + * Parameters: + * dest - Destination structure pointer (struct in6_addr*) + * src - Source structure pointer (const struct IPv6Address*) + * Notes: + * Reverse copy, functionally symmetric to the above macro + */ +#define COPY_IPV6_TO_IN6(dest, src) do { \ + if ((dest) != NULL && (src) != NULL) { \ + memcpy((dest)->s6_addr, (src)->data, 16); \ + } \ +} while (0) + + +/** + * @brief Proxy message header structure + * + * This structure defines the header format for proxy messages, containing metadata + * such as protocol version, message type, session identifiers, and payload length. + */ +typedef struct { + uint8_t version; // Protocol version. Currently unused, set to 1. + uint8_t proxy_msg_type; // Proxy message type. Possible values: device message (0), strategy message (1), session message (2), data message (3). + uint16_t frontend_sess_id; // Frontend session ID. Used for matching frontend and backend sessions in the frontend proxy. + uint16_t backend_sess_id; // Backend session ID. Used for matching frontend and backend sessions in the backend proxy. + uint16_t payload_len; // Payload length in bytes. Valid range: 1 to 4088. Must not exceed one physical page. +} __attribute__((packed)) ProxyMsgHeader; + + + + +/** + * Calculate the total memory space occupied by the complete message described by ProxyMsgHeader + * Including: size of the header structure itself + length of the payload data + */ +#define PROXY_MSG_TOTAL_SIZE(p_msg_header) \ + (sizeof(ProxyMsgHeader) + (p_msg_header)->payload_len) + + +/** + * @brief Macro to calculate total shared queue memory size with fixed fragment size + * + * For cross-system shared memory usage with strict size regulations, each fragment + * occupies a fixed size regardless of actual payload: + * - Each fragment = sizeof(ProxyMsgHeader) + PROXY_MSG_MAX_SIZE + * - Total memory = number of fragments × fixed fragment size + * + * Number of fragments is calculated using ceiling division to ensure all data is covered. + * + * @param data_size Size of the data payload to be sent (in bytes) + * @return size_t Total shared queue memory required (in bytes) + */ +#define PROXY_MSG_TOTAL_MEM_SIZE(data_size) \ + ( \ + /* Calculate number of fragments (ceiling division) */ \ + ( ((data_size) + PROXY_MSG_MAX_SIZE - 1) / PROXY_MSG_MAX_SIZE ) \ + * (sizeof(ProxyMsgHeader) + PROXY_MSG_MAX_SIZE) /* Fixed size per fragment */ \ + ) + + +typedef enum { + ACTION_TYPE_COMMAND = 0, // Command + ACTION_TYPE_RESPONSE // Response +} ActionType; + + +/** + * @brief Device message header structure + * + * This structure defines the header format for device-related messages, containing metadata such as + * protocol version, message type, message identifier, signaling type, and payload length. + */ +typedef struct { + uint16_t version; // Protocol version. Currently unused; set to 1. + uint16_t msg_type; // Message type. Possible values: Disable (0), Enable (1), Query (2) + uint16_t msg_id; // Message ID. Used to match commands with their corresponding responses. + uint16_t action_type; // Signaling type. Possible values: Command (0), Response (1) + uint16_t payload_len; // Payload length in bytes. +} __attribute__((packed)) DevMsgHeader; + + +typedef enum { + DEV_MSG_DISABLE = 0, // Disable + DEV_MSG_ENABLE, // Enable + DEV_MSG_QUERY // Query +} DevMsgType; + +// Check if the device message type is valid +#define IS_VALID_DEV_MSG_TYPE(dev_msg_type) \ + ((dev_msg_type) == DEV_MSG_DISABLE || \ + (dev_msg_type) == DEV_MSG_ENABLE || \ + (dev_msg_type) == DEV_MSG_QUERY) + + +/** + * @brief Device message mask structure + * + * Used to store the content of the "Enable"/"Disable" commands, specifically representing the mask + * that indicates which devices are selected for enabling or disabling. + */ +typedef struct { + uint16_t data; // Content of the "Enable"/"Disable" commands, representing the mask that indicates which devices are selected to enable or disable. +} __attribute__((packed)) DevMsgMask; + + + +/** + * @brief Device message report structure + * + * This structure contains the response data for the "Query" command, including + * status information, error details, and the active device mask. + */ +typedef struct { + uint8_t status; // Status code indicating the overall result of the operation + uint8_t error; // Error code providing specific details about any errors encountered + uint16_t data; // Response data from the "Query" command, returning the mask indicating which devices are active +} __attribute__((packed)) DevMsgReport; + + +/** + * Calculate the payload length of a device message based on its type and action type. + * + * @param dev_msg_type Device message type (DEV_MSG_ENABLE, DEV_MSG_DISABLE or DEV_MSG_QUERY) + * @param action_type Action type (ACTION_TYPE_COMMAND or ACTION_TYPE_RESPONSE) + * @return Payload length in bytes, or PROXY_MSG_INVALID_LEN if type is invalid + */ +#define DEV_MSG_PAYLOAD_LEN(dev_msg_type, action_type) \ +( \ + /* Check if device message type is valid */ \ + (dev_msg_type == DEV_MSG_ENABLE) ? \ + ( \ + /* Check if action type is valid */ \ + (action_type == ACTION_TYPE_COMMAND) ? 2 : \ + (action_type == ACTION_TYPE_RESPONSE) ? 4 : \ + PROXY_MSG_INVALID_LEN /* Invalid action type */ \ + ) : \ + (dev_msg_type == DEV_MSG_DISABLE) ? \ + ( \ + (action_type == ACTION_TYPE_COMMAND) ? 2 : \ + (action_type == ACTION_TYPE_RESPONSE) ? 2 : \ + PROXY_MSG_INVALID_LEN /* Invalid action type */ \ + ) : \ + (dev_msg_type == DEV_MSG_QUERY) ? \ + ( \ + (action_type == ACTION_TYPE_COMMAND) ? 0 : \ + (action_type == ACTION_TYPE_RESPONSE) ? 4 : \ + PROXY_MSG_INVALID_LEN /* Invalid action type */ \ + ) : \ + PROXY_MSG_INVALID_LEN /* Invalid device message type */ \ +) + + +/* + * Get payload length directly from DevMsgHeader struct + * euses DEV_MSG_PAYLOAD_LEN to avoid duplicate logic + */ +#define DEV_MSG_HEADER_PAYLOAD_LEN(header) \ + DEV_MSG_PAYLOAD_LEN((header)->msg_type, (header)->action_type) + + +/** + * @brief Strategy message header structure + * + * This structure defines the header format for strategy-related messages, containing metadata such as + * protocol version, message type, message identifier, signaling type, and payload length. + */ +typedef struct { + uint16_t version; // Protocol version. Currently unused; set to 1. + uint16_t msg_type; // Message type. Possible values: Set (0), Query (1) + uint16_t msg_id; // Message ID. Used to match commands with their corresponding responses. + uint16_t action_type; // Signaling type. Possible values: Command (0), Response (1) + uint16_t payload_len; // Payload length in bytes. +} __attribute__((packed)) StrgyMsgHeader; + + +typedef enum { + STRGY_MSG_SET = 0, // Set + STRGY_MSG_QUERY // Query +} StrgyMsgType; + + +typedef enum { + STRGY_OP_STATUS_SUCCESS = 0, // Session operation succeeded + STRGY_OP_STATUS_FAIL = 1, // Session operation failed + STRGY_OP_STATUS_NUM = 2 // Total number of enumeration members +} StrgyOpStatus; + + +// Corresponds to the code field in the structure, indicating specific reason codes for strategy operation results +typedef enum { + STRGY_OP_CODE_SUCCESS = 0, // Operation succeeded + STRGY_OP_CODE_PARAMETER_INVALID = 1, // Invalid Parameter + STRGY_OP_CODE_MAX // Total number of enumeration members +} StrgyOpCode; + + +// Check if the strategy message type is valid +#define IS_VALID_STRGY_MSG_TYPE(strgy_msg_type) \ + ((strgy_msg_type) == STRGY_MSG_SET || \ + (strgy_msg_type) == STRGY_MSG_QUERY) + + +/** + * Calculate the payload length of a strategy message based on its type and action type. + * + * @param strgy_msg_type Strategy message type (STRGY_MSG_SET or STRGY_MSG_QUERY) + * @param action_type Action type (ACTION_TYPE_COMMAND or ACTION_TYPE_RESPONSE) + * @return Payload length in bytes, or PROXY_MSG_INVALID_LEN if type is invalid + */ +#define STRGY_MSG_PAYLOAD_LEN(strgy_msg_type, action_type) \ +( \ + /* Check strategy message type first */ \ + (strgy_msg_type == STRGY_MSG_SET) ? \ + ( \ + /* Determine length for SET message based on action type */ \ + (action_type == ACTION_TYPE_COMMAND) ? 2 : /* SET command → 2 bytes */ \ + (action_type == ACTION_TYPE_RESPONSE) ? 2 : /* SET response → 2 bytes */ \ + PROXY_MSG_INVALID_LEN /* Invalid action type for SET message */ \ + ) : \ + (strgy_msg_type == STRGY_MSG_QUERY) ? \ + ( \ + /* Determine length for QUERY message based on action type */ \ + (action_type == ACTION_TYPE_COMMAND) ? 0 : /* QUERY command → 0 bytes */ \ + (action_type == ACTION_TYPE_RESPONSE) ? 4 : /* QUERY response → 4 bytes */ \ + PROXY_MSG_INVALID_LEN /* Invalid action type for QUERY message */ \ + ) : \ + PROXY_MSG_INVALID_LEN /* Invalid strategy message type */ \ +) + + +/* + * Get payload length directly from StrgyMsgHeader struct + * euses STRGY_MSG_PAYLOAD_LEN to avoid duplicate logic + */ +#define STRGY_MSG_HEADER_PAYLOAD_LEN(header) \ + DEV_MSG_PAYLOAD_LEN((header)->msg_type, (header)->action_type) + + +/** + * @brief Strategy command enable message structure + * + * This structure defines the format for strategy command enable messages, specifically containing + * parameters required when enabling a specified strategy. It is used to传递 configuration details + * for strategy activation. + */ +typedef struct { + uint16_t strgy_para; // Strategy parameter. Possible values: 0 (Round Robin), 1 (Select device with highest current available bandwidth), 2 (Select device with lowest current latency) +} __attribute__((packed)) StrgyCMDEnableMessage; + + +/** + * @brief Strategy message report structure + * + * This structure contains the response data for strategy-related "Query" commands, including + * a status code, error code, and the active strategy information returned by the query. + */ +typedef struct { + uint8_t status; // Status code indicating the overall result of the strategy operation (e.g., success or failure) + uint8_t error; // Error code providing specific details if the strategy operation encountered an error (0 for no error) + uint16_t data[]; // Flexible array member acting as a placeholder. Stores the response data from the "Query" command, specifically the active strategy code. +} __attribute__((packed)) StrgyMsgReport; + + +/** + * @brief Session message header structure + * + * This structure defines the header format for session-related messages, containing metadata such as + * protocol version, message type, signaling type, IP version, and payload length. It is used for + * managing session operations like creation and closure. + */ +typedef struct { + uint16_t version; // Protocol version. Currently unused; set to 1. + uint16_t msg_type; // Message type. Possible values: Create (0), Close (1) + uint16_t action_type; // Signaling type. Possible values: Command (0), Response (1) + uint16_t ip_version; // IP version. Possible values: SESS_IPV4_PROTO (4), SESS_IPV6_PROTO (6) + uint16_t payload_len; // Payload length in bytes. +} __attribute__((packed)) SessMsgHeader; + + + +/** + * @brief IPv4 session parameter structure + * + * This structure contains parameters required for establishing or managing an IPv4-based session, + * including device identification, transport protocol, IPv4 address, and corresponding port information. + */ +typedef struct{ + uint16_t dev_id; // Device identifier, uniquely identifies the target device in the session + uint16_t trans_proto; // Transport protocol used for the session (e.g., TCP, UDP) + struct IPv4Address ipv4_addr; // IPv4 address structure containing the device's IPv4 address information + uint16_t port; // Port number associated with the IPv4 address for the session +} __attribute__((packed)) SessParaIPv4; + + + +/** + * @brief IPv6 session parameter structure + * + * This structure contains parameters required for establishing or managing an IPv6-based session, + * including device identification, transport protocol, IPv6 address, and corresponding port information. + */ +typedef struct{ + uint16_t dev_id; // Device identifier, uniquely identifies the target device in the session + uint16_t trans_proto; // Transport protocol used for the session (e.g., TCP, UDP) + struct IPv6Address ipv6_addr; // IPv6 address structure containing the device's IPv6 address information + uint16_t port; // Port number associated with the IPv6 address for the session +} __attribute__((packed)) SessParaIPv6; + + +typedef enum { + SESS_MSG_CLOSE = 0, // Close + SESS_MSG_CREATE // Create +} SessMsgType; + + +typedef enum { + SESS_NON_IP_PROTO = 0, // None-IP protocol + SESS_IPV4_PROTO = 4, // IPv4 + SESS_IPV6_PROTO = 6 // IPv6 +} SessIpProtoVersion; + + +typedef enum { + SESS_UDP_PROTO = 0, // UDP + SESS_TCP_PROTO = 1, // TCP + SESS_FASTPATH_PROTO = 2 // XDP or eBPF +} SessTranProto; + +// Check if the session message type is valid +#define IS_VALID_SESS_MSG_TYPE(sess_msg_type) \ + ((sess_msg_type) == SESS_MSG_CREATE || \ + (sess_msg_type) == SESS_MSG_CLOSE) + + +// Check if the IP protocol verion is valid +#define IS_VALID_SESS_IP_VERSION(ip_version) \ + ((ip_version) == SESS_IPV4_PROTO || \ + (ip_version) == SESS_IPV6_PROTO) + + + +/** + * Calculate the payload length of a session message based on its type, action type, and IP protocol version. + * + * @param sess_msg_type Session message type (SESS_MSG_CREATE or SESS_MSG_CLOSE) + * @param action_type Action type (ACTION_TYPE_COMMAND or ACTION_TYPE_RESPONSE) + * @param ip_version IP protocol version (SESS_IPV4_PROTO or SESS_IPV6_PROTO) + * @return Payload length in bytes, or PROXY_MSG_INVALID_LEN if type/version is invalid + */ +#define SESS_MSG_PAYLOAD_LEN(sess_msg_type, action_type, ip_version) \ +( \ + /* Check session message type first */ \ + (sess_msg_type == SESS_MSG_CLOSE) ? \ + ( \ + utils_print("type is SESS_MSG_CLOSE\n"),\ + /* Determine length for CLOSE message based on action type */ \ + (action_type == ACTION_TYPE_COMMAND) ? 0 : /* CLOSE command → 0 bytes */ \ + (action_type == ACTION_TYPE_RESPONSE) ? 2 : /* CLOSE response → 2 bytes (status + error) */ \ + PROXY_MSG_INVALID_LEN /* Invalid action type for CLOSE message */ \ + ) : \ + (sess_msg_type == SESS_MSG_CREATE) ? \ + ( \ + utils_print("type is SESS_MSG_CREATE, action type is %d, ip_version is %d\n",action_type, ip_version),\ + /* Determine length for CREATE message based on action type */ \ + (action_type == ACTION_TYPE_RESPONSE) ? 2 : /* CREATE response → 2 bytes (status + error) */ \ + (action_type == ACTION_TYPE_COMMAND) ? \ + ( \ + /* Determine length for CREATE command based on IP version */ \ + (ip_version == SESS_IPV4_PROTO) ? 10 : /* IPv4 → 10 bytes (session params) */ \ + (ip_version == SESS_IPV6_PROTO) ? 22 : /* IPv6 → 22 bytes (session params) */ \ + PROXY_MSG_INVALID_LEN /* Invalid IP version for CREATE command */ \ + ) : \ + PROXY_MSG_INVALID_LEN /* Invalid action type for CREATE message */ \ + ) : \ + PROXY_MSG_INVALID_LEN /* Invalid session message type */ \ +) + +/* + * Get payload length directly from StrgySessHeader struct + * euses STRGY_MSG_PAYLOAD_LEN to avoid duplicate logic + */ +#define SESS_MSG_HEADER_PAYLOAD_LEN(header) \ + SESS_MSG_PAYLOAD_LEN((header)->msg_type, (header)->action_type, (header)->ip_version) + +typedef struct { + struct IPv4Address ipv4_addr; // IPv4 address + uint16_t port; // Transport layer port; +} __attribute__((packed)) IPv4PortTuple; + +typedef struct { + struct IPv6Address ipv6_addr; // IPv6 address + uint16_t port; // Transport layer port +} __attribute__((packed)) IPv6PortTuple; + +typedef union { + IPv4PortTuple ipv4_port_tuple; + IPv6PortTuple ipv6_port_tuple; +}IPPortTuple; + +/* + * The structure of the session create-response message's payload. + */ +// Corresponds to the status field in the structure, indicating the overall status of the session operation (success/failure) +typedef enum { + SESS_OP_STATUS_SUCCESS = 0, // Session operation succeeded + SESS_OP_STATUS_FAIL = 1, // Session operation failed + SESS_OP_STATUS_NUM = 2 // Total number of enumeration members +} SessOpStatus; + + +// Corresponds to the code field in the structure, indicating specific reason codes for operation results +typedef enum { + SESS_OP_CODE_SUCCESS = 0, // Operation succeeded + SESS_OP_CODE_NO_PERMISSION = 1, // No permission to perform the operation + SESS_OP_CODE_DEVICE_ERROR = 2, // Device error occurred + SESS_OP_CODE_RESOURCE_INSUFFICIENT = 3, // Insufficient resources + SESS_OP_CODE_NETWORK_UNREACHABLE = 4, // Network is unreachable + SESS_OP_CODE_PARAMETER_INVALID = 5, // Invalid Parameter + SESS_OP_CODE_MAX // Total number of enumeration members +} SessOpCode; + + +// Structure for session operation response data, containing status and specific reason code +typedef struct SessOpRespData_ { + uint8_t status; // Corresponding to SessOpStatus enumeration (overall operation status) + uint8_t code; // Corresponding to SessOpCode enumeration (specific reason code for operation result) +} __attribute__((packed)) SessOpRespData; + +/* + * Session message parameter structure, used to describe core parameters related to session establishment, + * including device identification, transport protocol type, and IP-port combination and other key information. + */ +struct SessMsgPara{ +// Frontend session ID, used to deliver information for establishing a new session. + uint16_t frontend_sess_id; +// Backend session ID, used to deliver information for establishing a new session. + uint16_t backend_sess_id; +// Device ID, of type uint16_t, with value range 0x0000-0xFFFF; when set to 0xFFFF, it indicates entering vertical handover mode. + uint16_t dev_id; +// IP version, of type uint16_t, of type uint16_t, supported values include: 4 (IPv4), 6 (IPv6). + uint16_t ip_version; +// Transport layer protocol type, of type uint16_t, supported values include: SESS_UDP_PROTO (0, UDP protocol), SESS_TCP_PROTO(1, TCP protocol), SESS_FASTPATH_PROTO(2, FastPath protocol). + uint16_t trans_proto; +// Tuple of IP address and port number, used to describe the combination of IP address and corresponding port number of the communication endpoint. + IPPortTuple ip_port_tuple; +}; + + +/** + * @brief Session parameters structure for IPv4-based sessions + * @details Contains parameters required to establish and manage an IPv4 session, + * including transport protocol, device selection, and destination endpoint. + * Uses packed alignment to ensure contiguous memory layout. + */ +typedef struct { +// uint16_t transport_layer_proto; /**< Transport layer protocol (2 bytes, e.g., SESS_TCP_PROTO for TCP, SESS_UDP_PROTO for UDP) */ + uint16_t device_selection; /**< Device selection identifier (2 bytes) */ + uint16_t transport_layer_proto; /**< Transport layer protocol (2 bytes, e.g., SESS_TCP_PROTO for TCP, SESS_UDP_PROTO for UDP) */ + IPv4PortTuple dest_endpoint; /**< Destination endpoint containing IPv4 address and port */ +} __attribute__((packed)) SessIPv4Params; + + + +/** + * @brief Session parameters structure for IPv6-based sessions + * @details Contains parameters required to establish and manage an IPv6 session, + * including transport protocol, device selection, and destination endpoint. + * Uses packed alignment to ensure contiguous memory layout. + */ +typedef struct { +// uint16_t transport_layer_proto; /**< Transport layer protocol (2 bytes, e.g., 6 for TCP, 17 for UDP) */ + uint16_t device_selection; /**< Device selection identifier (2 bytes) */ + uint16_t transport_layer_proto; /**< Transport layer protocol (2 bytes, e.g., 6 for TCP, 17 for UDP) */ + IPv6PortTuple dest_endpoint; /**< Destination endpoint containing IPv6 address and port */ +} __attribute__((packed)) SessIPv6Params; + + +typedef struct{ + ProxyMsgHeader outer_header; +// ProxyMsgType msg_type; + union { // Nested union to reduce memory usage (avoids redundant space) + DevMsgHeader dev_hdr; // Device message header member + StrgyMsgHeader strgy_hdr; // Strategy message header member + SessMsgHeader sess_hdr; // Session message header member + } inner_header; // Nested union alias for easy access to specific headers +} GeneralProxyMsgHeader; + +struct FrontendEngine_; +struct SharedMemoryPoolQueue; + +typedef enum { + MEMORY_ALLOC_SHARED, // Allocate in shared memory + MEMORY_ALLOC_CALLER // Allocated by caller +} MemoryAllocMode; + +int build_proxy_general_message(struct FrontendEngine_ *engine, GeneralProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, + uint8_t **result_msg, MemoryAllocMode alloc_mode, struct SharedMemoryPoolQueue *ring_buf); +int build_proxy_dev_message(DevMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); +int build_proxy_strgy_message(StrgyMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); +int build_proxy_sess_message(SessMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); +int build_proxy_data_message(ProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/queue.h b/projects/sel4test/apps/front/include/queue.h new file mode 100644 index 0000000..58e667e --- /dev/null +++ b/projects/sel4test/apps/front/include/queue.h @@ -0,0 +1,596 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +/* + * Singly-linked List access methods. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == (void *)(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == (void *)(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) + +#ifndef TAILQ_END +#define TAILQ_END(head) NULL // Standard definition: end of queue is NULL +#endif + +// Manually define TAILQ_FOREACH_SAFE macro (only use if the system header doesn't support it) +#ifndef TAILQ_FOREACH_SAFE + +#if 0 +#define TAILQ_FOREACH_SAFE(var, head, field, next_var) \ + for ((var) = TAILQ_FIRST((head)), \ + (next_var) = TAILQ_NEXT((var), field); \ + (var) != TAILQ_END((head)); \ + (var) = (next_var), (next_var) = TAILQ_NEXT((var), field)) +#endif + +#define TAILQ_FOREACH_SAFE(var, head, field, next_var) \ + for (var = TAILQ_FIRST(head); \ + var != TAILQ_END(head) && (next_var = TAILQ_NEXT(var, field), 1); \ + var = next_var) + +#endif + +#endif /* sys/queue.h */ diff --git a/projects/sel4test/apps/front/include/senario_test.h b/projects/sel4test/apps/front/include/senario_test.h new file mode 100644 index 0000000..a64418d --- /dev/null +++ b/projects/sel4test/apps/front/include/senario_test.h @@ -0,0 +1,34 @@ +#ifndef SENARIO_TEST_H_ +#define SENARIO_TEST_H_ + +#include "engine.h" +#include "frontend_proto.h" +#include "message.h" +#include "shared_mem_io.h" + + +int scenario_msg_inject_frontend(FrontendEngine *engine, + GeneralProxyMsgHeader *msg_header, + const uint8_t *msg_payload, + size_t msg_payload_len, + MemoryAllocMode alloc_mode, + uint8_t **result_msg, + char *result_desc, + size_t desc_len); + + +int test_proxy_scenario_multi_type_msg_build_frontend(FrontendEngine *engine); + + + +int device_msg_inject_frontend(FrontendEngine *engine); + +int strategy_msg_inject_frontend(FrontendEngine *engine); + +int session_msg_inject_frontend(FrontendEngine *engine); + +int data_msg_inject_frontend(FrontendEngine *engine); + +int test_proxy_scenario_msg_read_from_rx_queue_frontend(FrontendEngine *engine); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h new file mode 100644 index 0000000..47f9394 --- /dev/null +++ b/projects/sel4test/apps/front/include/session.h @@ -0,0 +1,343 @@ +#ifndef SESSION_H +#define SESSION_H + +#include +#include +#include "uthash.h" +// #include "shared_mem_io.h" + +// Frontend proxy processing result: Success (data read/written normally, logic executed completely) +#define FRONTEND_PROXY_PROCESS_OK 0 + +// Frontend proxy processing result: Failure (system-level error, such as memory allocation failure, invalid handle, protocol parsing error, etc. Requires error investigation) +#define FRONTEND_PROXY_PROCESS_ERROR -1 + +// Frontend proxy processing result: Process temporarily unavailable (non-error state, only data read/write cannot be completed, such as empty queue, data not ready, resource temporarily occupied, etc. Retry is allowed) +#define FRONTEND_PROXY_PROCESS_AGAIN 1 + + +/** +/** + * @brief Frontend session related event types + * + * This enumeration defines all event types involved in the lifecycle, data interaction, + * and exception handling of the frontend session (FrontendSession). Each event corresponds + * to a specific stage or state change of the session. + */ +typedef enum { + FRONTEND_SESS_EVENT_CONN, /**< Frontend session connection established event (triggered when the session successfully connects to the peer/backend) */ + FRONTEND_SESS_EVENT_RECVDATA, /**< Frontend session data received event (triggered when the session receives data from the peer/backend, and the data is ready for processing) */ + FRONTEND_SESS_EVENT_CLOSE, /**< Frontend session close event (triggered when the session needs to be closed actively or passively, e.g., after receiving close command or peer disconnect) */ + FRONTEND_SESS_EVENT_TIMEOUT, /**< Frontend session timeout event (triggered when the session has no data interaction or response within the predefined timeout period) */ + FRONTEND_SESS_EVENT_ABNOMAL, /**< Frontend session abnormal event (triggered when unexpected exceptions occur during session operation, such as resource allocation failure, protocol parsing error, or unexpected connection interruption) */ + FRONTEND_SESS_EVENT_MAX /**< Total number of frontend session event types (for boundary checking and iteration, not a valid event type) */ +} FrontendSessionEvent; + + + +struct ControlMsg{ + uint16_t dev_id; +}; + +/** + * @brief Memory source type of the data field in message segment (SessMsgSeg) + * + * Used to identify whether the memory pointed to by the data pointer in the SessMsgSeg structure + * is dynamically allocated or comes from shared memory (to determine subsequent memory management + * approaches, such as whether manual release is required) + */ +typedef enum { + SESS_MSG_SEG_DYNAMIC_ALLOC, ///< data points to dynamically allocated memory (needs to be freed with free()) + SESS_MSG_SEG_SHARED_MEM ///< data points to shared memory (no manual release needed; managed by the shared memory manager) +} SessMsgSegType; + + +/** + * @brief Structure representing a segment of session message data, supporting both dynamic and shared memory + * This structure encapsulates a segment of message data for session communication, including + * metadata about the data buffer and the buffer itself. It can manage data in two modes: + * dynamically allocated memory or shared memory (via a shared memory pool). + */ +struct SessMsgSeg { +/*< Length of the data buffer (in bytes). Indicates the valid data size in the buffer pointed to by @ref data. */ + uint16_t len; +/* + * < Memory source type of the data buffer, corresponding to SessMsgSegType. + * Valid values: SESS_MSG_SEG_DYNAMIC_ALLOC (dynamic memory) or SESS_MSG_SEG_SHARED_MEM (shared memory). + */ + uint16_t type; +/* + * < Pointer to the associated shared memory pool. + * Valid and non-NULL only when @ref type is SESS_MSG_SEG_SHARED_MEM, used for managing shared memory ownership and validation. + * NULL when @ref type is SESS_MSG_SEG_DYNAMIC_ALLOC (no shared memory pool associated). + */ + struct SharedMemoryPool *mem_pool; +/*< Pointer to the actual data buffer. + * For SESS_MSG_SEG_DYNAMIC_ALLOC: Points to memory allocated via malloc(). + * For SESS_MSG_SEG_SHARED_MEM: Points to a valid data segment within the shared memory managed by @ref mem_pool. + */ + uint8_t *data; +/* + * < TAILQ queue entry. Used to link multiple SessMsgSeg structures into a doubly linked list for sequential access. + */ + TAILQ_ENTRY(SessMsgSeg) entry; +}; + + +TAILQ_HEAD(SessMsgQueue, SessMsgSeg); + + + +struct SessMsgSeg *sess_msg_seg_alloc(size_t len, SessMsgSegType type, uint8_t *shared_data, struct SharedMemoryPool *mem_pool); +struct SessMsgSeg* sess_msg_seg_alloc_lite(SessMsgSegType type); +void sess_msg_seg_free(struct SessMsgSeg **seg_ptr); + +void sess_msg_queue_free_all(struct SessMsgQueue *queue); + +struct FrontendProtocolProcess; +struct FrontendEngine_; +struct FrontendSession; + +#define FRONTEND_SESS_LINKED_TO_QUEUE 1 +typedef void (*SESS_CALLBACK)(struct FrontendSession *sess, uint8_t *data, int len); +typedef void (*SESS_EVENT_CALLBACK)(struct FrontendSession *sess, FrontendSessionEvent event); + + + +/** + * @brief Frontend session lifecycle state enumeration + * + * Defines all possible lifecycle states of a frontend session, describing the connection status + * between the frontend and backend during the session's lifetime. + */ +typedef enum { + FRONTEND_SESS_INITIALIZE, ///< Initial state: Newly created frontend session (no connection attempt yet) + FRONTEND_SESS_CONNECTING, ///< Connecting state: Attempting to establish a connection with the backend + FRONTEND_SESS_CONNECTED, ///< Connected state: Successfully established connection with the backend session + FRONTEND_SESS_CLOSED ///< Closed state: Session has been terminated (final state) +} FrontendSessState; + + +struct FrontendSession { + int sess_type; +/* + * Session overall state machine, identifying the lifecycle phase of the frontend session. + * Supported states: + * 1. FRONTEND_SESS_INITIALIZE: Newly created frontend session (initial state) + * 2. FRONTEND_SESS_CONNECTING: Attempting to establish a connection with the backend + * 3. FRONTEND_SESS_CONNECTED: Connection with the backend session has been successfully established + * 4. FRONTEND_SESS_CLOSED: Session has been closed (terminated state) + */ + FrontendSessState sess_state; + int ip_version; + uint16_t frontend_sess_id; + uint16_t backend_sess_id; // hash key +/* + * State machine states, indicating the linked status of the session in different directions + * state_f2b: When its value is FRONTEND_SESS_LINKED_TO_QUEUE, it means the entries_f2b node of the current session + * is linked to the queue_f2b (belonging to FrontendSessionPool in FrontendEngine_) + */ + uint8_t state_f2b; + uint8_t state_b2f; + +// Message queues + struct SessMsgQueue msg_f2b; // front-end to back-end message queue + struct SessMsgQueue msg_b2f; // back-end to front-end message queue +// Queue link nodes + TAILQ_ENTRY(FrontendSession) entries_f2b; // front-end to back-end active queue node + TAILQ_ENTRY(FrontendSession) entries_b2f; // back-end to front-end active queue node +// Protocol processing + struct FrontendProtocolProcess *protocol_process; // protocol processing module pointer +// Pointer to the Frontend engine associated with this session + struct FrontendEngine_ *eng; +// Private data pointer (used to store session-specific data) + void *pri_data; + SESS_CALLBACK data_process_callback; + SESS_EVENT_CALLBACK event_callback; + UT_hash_handle hh; +}; + + +/** + * @brief Get the block size of the front-end session's queue in the specified direction (with null-pointer safe check, branchless concatenation implementation) + * + * Dynamically select the queue member using identifier concatenation without conditional judgment; + * perform step-by-step null-pointer checks along the pointer chain to avoid illegal access. + * Pointer chain: sess -> eng -> [tx_queue/rx_queue] -> block_size (target queue member generated by concatenating dir and _queue via ##) + * + * @param sess Pointer to the front-end session instance (type: struct FrontendSession *), which can be NULL + * @param dir Queue direction identifier, only two valid values are supported: tx (corresponds to tx_queue) and rx (corresponds to rx_queue) + * + * @return uint16_t Block size of the queue (block_size): + * - Non-0: All dependent pointers are valid, returns the actual configured block size (unit: bytes) + * - 0: Any dependent pointer is NULL (sess/eng/target direction queue) or no corresponding member after dir concatenation (may report an error in advance during compilation) + * + * @note 1. Branchless design: Directly generate tx_queue/rx_queue member names by concatenating dir and _queue via ##, + * completely replacing the original conditional judgment with higher execution efficiency and simpler syntax; + * 2. Compile-time validity check: If an invalid value other than tx/rx is passed to dir (e.g., txx), + * concatenation will generate a non-existent member like txx_queue, which directly reports an error during compilation, + * avoiding issues earlier than runtime judgment; + * 3. Null-pointer safety retained: Maintains the original three-level pointer check (sess→eng→queue), + * and directly returns 0 when any link is NULL through the && short-circuit feature, without sacrificing null-pointer safety; + * 4. Type compatibility: The return type is consistent with block_size (uint16_t), and 0U is an unsigned integer 0, + * compatible with numerical comparison scenarios; + * 5. Concatenation safety: All parameters and member accesses in the macro are enclosed in parentheses to avoid + * syntax errors caused by operator precedence conflicts. + */ +#define SESS_GET_QUEUE_BLOCK_SIZE(sess, dir) \ +( \ + (sess) != NULL && \ + (sess)->eng != NULL && \ + (sess)->eng->dir##_queue != NULL \ + ? (sess)->eng->dir##_queue->block_size \ + : 0U \ +) + + +/** + * @brief Link Frontend session to the specified queue (only if not linked yet) and set state bit + * + * @param[in] sess Pointer to struct FrontendSession (target session) + * @param[in] dir Direction identifier ("f2b" for front2back, "b2f" for back2front) + * + * @details 1. Validate critical pointers (sess/eng/sess_pool) + * 2. Check if FRONTEND_SESS_LINKED_TO_QUEUE bit is NOT set in state_ + * 3. If not set: set the bit (bitwise OR) + insert entries_ into queue_ + * + * @note - Avoids duplicate linking (prevents inserting the same node into TAILQ multiple times) + * - FRONTEND_SESS_LINKED_TO_QUEUE must be a single-bit mask (e.g., 1U<<0) + */ +#define FRONTEND_SESS_LINK_TO_QUEUE(sess, dir) do { \ + /* Step 1: Validate pointers to avoid null dereference */ \ + if ((sess) != NULL && (sess)->eng != NULL && (sess)->eng->sess_pool != NULL) { \ + /* Step 2: Check if NOT linked yet (target bit is 0) */ \ + if (((sess)->state_##dir & FRONTEND_SESS_LINKED_TO_QUEUE) == 0) { \ + /* Step 3: Set linked bit + insert into queue */ \ + (sess)->state_##dir |= FRONTEND_SESS_LINKED_TO_QUEUE; \ + TAILQ_INSERT_TAIL(&(sess)->eng->sess_pool->queue_##dir, (sess), entries_##dir); \ + } \ + } \ +} while (0) + + + +/** + * @brief Unlink Frontend session from the specified queue (only if linked) and clear state bit + * + * @param[in] sess Pointer to struct FrontendSession (target session) + * @param[in] dir Direction identifier ("f2b" for front2back, "b2f" for back2front) + * + * @details 1. Validate critical pointers (sess/eng/session_pool) + * 2. Check if FRONTEND_SESS_LINKED_TO_QUEUE bit is set in state_ + * 3. If set: remove entries_ from queue_ + clear the bit (bitwise AND NOT) + * + * @note - Avoids invalid unlinking (prevents removing a node not in TAILQ) + * - Ensure queue_ is initialized before calling + */ +#define FRONTEND_SESS_UNLINK_FROM_QUEUE(sess, dir) do { \ + /* Step 1: Validate pointers to avoid null dereference */ \ + if ((sess) != NULL && (sess)->eng != NULL && (sess)->eng->sess_pool != NULL) { \ + /* Step 2: Check if already linked (target bit is 1) */ \ + if (((sess)->state_##dir & FRONTEND_SESS_LINKED_TO_QUEUE) != 0) { \ + /* Step 3: Remove from queue + clear linked bit */ \ + TAILQ_REMOVE(&(sess)->eng->sess_pool->queue_##dir, (sess), entries_##dir); \ + (sess)->state_##dir &= ~FRONTEND_SESS_LINKED_TO_QUEUE; \ + } \ + } \ +} while (0) + + +/** + * @brief Get the associated shared memory pool (mem_pool) from a FrontendSession pointer + * This macro retrieves the shared_memory_pool pointer contained in FrontendEngine_ + * through the eng member (pointing to FrontendEngine_) of the FrontendSession structure. + * @param sess Pointer to a struct FrontendSession + * @return Pointer to struct SharedMemoryPool, i.e., sess->eng->mem_pool + */ +#define FRONTEND_SESSION_MEM_POOL(sess) ((sess)->eng->mem_pool) + + +/** + * @brief Insert a SessMsgSeg pointer into the specified direction queue of FrontendSession + * + * @param[in] sess Pointer to struct FrontendSession (the session containing the target queue) + * @param[in] seg Pointer to struct SessMsgSeg (the message segment to be inserted) + * @param[in] dir Direction identifier, must be "f2b" (front2back) or "b2f" (back2front) + * + * @details 1. Validate that sess and seg are non-NULL to avoid null dereference + * 2. Insert seg into sess->msg_ queue using TAILQ_INSERT_TAIL (FIFO order) + * 3. The queue is identified by concatenating "msg_" with dir (msg_f2b or msg_b2f) + * + * @note - must be "f2b" or "b2f"; invalid values will cause compilation errors + * - Ensure sess->msg_ has been initialized with TAILQ_INIT() before insertion + * - seg must point to a valid SessMsgSeg instance (allocated and initialized) + * - This macro performs a tail insertion to maintain FIFO order of messages + */ +#define SESS_MSG_SEG_INSERT_QUEUE(sess, seg, dir) do { \ + /* Validate critical pointers */ \ + if ((sess) != NULL && (seg) != NULL) { \ + /* Insert the segment into the target queue (msg_f2b or msg_b2f) */ \ + TAILQ_INSERT_TAIL(&(sess)->msg_##dir, (seg), entry); \ + } \ +} while (0) + + +/** + * @brief Remove and return the first SessMsgSeg pointer from the specified direction queue of FrontendSession + * + * @param[in] sess Pointer to struct FrontendSession (the session containing the target queue) + * @param[out] seg_ptr Double pointer to struct SessMsgSeg (output: receives the removed segment; set to NULL if queue is empty) + * @param[in] dir Direction identifier, must be "f2b" (front2back) or "b2f" (back2front) + * + * @details 1. Validate that sess and seg_ptr are non-NULL to avoid null dereference + * 2. Check if the target queue (msg_) is non-empty using TAILQ_FIRST + * 3. If non-empty: remove the first element with TAILQ_REMOVE and assign to *seg_ptr + * 4. If empty: set *seg_ptr to NULL + * + * @note - must be "f2b" or "b2f"; invalid values will cause compilation errors + * - Ensure sess->msg_ has been initialized with TAILQ_INIT() before removal + * - seg_ptr must be a valid double pointer (points to a struct SessMsgSeg* variable) + * - The removed segment's memory is not freed by this macro (caller must handle via sess_msg_seg_free) + */ +#define SESS_MSG_SEG_REMOVE_HEAD(sess, seg_ptr, dir) do { \ + /* Validate critical pointers */ \ + if ((sess) != NULL && (seg_ptr) != NULL) { \ + /* Initialize output to NULL (handles empty queue case) */ \ + *(seg_ptr) = NULL; \ + /* Check if queue is non-empty */ \ + if (TAILQ_FIRST(&(sess)->msg_##dir) != NULL) { \ + /* Get the first element and remove it from the queue */ \ + *(seg_ptr) = TAILQ_FIRST(&(sess)->msg_##dir); \ + TAILQ_REMOVE(&(sess)->msg_##dir, *(seg_ptr), entry); \ + } \ + } \ +} while (0) + +struct FrontendProtocolProcess { + + int (*connect)(struct FrontendSession* sess); + + int (*accept)(struct FrontendSession* sess); + + int (*read)(struct FrontendSession* sess, uint8_t* data, uint32_t size); + + int (*write)(struct FrontendSession* sess, const uint8_t* data, uint32_t size); + + int (*close)(struct FrontendSession* sess); + +}; + +TAILQ_HEAD(FrontendSessionQueue, FrontendSession); + +struct FrontendSessionID { + uint16_t id; + TAILQ_ENTRY(FrontendSessionID) entry; +}; +TAILQ_HEAD(FrontendSessionIDQueue, FrontendSessionID); + + +void default_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event); +int session_send(struct FrontendSession* sess, const uint8_t* data, uint32_t size); +int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/session_pool.h b/projects/sel4test/apps/front/include/session_pool.h new file mode 100644 index 0000000..7cc53c4 --- /dev/null +++ b/projects/sel4test/apps/front/include/session_pool.h @@ -0,0 +1,63 @@ +#ifndef SESSION_POOL_H +#define SESSION_POOL_H + +#include +#include + +#include "message.h" +#include "session.h" + +struct FrontendEngine_; + +struct FrontendSessionPoolOps; + +struct FrontendSessionPool { + char *pool_name; // Name/identifier of the session pool + int sess_num; // Current number of sessions in the pool + int capacity; // Maximum number of sessions the pool can hold (total capacity) + struct FrontendEngine_ *engine; // Pointer to the engine this pool belongs to + struct FrontendSessionPoolOps *ops; // Set of operation functions for the pool (e.g., create, delete) + struct FrontendSessionQueue queue_f2b; // Queue containing active sessions + struct FrontendSessionQueue queue_b2f; // Queue for backend-to-frontend session communication/mapping + struct FrontendSessionIDQueue id_queue; // Queue holding available/reusable session IDs + struct FrontendSession *htable; // Hash table storing sessions (for efficient lookup by ID) +}; + + + + +struct FrontendSessionPoolOps { + int (*create_sess_step1)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para); + int (*create_sess_step2)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, uint16_t sess_id, SessOpRespData *resp); + int (*create_sess_passive)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para); + int (*insert_sess)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); + struct FrontendSession* (*search_sess)(struct FrontendSessionPool *s_pool, uint16_t id); + int (*delete_sess)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); + int (*data_process)(struct FrontendSession *sess); + int (*data_process_b2f)(struct FrontendSession *sess); + int (*data_process_f2b)(struct FrontendSession *sess); + int (*close_sess_step1)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); + int (*close_sess_step2)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, SessOpRespData *resp); + void (*destroy_pool)(struct FrontendSessionPool *s_pool); +}; + +extern struct FrontendSessionPool *front_high_speed_pool; +int frontend_high_speed_init_pool(struct FrontendSessionPool *pool); + +struct FrontendSessionPool *frontend_get_high_speed_pool(); + +//helper func +uint16_t allocate_id(struct FrontendSessionIDQueue *id_q); +void release_id(struct FrontendSessionIDQueue *id_q, uint16_t id); +void print_pool(struct FrontendSessionPool *s_pool); +void high_speed_delete_all_sess(struct FrontendSessionPool *s_pool); +void fill_id_queue(struct FrontendSessionIDQueue *id_q); +void inc_sess_num(struct FrontendSessionPool *pool); +void dec_sess_num(struct FrontendSessionPool *pool); + + +int frontend_high_speed_data_process_b2f(struct FrontendSession *sess); +int frontend_high_speed_data_process_f2b(struct FrontendSession *sess); + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/shared_mem_io.h b/projects/sel4test/apps/front/include/shared_mem_io.h new file mode 100644 index 0000000..256d981 --- /dev/null +++ b/projects/sel4test/apps/front/include/shared_mem_io.h @@ -0,0 +1,567 @@ +#ifndef SHARED_MEM_IO_H +#define SHARED_MEM_IO_H + + +#include +#include +#include +#include +#include +#include + +#include "session.h" + +#define ERROR_SHARED_MEM_ADDR UINT64_MAX + +#define MAX_MAP_TABLE_ENTRY_COUNT 64 + +#define HSNET_RX_PHY_ADDR_BASE 0xA000 +#define HSNET_MEM_BLOCK_SIZE 4096 +#define HSNET_RX_MEM_BLOCK_COUNT MAX_MAP_TABLE_ENTRY_COUNT + +#define HSNET_TX_PHY_ADDR_BASE HSNET_RX_PHY_ADDR_BASE + HSNET_MEM_BLOCK_SIZE * MAX_MAP_TABLE_ENTRY_COUNT + +struct SharedMemoryPoolLock{ + int value; // Just for placehoder. We will redefine this struct after receiving the partner's document. +}; + +struct SharedMemoryPool{ + int value; // Just for placehoder. We will redefine this struct after receiving the partner's document. + struct SharedMemoryPoolLock lock; +}; + +struct DequeNode{ + int value; // // Just for placehoder. We will redefine this struct after receiving the partner's document. + struct DequeNode *prev; + struct DequeNode *next; +}; + +/** + * Enumeration of shared memory mapping modes, describing the continuity relationship between physical and logical addresses + */ +typedef enum { + SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH, // Physical addresses are contiguous, logical addresses are contiguous + SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL // Physical addresses are contiguous, logical addresses are discrete +} ShareMemMapMode; + + + +/** + * When the map mode is SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL, the shared memory queue maintains a mapping table. + * This table contains a set of MapTableEntries to establish the mapping relationship between physical addresses and virtual addresses. + * + * Mapping table entry: Describes a single mapping relationship between a virtual address and a physical address. + */ +typedef struct { + uint64_t virt_addr; /* Virtual address */ + uint64_t phy_addr; /* Physical address */ +} MapTableEntry; + + + + + +/** + * @brief FIFO queue (ring buffer implementation) based on SharedMemoryPool + * A high-efficiency first-in-first-out (FIFO) queue implemented with a ring buffer structure, + * which allocates memory from an associated shared memory pool. It is designed for inter-process + * or inter-thread communication, managing element enqueue/dequeue via ring buffer indexes + * and reflecting memory allocation details through core parameters. + */ +struct SharedMemoryPoolQueue { + /* + * the memory-maping mode in Linux side. + */ + uint8_t map_mode1; + /* + * the memory-maping mode in microkernel-OS side. + */ + uint8_t map_mode2; + + /* header index of the ring buffer, pointing to the next free slot for enqueuing. + Works with `tail` to maintain FIFO order. */ + uint16_t header; + + /* tail index of the ring buffer, pointing to the next element to be dequeued. + Works with `header` to maintain FIFO order. */ + uint16_t tail; + + /* Current number of elements in the queue. Dynamically updated with + enqueue (increment) and dequeue (decrement) operations. */ + // size_t length; + + /* Total memory size (in bytes) allocated to this queue from the shared memory pool. + Determines the maximum storage space available for elements. */ + /* + * Maximum number of elements the queue can hold, representing the upper limit of elements + * based on allocated memory and element size. + */ + uint16_t capacity; + + /* Maximum number of elements the queue can hold. Calculated as (capacity / block_size), + representing the upper limit of elements based on allocated memory and element size. */ +// size_t max_num_items; + + /* Size (in bytes) of each element's memory block. All elements in the queue + occupy a fixed size to simplify memory management and access. */ + uint16_t block_size; + /* + * Physical address of the queue control block in shared memory. + * Used for direct access across processes or between kernel and user spaces. + * Can also assist in spinlock/mutex synchronization during sharing processes. + */ + uint64_t phy_addr; + /* + * Virtual address of the queue control block in the Linux size. + * Valid when map_mode1 is SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH. + */ + uint64_t virt_addr1; + /* + * Virtual address of the queue control block in the microkernel-OS size. + * Valid when map_mode2 is SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH + */ + uint64_t virt_addr2; + /* + * Address mapping table for the Linux side, storing up to MAX_MAP_TABLE_ENTRY_COUNT entries + * that map virtual addresses to physical addresses. + * + * * Valid when map_mode1 is SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL. + */ + MapTableEntry table1[MAX_MAP_TABLE_ENTRY_COUNT]; + /* + * Address mapping table for the microkernel-OS side, storing up to MAX_MAP_TABLE_ENTRY_COUNT entries + * that map virtual addresses to physical addresses. + * + * Valid when map_mode2 is SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL. + */ + MapTableEntry table2[MAX_MAP_TABLE_ENTRY_COUNT]; + /* + * Pointer to the associated shared memory pool. All memory for the queue + * (including control block and data blocks) is allocated from this pool. + */ + struct SharedMemoryPool *pool; +}__attribute__((packed)); + + +/** + * @brief Configuration parameters structure for initializing SharedMemoryPoolQueue + * + * A structure containing all necessary parameters required to initialize a SharedMemoryPoolQueue + * instance. It aggregates external input parameters that define the queue's memory properties + * and association with a shared memory pool, serving as a clean interface for queue initialization. + * + * Key parameters include: + * - `pool`: Reference to the associated shared memory pool (mandatory) + * - `map_mode`: the memory mapping mode + * - `phy_addr`/`virt_addr`: Addresses of the control block in shared memory and current process + * - `capacity`: Total number of elememts allocated to the queue from the pool + * - `block_size`: Fixed size of each element's memory block (determines max element count) + */ +typedef struct SharedMemoryPoolQueueConfig_{ + struct SharedMemoryPool *pool; // Associated shared memory pool +/* + * < Memory mapping mode (enumerated type). Specifies how the shared + * memory is mapped into the current process address space (e.g., + * direct physical mapping, virtual address translation). Refer to + * SHARE_MEM_MAP_MODE_* macros for valid values. + */ + uint16_t map_mode; + uint64_t phy_addr; // Physical address of control block in shared memory + uint64_t virt_addr; // Virtual address of control block in current process + size_t capacity; // Total memory size (bytes) allocated to the queue + size_t block_size; // Size (bytes) of each element's memory block +} SharedMemoryPoolQueueConfig; + + +/** + * Macro: Check if map_mode of SharedMemoryPoolQueueConfig is a valid enumerated value + * Function: Verifies whether the map_mode of the SharedMemoryPoolQueueConfig is correctly assigned to one of the valid values + * defined in the ShareMemMapMode enumeration, while ensuring the input config pointer is non-null + * (to avoid null pointer dereference). + * @param conf Pointer to the SharedMemoryPoolQueueConfig structure whose map_mode member needs to be checked + * @return Returns 1 (true) if conf is non-null and map_mode is a valid enumerated value; otherwise returns 0 (false) + */ +#define IS_VALID_SHM_CONF_MAP_MODE(conf) \ + ((conf) != NULL && ((conf)->map_mode == SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH \ + || (conf)->map_mode == SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL)) + + + +/** + * @brief FIFO queue ENQUEUE operation macro (standard C compatible version) + * + * This macro implements the PUSH operation for SharedMemoryPoolQueue. It uses a do...while structure + * to ensure syntax compatibility and returns the operation result through the second parameter. + * The specific logic is as follows: + * 1. Increment the header index; if it exceeds max_num_items(capacity), wrap around (circular nature) + * 2. Check if the operation triggers a queue exception (header equals tail or out of bounds) + * 3. If abnormal, roll back the header and set an error status; otherwise, set a success status + * + * @param queue Pointer to the SharedMemoryPoolQueue structure (input) + * @param result Variable to receive the operation result (output), which can be: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: Operation failed (queue is full or abnormal) + */ +#define SHMP_QUEUE_ENQUEUE(queue, result) do { \ + /* Initialize result to success status */ \ + (result) = FRONTEND_PROXY_PROCESS_OK; \ + /* Save original header for rollback in case of exception */ \ + uint16_t original_header = (queue)->header; \ + \ + /* Update header: increment by 1, wrap around if exceeding capacity */ \ + (queue)->header++; \ + if ((queue)->header >= (queue)->capacity) { \ + (queue)->header -= (queue)->capacity; \ + } \ + \ + /* Check for abnormal status: header conflicts with tail or out of bounds */ \ + if ((queue)->header == (queue)->tail || (queue)->header >= (queue)->capacity) { \ + /* Roll back header to pre-operation state */ \ + (queue)->header = original_header; \ + /* Set error result */ \ + (result) = FRONTEND_PROXY_PROCESS_ERROR; \ + } \ +} while(0) + + +/** + * @brief FIFO-queue DEQUEUE operation macro (standard C compatible version) + * + * This macro implements the DEQUEUE operation for SharedMemoryPoolQueue with priority on empty queue check. + * It first verifies if the queue is empty before modifying any indices. The logic is: + * 1. Check if queue is empty (tail equals header) - return error immediately if true + * 2. Save original tail for rollback in case of subsequent errors + * 3. Increment the tail index; if exceeding capacity, wrap around (circular nature) + * 4. Check for out-of-bounds error; rollback and return error if detected + * 5. Return success status if all operations complete normally + * + * @param queue Pointer to the SharedMemoryPoolQueue structure (input) + * @param result Variable to receive the operation result (output), which can be: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: Operation failed (queue is empty or abnormal) + */ +#define SHMP_QUEUE_DEQUEUE(queue, result) do { \ + /* First check if queue is empty (tail equals header) */ \ + if ((queue)->tail == (queue)->header) { \ + (result) = FRONTEND_PROXY_PROCESS_ERROR; \ + } else { \ + /* Initialize result to success status */ \ + (result) = FRONTEND_PROXY_PROCESS_OK; \ + /* Save original tail for rollback in case of exception */ \ + uint16_t original_tail = (queue)->tail; \ + \ + /* Update tail: increment by 1 */ \ + (queue)->tail++; \ + \ + /* Handle wrap-around if exceeding capacity */ \ + if ((queue)->tail >= (queue)->capacity) { \ + (queue)->tail -= (queue)->capacity; \ + } \ + } \ +} while(0) + + +/** + * @brief Macro to get the virtual address of the element pointed by header in the shared memory queue + * + * Calculates the virtual address of the queue element that the header index points to. + * The address is derived from the queue's base virtual address plus the offset calculated by + * header index multiplied by block size. + * + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @return uint64_t Virtual address of the element at header position + */ +#define SHMP_QUEUE_HEADER_VIRT_ADDR(queue) \ + ((uint64_t)(queue)->virt_addr + (uint64_t)((queue)->header + 1) * (uint64_t)(queue)->block_size) + + +/** + * @brief Macro to get the virtual address of the position pointed by tail in the shared memory queue + * + * Calculates the virtual address of the queue position that the tail index points to (next available slot for enqueuing). + * The address is derived from the queue's base virtual address plus the offset calculated by + * tail index multiplied by block size. + * + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @return uint64_t Virtual address of the position at tail index + */ +#define SHMP_QUEUE_TAIL_VIRT_ADDR(queue) \ + ((uint64_t)(queue)->virt_addr + (uint64_t)((queue)->tail + 1) * (uint64_t)(queue)->block_size) + + +/** + * @brief Macro to calculate used memory size using header and tail indices + * + * Computes the total memory occupied by elements in the queue using header and tail indices, + * without relying on the length field. Follows circular queue logic: + * - When tail >= header: used elements = tail - header + * - When tail < header: used elements = (capacity - header) + tail + * Total used memory is then elements count multiplied by block size. + * + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @return size_t Total used memory size in bytes + */ +#define SHMP_QUEUE_USED_MEMORY(queue) \ + ((size_t)( \ + ((queue)->tail >= (queue)->header) ? \ + ((queue)->tail - (queue)->header) : \ + ((queue)->capacity - (queue)->header + (queue)->tail) \ + ) * (size_t)(queue)->block_size) + + +/** + * @brief Macro to calculate surplus (available) memory in the shared memory queue + * + * Implements a two-step internal calculation: + * 1. First compute the number of available slots using header, tail and capacity: + * - When tail >= header: surplus slots = capacity - (tail - header) + * - When tail < header: surplus slots = header - tail + * 2. Then multiply available slots by block size to get surplus memory in bytes + * + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @return size_t Surplus memory size in bytes + */ +#define SHMP_QUEUE_SURPLUS_MEMORY(queue) \ + ((size_t)( \ + /* Step 1: Calculate number of surplus slots */ \ + ((queue)->tail >= (queue)->header) ? \ + ((queue)->capacity - ((queue)->tail - (queue)->header)) : \ + ((queue)->header - (queue)->tail) \ + /* Step 2: Convert slots to memory size */ \ + ) * (size_t)(queue)->block_size) + + +/** + * @brief Allocate a memory slot from the queue head + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @param addr_ptr Pointer to store the virtual address of allocated slot (output) + * @return Returns FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + * @note Allocation logic: + * 1. Check if queue is full (next header == tail) + * 2. If not full, store current header's slot address in addr_ptr + * 3. Update header with wrap-around handling (mod capacity) + */ +#define SHM_POOL_QUEUE_HEAD_ALLOC(queue, addr_ptr) ({ \ + int _status = FRONTEND_PROXY_PROCESS_ERROR; \ + if ((queue != NULL) && (addr_ptr != NULL)) { \ + uint16_t _next_header = (queue->header + 1) % (uint16_t)queue->capacity; \ + if (_next_header != queue->tail) { \ + *addr_ptr = (uintptr_t)(queue->virt_addr1 + (queue->header + 1)* queue->block_size); \ + queue->header = _next_header; \ + _status = FRONTEND_PROXY_PROCESS_OK; \ + } else { \ + *addr_ptr = ERROR_SHARED_MEM_ADDR; \ + } \ + } \ + _status; \ +}) + + + +/** + * @brief Looks up the virtual address from table1 or table2 with boundary checks + * + * This macro retrieves the virtual address from either table1 or table2 of the + * SharedMemoryPoolQueue structure based on table_id, with validation of input parameters. + * + * @param queue Pointer to SharedMemoryPoolQueue structure + * @param table_id Table identifier (must be 1 for table1 or 2 for table2) + * @param idx Index of the entry to look up (must be in range 0 to capacity-1) + * @param addr_ptr Pointer to store the retrieved virtual address. + * Set to ERROR_SHARED_MEM_ADDR if parameters are invalid. + * + * @note Performs validation on both table_id and index range. Ensures access + * only to valid entries within the bounds defined by queue->capacity. + */ +#define SHM_POOL_QUEUE_LOOKUP_VIRTADDR(queue, table_id, idx, addr_ptr) \ + do { \ + /* Initialize to error value by default */ \ + *(addr_ptr) = ERROR_SHARED_MEM_ADDR; \ + \ + /* Validate table_id is either 1 or 2 */ \ + if (table_id != 1 && table_id != 2) { \ + break; \ + } \ + \ + /* Validate index is within [0, capacity-1] range */ \ + if (idx >= (queue)->capacity || idx < 0) { \ + break; \ + } \ + \ + /* Look up virtual address from the specified valid table */ \ + if (table_id == 1) { \ + *(addr_ptr) = (queue)->table1[idx].virt_addr; \ + } else { /* table_id == 2 */ \ + *(addr_ptr) = (queue)->table2[idx].virt_addr; \ + } \ + } while (0) + + +/** + * @def SHM_POOL_QUEUE_ALLOC_FROM_HEADER(queue, addr_ptr) + * @brief Allocates a memory block from the header of a shared memory pool queue + * + * This macro allocates a memory block from the header position of a shared memory pool queue. + * It calculates the next header position, checks for available space, and assigns the memory + * address based on the queue's memory mapping mode. If allocation fails (e.g., null pointers + * or no available space), it sets the address to ERROR_SHARED_MEM_ADDR. + * + * @param[in] queue Pointer to the shared memory pool queue structure + * @param[out] addr_ptr Pointer to store the allocated memory block's address (as uintptr_t) + * + * @details The allocation process works as follows: + * 1. Checks if both @p queue and @p addr_ptr are non-null + * 2. Calculates the next header position using modulo arithmetic with queue capacity + * 3. Checks if space is available (next header != queue tail) + * 4. Assigns address based on mapping mode: + * - SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH: Directly calculates address from virtual base + * - SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL: Uses lookup macro for virtual address + * - Other modes: Sets error address + * 5. Updates queue header to next position on successful allocation + * 6. Sets @p addr_ptr to ERROR_SHARED_MEM_ADDR on errors (null pointers or no space) + */ +#define SHM_POOL_QUEUE_ALLOC_FROM_HEADER(queue, addr_ptr) \ + do { \ + if ((queue != NULL) && (addr_ptr != NULL)) { \ + uint16_t _next_header = (queue->header + 1) % (uint16_t)queue->capacity; \ + if (_next_header != queue->tail) { \ + if(SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH == queue->map_mode1) {\ + *addr_ptr = (uintptr_t)(queue->virt_addr1 + (queue->header + 1) * queue->block_size); \ + queue->header = _next_header; \ + } else if(SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL == queue->map_mode1){\ + SHM_POOL_QUEUE_LOOKUP_VIRTADDR(queue, 1, queue->header, addr_ptr);\ + queue->header = _next_header; \ + } else { \ + *addr_ptr = ERROR_SHARED_MEM_ADDR;\ + } \ + } else { \ + *addr_ptr = ERROR_SHARED_MEM_ADDR; \ + } \ + }\ + }while(0) + + +/** + * @brief Roll back the head position of a shared memory pool queue after failed post-allocation processing + * This macro reverts the head pointer of a shared memory pool queue to its previous position, + * specifically intended for scenarios where an element was successfully allocated (head pointer incremented), + * but subsequent processing of the allocated element (e.g., data initialization, validation, or business logic) failed. + * It restores the queue to a consistent state, preventing invalid allocation markers. + * @param queue Pointer to the SharedMemoryPoolQueue structure. If NULL, the macro performs no operation. + * @note Rollback conditions and logic: + * No action is taken if the input queue pointer is NULL (safety check) + * Rollback is skipped if header == tail (queue is empty), as this would create an invalid underflow state + * When rollback is performed: + * If header is 0, it wraps around to (capacity - 1) to maintain circular queue semantics + * For non-zero header values, it is simply decremented by 1 + * @details Critical usage context: + * This macro should be invoked only after a successful element allocation (where the head pointer was already advanced) + * but before completing the element's processing. Common failure scenarios include: + * Failed data writing to the allocated element + * Validation errors in the data to be stored + * Resource shortages during element initialization + * Aborted business logic operations after allocation + * Without this rollback, the queue would retain an advanced head pointer, marking a "used" slot that contains no valid data, + * leading to lost queue capacity and potential data corruption in subsequent operations. + * The do-while(0) structure ensures the macro behaves like a single statement, safe for use in if/else blocks without extra braces. + */ +#define SHM_POOL_QUEUE_HEAD_ROLLBACK(queue) do { \ + if (queue != NULL) { \ + if(queue->header == queue->tail) \ + break;\ + queue->header = (queue->header == 0) ? \ + (uint16_t)(queue->capacity - 1) : \ + (queue->header - 1); \ + } \ +} while (0) + + +int init_shared_mem_pool(struct SharedMemoryPool *mem_pool); +void free_shared_mem_pool(struct SharedMemoryPool *mem_pool); +uint64_t alloc_shared_mem(struct SharedMemoryPool *mem_pool); +void free_shared_mem(struct SharedMemoryPool *mem_pool, uint64_t addr); + +int init_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool); +void free_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool); +int fetch_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool); +int release_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool); + + + +struct SharedMemoryPoolQueue *shared_mem_pool_queue_create_frontend(const SharedMemoryPoolQueueConfig *config); + +int shared_mem_pool_queue_frontend_setup_pages(struct SharedMemoryPoolQueue *queue, uint32_t page_num, uint64_t page_phy[]); + +int shared_mem_pool_queue_initialize(struct SharedMemoryPoolQueue *queue, const SharedMemoryPoolQueueConfig *config); + +int shared_mem_pool_queue_destroy(struct SharedMemoryPoolQueue* queue); + + + +int shared_mem_pool_queue_send(struct SharedMemoryPoolQueue *queue, + const void *data, + size_t data_size); + +/** + * @brief Sends a variable-size block (up to block_size) to the SharedMemoryPoolQueue: One Copy + * + * Stores a variable-length data block (with size ≤ block_size) into the queue using one-copy semantics. + * The data is transferred to a shared memory slot through a single copy operation. Regardless of the + * actual data size, the entire block_size space in shared memory is occupied to maintain fixed-size + * slot management. Explicitly handles full queue cases. + * + * @param queue Pointer to the SharedMemoryPoolQueue instance + * @param data Input pointer to the variable-length data block to be sent. Must point to valid memory + * containing the data (not a pointer to a pointer). + * @param data_size Size of the input data block (must be > 0 and ≤ queue->block_size for valid operation) + * + * @return int Returns: + * - FRONTEND_PROXY_PROCESS_OK: Success, data was copied to shared memory and queue state updated + * - FRONTEND_PROXY_PROCESS_AGAIN: Queue is full (next tail position equals header) + * - FRONTEND_PROXY_PROCESS_ERROR: Invalid input parameters (NULL pointers for queue or data), + * invalid block_size (0 bytes), data_size = 0, or data_size > queue->block_size + * + * @note The input data block (pointed to by data) must remain valid until the copy operation completes. + * One-copy semantics involve a single data transfer (e.g., via memcpy) from the input buffer to the + * target shared memory slot. + * Critical behavior: Even if data_size < block_size, the entire block_size space in shared memory is + * reserved and counted as occupied (surplus decreases by block_size, not data_size). + * Queue state (tail, length, surplus) is updated via the SHMP_QUEUE_ENQUEUE macro after the copy completes. + * Address calculation: Target slot address is derived from queue->virt_addr + (tail * block_size). + */ +int shared_mem_pool_queue_send_oc(struct SharedMemoryPoolQueue *queue, + const void *data, + size_t data_size); + + +int shared_mem_pool_queue_recv_zc(struct SharedMemoryPoolQueue *queue, + void **buffer, + size_t *out_data_size); + + +/** + * @brief Acquire access right to the shared memory queue by obtaining the shared memory pool lock + * @param queue Pointer to the SharedMemoryPoolQueue instance to be accessed + * @return FRONTEND_PROXY_PROCESS_OK if lock is acquired successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., invalid pool handle); + * FRONTEND_PROXY_PROCESS_AGAIN if lock acquisition times out (retry may succeed) + * @note Retrieves the lock associated with the shared memory pool (queue->pool) to control queue access; + * Must be paired with SHARED_MEM_QUEUE_UNLOCK using the same queue to prevent deadlocks; + * FRONTEND_PROXY_PROCESS_AGAIN indicates temporary unavailability - callers should retry later + */ +#define SHARED_MEM_QUEUE_LOCK(queue) (fetch_shared_mem_pool_lock((queue)->pool)) + +/** + * @brief Release access right to the shared memory queue by releasing the shared memory pool lock + * @param queue Pointer to the SharedMemoryPoolQueue instance that was accessed + * @return FRONTEND_PROXY_PROCESS_OK if lock is released successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., releasing an unheld lock) + * @note Releases the lock associated with the shared memory pool (queue->pool) to end controlled access; + * Must be paired with SHARED_MEM_QUEUE_LOCK using the same queue to prevent deadlocks; + * Does not return FRONTEND_PROXY_PROCESS_AGAIN - release operation either succeeds or fails + */ +#define SHARED_MEM_QUEUE_UNLOCK(queue) (release_shared_mem_pool_lock((queue)->pool)) + +struct SharedMemoryPoolQueue *shared_mem_pool_queue_create_frontendend(const SharedMemoryPoolQueueConfig *config); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/uthash.h b/projects/sel4test/apps/front/include/uthash.h new file mode 100644 index 0000000..06c2eeb --- /dev/null +++ b/projects/sel4test/apps/front/include/uthash.h @@ -0,0 +1,1137 @@ +/* +Copyright (c) 2003-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.3.0 + +#include /* memcmp, memset, strlen */ +#include /* ptrdiff_t */ +#include /* exit */ + +#if defined(HASH_NO_STDINT) && HASH_NO_STDINT +/* The user doesn't have , and must figure out their own way + to provide definitions for uint8_t and uint32_t. */ +#else +#include /* uint8_t, uint32_t */ +#endif + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define DECLTYPE(x) (__typeof(x)) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifndef HASH_FUNCTION +#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FUNCTION(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) ((bv[(idx)/8U] & (1U << ((idx)%8U))) != 0) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) 1 +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + const struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#include /* fprintf, stderr */ +#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx + * (archive link: https://archive.is/Ivcan ) + */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ + default: ; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + break; \ + default: ; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ -- Gitee From c68d9e27da6edf81aca9aaaf78d2ce8ded1fdd8f Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:34:39 +0800 Subject: [PATCH 033/133] upload source files --- .../sel4test/apps/front/src/common_utils.c | 17 + projects/sel4test/apps/front/src/dev.c | 29 + projects/sel4test/apps/front/src/engine.c | 545 ++++++++ .../sel4test/apps/front/src/frontend_api.c | 510 +++++++ .../sel4test/apps/front/src/frontend_proto.c | 1243 +++++++++++++++++ projects/sel4test/apps/front/src/main.c | 39 + .../sel4test/apps/front/src/senario_test.c | 445 ++++++ projects/sel4test/apps/front/src/session.c | 246 ++++ .../sel4test/apps/front/src/session_pool.c | 801 +++++++++++ .../sel4test/apps/front/src/shared_mem_io.c | 397 ++++++ 10 files changed, 4272 insertions(+) create mode 100644 projects/sel4test/apps/front/src/common_utils.c create mode 100644 projects/sel4test/apps/front/src/dev.c create mode 100644 projects/sel4test/apps/front/src/engine.c create mode 100644 projects/sel4test/apps/front/src/frontend_api.c create mode 100644 projects/sel4test/apps/front/src/frontend_proto.c create mode 100644 projects/sel4test/apps/front/src/main.c create mode 100644 projects/sel4test/apps/front/src/senario_test.c create mode 100644 projects/sel4test/apps/front/src/session.c create mode 100644 projects/sel4test/apps/front/src/session_pool.c create mode 100644 projects/sel4test/apps/front/src/shared_mem_io.c diff --git a/projects/sel4test/apps/front/src/common_utils.c b/projects/sel4test/apps/front/src/common_utils.c new file mode 100644 index 0000000..f9898d6 --- /dev/null +++ b/projects/sel4test/apps/front/src/common_utils.c @@ -0,0 +1,17 @@ +#include "common_utils.h" + +void error_print(char *error){ +#if UTILS_ENABLE_DEBUG + printf("%s\n", error); +#endif +} + + +int debug_print(const char *format, ...){ +#if UTILS_ENABLE_DEBUG + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +#endif +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/dev.c b/projects/sel4test/apps/front/src/dev.c new file mode 100644 index 0000000..ad8c11e --- /dev/null +++ b/projects/sel4test/apps/front/src/dev.c @@ -0,0 +1,29 @@ +#include "common_utils.h" +#include "dev.h" +#include "engine.h" +#include "session.h" +#include "session_pool.h" + + + +FrontendDevInfo dev_array[] = { + [0] = { + .dev_status = 0, + .dev_id = 0, + .dev_type = 1, + .name = "Frontend_Dev0" + } +}; + + +FrontendDevListCfg global_dev_list_cfg; + + +void frontend_init_dev_list(){ + p_global_dev_list_cfg = &global_dev_list_cfg; + memset(p_global_dev_list_cfg, 0, sizeof(p_global_dev_list_cfg)); + p_global_dev_list_cfg->dev_info = dev_array; + p_global_dev_list_cfg->dev_num = sizeof(dev_array)/sizeof(FrontendDevInfo); + + utils_print("In %s: number of device is %d\n", __func__, sizeof(dev_array)/sizeof(FrontendDevInfo)); +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c new file mode 100644 index 0000000..41ac27b --- /dev/null +++ b/projects/sel4test/apps/front/src/engine.c @@ -0,0 +1,545 @@ +#include "engine.h" + +extern FrontendEngine *p_g_fr_eng; +extern FrontendEngine g_fr_eng; + + +/** + * Configuration structure for the high-speed network receive queue. + * + * This global instance initializes parameters for managing the receive queue's shared memory, + * including memory mapping mode, addresses, capacity, and block size. + */ +SharedMemoryPoolQueueConfig high_speed_net_rx_queue_config = { + .pool = NULL, + .map_mode = SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH, // Assumes virtual address mapping mode + .phy_addr = HSNET_RX_PHY_ADDR_BASE, // 64-bit unsigned integer zero value + .virt_addr = 0ULL, // 64-bit unsigned integer zero value + .capacity = MAX_MAP_TABLE_ENTRY_COUNT, // Total number of element + .block_size = 4096 // 4096 bytes per element block +}; + + +/** + * Configuration structure for the high-speed network transmit queue. + * + * This global instance initializes parameters for managing the transmit queue's shared memory, + * including memory mapping mode, addresses, capacity, and block size. + */ +SharedMemoryPoolQueueConfig high_speed_net_tx_queue_config = { + .pool = NULL, + .map_mode = SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH, // Assumes virtual address mapping mode + .phy_addr = HSNET_TX_PHY_ADDR_BASE, // 64-bit unsigned integer zero value + .virt_addr = 0ULL, // 64-bit unsigned integer zero value + .capacity = MAX_MAP_TABLE_ENTRY_COUNT, // Total number of element + .block_size = 4096 // 4096 bytes per element block +}; + + +/** + * @brief Get data from the specified RX queue (residing in shared memory) + * @param queue Pointer to the SharedMemoryPoolQueue (RX queue) to operate on + * @param[out] buf_ptr Double pointer to store the address of data in shared memory + * (points to actual data location in shared memory on success) + * @param buf_max_len Maximum allowed length of data that can be retrieved (in bytes) + * @param[out] out_len Pointer to store the actual length of obtained data (in bytes) + * @return FRONTEND_PROXY_PROCESS_OK if data is retrieved successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., invalid queue handle); + * FRONTEND_PROXY_PROCESS_AGAIN if data is temporarily unavailable (e.g., queue is empty) + * @note The caller is responsible for managing the lock of the shared memory pool + * (lock once before multiple calls to reduce overhead) + */ +int frontend_engine_rx_queue_get(struct SharedMemoryPoolQueue *queue, void **buf_ptr, + size_t buf_max_len, size_t *out_len){ + int ret; + size_t msg_size, buf_size; + ProxyMsgHeader *msg_hdr; + + if(NULL == queue || NULL == buf_ptr || NULL == out_len){ + error_print("frontend_engine_rx_queue_get failed: invalid input parameters (NULL pointers)"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + utils_print("In %s, before call shared_mem_pool_queue_recv_zc, buf_ptr = %p, *buf_ptr = %p\n", __func__, buf_ptr, *buf_ptr); + ret = shared_mem_pool_queue_recv_zc(queue, buf_ptr, &buf_size); + utils_print("In %s, after call shared_mem_pool_queue_recv_zc, buf_ptr = %p, *buf_ptr = %p\n", __func__, buf_ptr, *buf_ptr); + + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_engine_rx_queue_get failed: failed to retrieve data from RX queue"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + error_print("frontend_engine_rx_queue_get returns: the RX queue is empty"); + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + msg_hdr = (ProxyMsgHeader *)(*buf_ptr); + msg_size = msg_hdr->payload_len; + + if(msg_size + sizeof(ProxyMsgHeader) > buf_max_len){ + error_print("frontend_engine_rx_queue_get failed: message total size (header + payload) exceeds buffer maximum length"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + *out_len = msg_size; + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Retrieves a message from the shared memory pool queue. + * + * @details This function fetches message data from the specified SharedMemoryPoolQueue instance. + * It returns a pointer to the message buffer if data is available, and outputs the + * operation status and actual message length through output parameters. + * The caller should check the status code stored in @p ret before using the returned buffer + * and message length. + * + * @param[in] queue Pointer to the SharedMemoryPoolQueue instance, cannot be NULL + * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow + * @param[out] ret Pointer to an integer variable that stores the operation status code + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * + * @return Pointer to the uint8_t message data buffer on successful data retrieval; + * NULL if the operation fails, the queue is empty, or a system error occurs + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., invalid queue handle, NULL input pointers), + * the returned pointer is NULL and @p out_len is undefined + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message data is temporarily unavailable (e.g., queue is empty), + * the returned pointer is NULL and @p out_len is undefined + * + * @note The ownership and release mechanism of the returned data buffer depend on the implementation + * of the SharedMemoryPoolQueue module; refer to the module's documentation for memory management rules. + */ +uint8_t *frontend_engine_rx_queue_get_msg(struct SharedMemoryPoolQueue *queue, size_t max_msg_len, + int *ret, size_t *out_len){ + size_t msg_size, buf_size; + ProxyMsgHeader *msg_hdr; + uint8_t *buff_addr; + int ret_val; + + if(NULL == queue || NULL == ret || NULL == out_len){ + error_print("frontend_engine_rx_queue_get_msg failed: invalid input parameters (NULL pointers)\n"); + *ret = FRONTEND_PROXY_PROCESS_ERROR; + *out_len = 0; + return NULL; + } + + utils_print("In %s, before call shared_mem_pool_queue_recv_zc, buf_ptr = %p\n", __func__, buff_addr); + ret_val = shared_mem_pool_queue_recv_zc(queue, &buff_addr, &buf_size); + utils_print("In %s, after call shared_mem_pool_queue_recv_zc, buf_ptr = %p\n", __func__, buff_addr); + + if(FRONTEND_PROXY_PROCESS_ERROR == ret_val){ + error_print("frontend_engine_rx_queue_get_msg failed: failed to retrieve data from RX queue\n"); + *ret = FRONTEND_PROXY_PROCESS_ERROR; + *out_len = 0; + return NULL; + } + + if(FRONTEND_PROXY_PROCESS_AGAIN == ret_val){ + error_print("frontend_engine_rx_queue_get_msg failed: the RX queue is empty\n"); + *ret = FRONTEND_PROXY_PROCESS_AGAIN; + *out_len = 0; + return NULL; + } + + msg_hdr = (ProxyMsgHeader *)(buff_addr); + msg_size = msg_hdr->payload_len; + *ret = FRONTEND_PROXY_PROCESS_OK; + *out_len = msg_size + sizeof(ProxyMsgHeader); + + utils_print("*out_len = %d\n", *out_len); + + return buff_addr; +} + + +/** + * @brief Initialize the global FrontendEngine instance + * + * This function initializes the global FrontendEngine singleton (pointed to by `p_g_fr_eng`), + * including resource allocation, dependency initialization, and configuration loading. + * It must be called before any other operations related to the FrontendEngine (e.g., + * calling `get_global_frontend_engine()` to use the instance). + * + * @note 1. Recommended to call this function once during program startup (e.g., in main() before business logic); + * 2. This function is not thread-safe. Ensure no concurrent calls during initialization; + * 3. The function is non-reentrant. Repeated calls may cause resource leaks or initialization conflicts; + * 4. If initialization fails, subsequent use of the FrontendEngine instance (via `get_global_frontend_engine()`) + * may return a null pointer or lead to undefined behavior. + * + * @warning Do not skip this initialization or call it multiple times. Uninitialized or repeatedly initialized + * FrontendEngine may result in program crashes, resource conflicts, or functional abnormalities. + */ +void frontend_engine_init(){ + int ret; + memset(&g_fr_eng, 0, sizeof(g_fr_eng)); + p_g_fr_eng = &g_fr_eng; + + + p_g_fr_eng->sess_pool = malloc(sizeof(struct FrontendSessionPool)); + + ret = frontend_high_speed_init_pool(p_g_fr_eng->sess_pool); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize frontend high-speed session pool (frontend_high_speed_init_pool returned error)!"); + free(p_g_fr_eng->sess_pool); + abort(); + } + + ret = engine_init_hs_net_dev(p_g_fr_eng); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize frontend high-speed device list!\n!"); + free(p_g_fr_eng->sess_pool); + free(p_g_fr_eng->dev_set); + abort(); + } + + ret = engine_init_shared_mem_queue(p_g_fr_eng); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize shared memory queue!\n!"); + free(p_g_fr_eng->sess_pool); + free(p_g_fr_eng->dev_set); + abort(); + } +} + + +int frontend_engine_init_eng_ops(FrontendEngine *eng){ + if(NULL == eng){ + error_print("frontend_engine_init_eng_ops failed: the eng point is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int engine_init_shared_mem_queue(FrontendEngine *eng){ + struct SharedMemoryPoolQueue *rx_queue, *tx_queue; + SharedMemoryPoolQueueConfig *rx_queue_conf, *tx_queue_conf; + + if(NULL == eng){ + error_print("engine_init_shared_mem_queue() failed: the engine instance is NULL (uninitialized or invalid)!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + rx_queue_conf = &high_speed_net_rx_queue_config; + rx_queue = shared_mem_pool_queue_create_frontend(rx_queue_conf); + + + if(NULL == rx_queue){ + error_print("engine_init_shared_mem_queue() failed: out of memory for the shared memory RX queue allocation!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + tx_queue_conf = &high_speed_net_tx_queue_config; + tx_queue = shared_mem_pool_queue_create_frontend(tx_queue_conf); + + if(NULL == tx_queue){ + error_print("engine_init_shared_mem_queue() failed: out of memory for the shared memory TX queue allocation!"); + free(rx_queue); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + rx_queue_conf->pool = eng->mem_pool; + tx_queue_conf->pool = eng->mem_pool; + + eng->rx_queue = rx_queue; + eng->tx_queue = tx_queue; + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Initialize high-speed network devices for the frontend engine + * + * @details This function executes the full initialization process for high-speed network devices. + * It validates the input engine pointer, dynamically allocates memory for the high-speed + * device set structure, and initializes the global device configuration list. + * The function verifies the validity of the device count from the global configuration, + * initializes the device set memory to zero, and traverses the global configuration to + * copy core device parameters (ID, type, name) to the local high-speed network device array. + * All allocated memory will be freed properly in error scenarios to avoid memory leaks. + * + * @param eng Pointer to the FrontendEngine instance, must not be NULL + * + * @return int Status code indicating the execution result of device initialization + * @retval FRONTEND_PROXY_PROCESS_OK Initialization completed successfully + * @retval FRONTEND_PROXY_PROCESS_ERROR Initialization failed (null pointer, memory allocation failure, invalid device count) + * + * @note Depends on external function: frontend_init_dev_list() + * @note Depends on global configuration structure: p_global_dev_list_cfg + * @note Depends on macro definitions: MAX_HS_DEV_NUM, MIN_HS_DEV_NUM + * @note Uses memcpy for device name copying and memset for memory zero-initialization + * + * @warning The function performs dynamic memory allocation with malloc(), remember to manage + * the lifecycle of dev_set in subsequent business logic + */ +int engine_init_hs_net_dev(FrontendEngine *eng){ + FrontendHighSpeedNetDeviceSet *dev_set; + FrontendDevInfo *dev_info; + int dev_cnt, dev_num; + + if(NULL == eng){ + error_print("engine_init_hs_net_dev failed: the engine should not be NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + dev_set = malloc(sizeof(FrontendHighSpeedNetDeviceSet)); + + if(NULL == dev_set){ + error_print("engine_init_hs_net_dev failed: insurficient memory resource!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* + * Initialize global device configuration list + */ + frontend_init_dev_list(); + + dev_num = p_global_dev_list_cfg->dev_num; + + if(dev_num > MAX_HS_DEV_NUM || dev_num < MIN_HS_DEV_NUM){ + error_print("engine_init_hs_net_dev failed: invalied device number!\n"); + free(dev_set); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + memset(dev_set, 0, sizeof(FrontendHighSpeedNetDeviceSet)); + dev_cnt = 0; + +/* + * Traverse and obtain device pointers sequentially based on configurations in p_global_dev_list_cfg. + */ + while(dev_cnt < dev_num){ + dev_info = &dev_set->hs_net_dev[dev_cnt]; + dev_info->dev_id = p_global_dev_list_cfg->dev_info[dev_cnt].dev_id; + dev_info->dev_type = p_global_dev_list_cfg->dev_info[dev_cnt].dev_status; + memcpy(dev_info->name, p_global_dev_list_cfg->dev_info[dev_cnt].name, strlen(p_global_dev_list_cfg->dev_info[dev_cnt].name)); +// utils_print("The device name is %s\n", p_global_dev_list_cfg->dev_info[dev_cnt].name); +// utils_print("The device name is %s\n", dev_info->name); + dev_cnt++; + } + + eng->dev_set = dev_set; + + return FRONTEND_PROXY_PROCESS_OK; +} + + + + +/** + * @brief Main loop function of the engine, handling message processing and data transmission cyclically + * This function executes a continuous loop consisting of four main steps. After completing all steps, + * it returns to the first step to implement the core operation of the frontend engine. + * @details The loop process is as follows: + * + * 1. Read data from the RX queue of the shared memory queue owned by the FrontendEngine instance, and process + * them sequentially through the frontend proxy protocol stack. + * If any data is read, there are two cases: + * (a) For device messages, strategy messages, and session messages (these are responses from the backend): + * The frontend proxy protocol stack performs corresponding processing for each type, and updates the frontend's + * relevant state based on the message content (the frontend does not generate response packets). + * (b) For sessions that receive data messages: + * These sessions are placed into the backend-to-frontend active queue for processing in step (2). + * If no data message is found, the procedure jumps to step (3). + * + * 2. Access and process session instances in the backend-to-frontend active queue sequentially: + * Sequentially call the application callback function registered by each session in the queue (serving as a data-ready notification). + * After receiving the callback notification, the application will call the dedicated read API to read data from the corresponding session, + * and complete business processing independently based on the read data (the frontend does not involve active data extraction or socket transmission here). + * If the application needs to send data to the backend proxy through the shared memory queue, when the data to be sent is added to the send buffer, + * the session will be added to the frontend-to-backend active queue, and these sessions will be processed in STEP (3). + * + * 3. Access and process session instances in the frontend-to-backend active queue sequentially: + * For each session in the queue, read the pending data stored in the session's send buffer (data to be sent to the backend proxy by the application). + * Construct data messages according to the frontend proxy protocol specification, and send the messages to the backend proxy through the shared memory's + * TX queue. + * After the data is successfully sent, clear the corresponding pending data in the session's send buffer; if the send buffer becomes empty, remove the session + * from the frontend-to-backend active queue. (This step specifically handles data transmission requests initiated by the application, realizing the frontend- + * -to-backend data proxy through the shared memory queue) + * + * After completing all operations in step (3), the loop returns to step (1) to continue the cyclic processing. + */ +void frontend_engine_run(){ + FrontendEngine *eng; + struct SharedMemoryPoolQueue *rx_queue, *tx_queue; + struct FrontendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *cur_sess, *next_sess; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + uint8_t *proxy_msg; + uint32_t msg_size; + int ret; + + eng = frontend_get_global_engine(); +/* + * rx_queue: Local receive queue, which actually maps to the back-end's transmit queue (tx_queue). + * Data sent by the front-end through its tx_queue will be received by the local side via this rx_queue. + * + * tx_queue: Local transmit queue, which is used as the back-end's receive queue (rx_queue). + * Data sent by the local side through this tx_queue will be received by the back-end via its rx_queue. + */ + if(NULL == eng->rx_queue || NULL == eng->tx_queue){ + error_print("frontend_engine_run failed: The global backend engine's RX queue or TX queue has not been initialized!"); + return ; + } + + FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); + FRONTEND_ENGINE_GET_B2F_QUEUE(eng, active_queue_b2f); + + if(NULL == active_queue_f2b || NULL == active_queue_b2f){ + error_print("frontend_engine_run failed: The global frontend engine's f2b session queue or b2f session queue has not been initialized!"); + return ; + } + + if(NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("frontend_engine_run failed: Global frontend engine's session pool (sess_pool) or its operation set (ops) is not initialized!"); + return ; + } + + sess_pool = eng->sess_pool; + sess_pool_ops = sess_pool->ops; + + do{ +/* + * STEP (1). + */ +eng_run_step1: +/* + * Acquire access lock for the RX queue. + */ + ret = SHARED_MEM_QUEUE_LOCK(rx_queue); + +/* + * If returning BACKEND_PROXY_PROCESS_ERROR, it indicates a system-level error (e.g., invalid lock handle, shared memory pool corruption) + * Failed to acquire the lock; print error message and exit the current flow. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_engine_run failed: failed to get the lock of the RX queue!"); + return; + } + +/* + * If returning BACKEND_PROXY_PROCESS_AGAIN, it indicates lock acquisition timed out (temporary unavailability, e.g., lock held by another process) + * No error occurred; jump to eng_run_step3 to retry or proceed with alternative logic. + */ + if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + goto eng_run_step3; + } + + + do{ + + /* + * Retrieve data from the RX queue. + */ + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + + /* + * If returning FRONTEND_PROXY_PROCESS_ERROR, it indicates a system-level error (e.g., invalid queue handle, shared memory access exception, etc.) + * Processing cannot continue; print error message and return directly. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_engine_run failed: failed to get data from RX queue!"); + return; + } + + /* + * If returning FRONTEND_PROXY_PROCESS_AGAIN, it indicates temporary inability to retrieve data (e.g., empty queue, resource temporarily occupied, etc., non-error state) + * No error reporting needed; jump to eng_run_step2 to execute the next process. + */ + if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + goto eng_run_step2; + } + /* + * Process the proxy message. + */ + frontend_proxy_msg_process(proxy_msg); + + }while(FRONTEND_PROXY_PROCESS_OK == ret); + +eng_run_step2: + SHARED_MEM_QUEUE_UNLOCK(rx_queue); +/* + * Recall the FRONTEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (1) procedure may renew the back-to-front queue (queue_b2f) of the session pool. + */ + FRONTEND_ENGINE_GET_B2F_QUEUE(eng, active_queue_b2f); + + TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess){ + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_RECVDATA); + +/* + * queue_b2f: Session queue for sessions with pending data to be received by the frontend from the backend. + * (Pending data refers to data sent from the backend to the frontend, which the frontend needs to read.) + * If the receive queue (msg_b2f) is empty, the session instance should be removed from the queue_b2f list. + */ + if(TAILQ_EMPTY(&cur_sess->msg_b2f)){ + TAILQ_REMOVE(active_queue_b2f, cur_sess, entries_b2f); + cur_sess->state_b2f &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } + } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess) + + +eng_run_step3: +/* + * Recall the BACKEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (2) procedure may renew the front-to-back queue (queue_f2b) of the session pool. + */ + FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); + + SHARED_MEM_QUEUE_LOCK(tx_queue); + + TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess){ + +/* + * Call the corresponding data_process_f2b function pointer in the session pool's operation set (sess_pool_ops), which attempts to send the frontend-to-backend data maintained by the + * current session (cur_sess) via the shared memory's TX queue. + * The return value corresponds to three scenarios: + * Returns FRONTEND_PROXY_PROCESS_OK: All data has been sent successfully. + * Returns FRONTEND_PROXY_PROCESS_AGAIN: Not all data has been sent, and no errors occurred. + * Returns FRONTEND_PROXY_PROCESS_ERROR: An error occurred during the sending process. + */ + ret = sess_pool_ops->data_process_f2b(cur_sess); +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_OK, this indicates all message segments in the front-to-back (F2B) message queue have been sent via the shared memory's TX queue. + * Such sessions should be detached from the F2B active queue, and their "linked to queue" state flag should be cleared. + */ + if(FRONTEND_PROXY_PROCESS_OK == ret){ + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->state_f2b &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_ERROR, it indicates an error occurred when attempting to send data via the shared memory's TX queue. + * For such sessions, they should be removed from the frontend-to-backend active queue (active_queue_f2b), and the session's abnormal event callback should be triggered. + * + * Note that the recovery of resources occupied by the session needs to be completed during the processing of the FRONTEND_SESS_EVENT_ABNOMAL event, and this responsibility + * lies with the application. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNOMAL); + } +/* + * When not all data has been sent and no errors have occurred, no action is required. + */ + } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess) + + SHARED_MEM_QUEUE_UNLOCK(tx_queue); + + }while(1); +} + + +void frontend_engine_destory(){} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/frontend_api.c b/projects/sel4test/apps/front/src/frontend_api.c new file mode 100644 index 0000000..b2d94d5 --- /dev/null +++ b/projects/sel4test/apps/front/src/frontend_api.c @@ -0,0 +1,510 @@ +#include "frontend_api.h" + +/** + * @brief Macro to convert a dotted-decimal IPv4 string to an IPv4Address structure + * @details Parses a string in "xxx.xxx.xxx.xxx" format, validates each octet range (0-255), + * and populates the IPv4Address structure with the binary representation. + * Provides error messages to stderr for invalid formats or out-of-range values. + * + * @param ip_str Input string in dotted-decimal IPv4 format (e.g., "192.168.1.1") + * @param addr_struct Output IPv4Address structure to be populated with parsed values + */ +#define IPV4_STR_TO_ADDR(ip_str, addr_struct) do { \ + unsigned int a, b, c, d; /**< Temporary storage for parsed octet values */ \ + \ + /* Attempt to parse 4 octets from the input string */ \ + if (sscanf(ip_str, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) { \ + \ + /* Validate all octets are within the 0-255 range */ \ + if (a <= 255 && b <= 255 && c <= 255 && d <= 255) { \ + /* Populate the structure with validated octets */ \ + (addr_struct).data[0] = (uint8_t)a; \ + (addr_struct).data[1] = (uint8_t)b; \ + (addr_struct).data[2] = (uint8_t)c; \ + (addr_struct).data[3] = (uint8_t)d; \ + } else { \ + /* Handle octet values outside valid range */ \ + error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 address!"); \ + } \ + } else { \ + /* Handle invalid string format (not matching xxx.xxx.xxx.xxx) */ \ + error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 format!"); \ + } \ +} while(0) + + + +/** + * @brief Macro to convert an IPv4:port string to an IPv4PortTuple structure + * @details Parses a string in "xxx.xxx.xxx.xxx:port" format, splits it into IP address + * and port components, validates both parts, and populates the IPv4PortTuple. + * Port numbers must be in the range 0-65535. + * + * @param ip_port_str Input string in "xxx.xxx.xxx.xxx:port" format (e.g., "192.168.1.100:8080") + * @param tuple_struct Output IPv4PortTuple structure to be populated with parsed values + */ +#define IPV4_PORT_STR_TO_TUPLE(ip_port_str, tuple_struct) do { \ + char ip_str[16]; /* Buffer to store the IP address part (max IPv4 string length is 15) */ \ + unsigned int port; \ + \ + /* Parse the IP address and port from the input string */ \ + if (sscanf(ip_port_str, "%15[^:]:%u", ip_str, &port) == 2) { \ + \ + /* Convert and validate the IP address part */ \ + IPV4_STR_TO_ADDR(ip_str, (tuple_struct).ipv4_addr); \ + \ + /* Validate the port number (0-65535 range) */ \ + if (port > 65535) { \ + error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid port number: must be 0-65535)!"); \ + } else { \ + (tuple_struct).port = (uint16_t)port; \ + } \ + } else { \ + error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid IP:port format!"); \ + } \ +} while(0) + + +/** + * @brief Create and initialize a new FrontendSession instance + * + * This function is responsible for creating a FrontendSession object by first validating the input + * FrontendEngine_ context (including its session pool and associated operations), then allocating + * memory for the new session, and initializing the core association between the session and the engine. + * The new session will hold a reference to the input engine, while other session-specific fields + * remain uninitialized and require further setup by the caller. + * + * @param[in] eng Pointer to a FrontendEngine_ instance that provides the necessary context for session creation, + * including a valid session pool (`sess_pool`) and corresponding operation set (`sess_pool->ops`). + * Must not be NULL, and its nested `sess_pool` and `sess_pool->ops` must also be non-NULL. + * + * @return struct FrontendSession* + * - Pointer to the newly created and partially initialized FrontendSession instance on success. + * - NULL on failure (potential causes: invalid engine context (eng/sess_pool/ops is NULL), + * or memory allocation for FrontendSession fails). + * + * @note 1. This function only initializes the `eng` field of the FrontendSession; other member fields + * (e.g., session ID, connection status, protocol parameters) remain uninitialized and need + * to be set explicitly by the caller before using the session. + * 2. The memory of the returned FrontendSession is allocated via `malloc`. The caller is responsible + * for managing its lifecycle (e.g., releasing memory via `free` when the session is no longer needed) + * to avoid memory leaks. + * 3. The validity of `eng->sess_pool` and `eng->sess_pool->ops` is a prerequisite for subsequent + * session management operations (e.g., connection, release), hence the strict validation here. + * + * @warning 1. If `eng` is NULL, or `eng->sess_pool` is NULL, or `eng->sess_pool->ops` is NULL, the function + * will immediately return NULL and print an error message ("invalid engine context"), without + * attempting memory allocation. + * 2. Memory allocation failure (returned by `malloc`) will also result in a NULL return and an + * error message. The caller must check the return value before using the session pointer to + * prevent null pointer dereference crashes. + * 3. Uninitialized fields of the returned FrontendSession may contain garbage values; do not use + * the session for connection or other operations until all required fields are properly initialized. + */ +struct FrontendSession *frontend_sess_new(struct FrontendEngine_ *eng){ + struct FrontendSession *new_sess; + + if(NULL == eng || NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("frontend_sess_new failed: invalid engine context!\n"); + return NULL; + } + + new_sess = malloc(sizeof(struct FrontendSession)); + + if(NULL == new_sess){ + error_print("frontend_sess_new failed: failed to allocate memory for FrontendSession!\n"); + return NULL; + } + + new_sess->eng = eng; + new_sess->event_callback = default_session_event_callback; + + return new_sess; +} + +/** + * @brief Build a frontend session connection request based on the IP:PORT string and specified protocol, + * then send it to the shared memory queue + * + * This function accepts a frontend session object, protocol type, and an IP:PORT formatted address string. + * It will attempt to convert the proto and IP:PORT into an instance of struct SessMsgPara. If the conversion + * is successful, it will call the frontend_sess_connect function. Additionally, it parses the address information, + * constructs a connection request, and finally sends the request to the shared memory queue to complete the + * initialization and delivery of the connection request. + * + * @param[in,out] sess Pointer to the frontend session object, used to store session-related configurations + * and status information. Valid memory must be pre-allocated (non-NULL). + * @param[in] proto Connection protocol type (valid values: predefined protocol macros corresponding + * to TCP/UDP, which must be semantically consistent with the protocol field in SessMsgPara). + * @param[in] addr_str IP:PORT formatted address string (supports IPv4: "x.x.x.x:port" or IPv6: "[ipv6_addr]:port". + * Cannot be NULL or an empty string). + * + * @return int Function execution result: + * - FRONTEND_PROXY_PROCESS_OK: Successfully converted to struct SessMsgPara, called frontend_sess_connect, + * and sent the connection request to the shared memory queue. + * - FRONTEND_PROXY_PROCESS_ERROR: Execution failed (potential causes: sess is NULL, invalid format of addr_str, + * invalid protocol type, failed conversion to struct SessMsgPara, + * failed call to frontend_sess_connect, failed operation on the shared memory queue, etc.). + * + * @note 1. addr_str must strictly comply with the "IP:PORT" format. + * 2. The proto parameter must be a predefined valid protocol macro (e.g., SESS_TCP_PROTO/SESS_UDP_PROTO). Passing an invalid + * value will result in conversion failure and return FRONTEND_PROXY_PROCESS_ERROR. + * 3. The sess pointer must point to effectively allocated memory. The function will not actively allocate or free + * the sess memory; the caller is responsible for its lifetime management. + * 4. The initialization status of the shared memory queue is guaranteed by the underlying module. If the queue is + * not ready, the function will return FRONTEND_PROXY_PROCESS_ERROR. + * + * @warning If sess is NULL, addr_str is NULL, or addr_str has an invalid format, the function will immediately return + * FRONTEND_PROXY_PROCESS_ERROR without performing the conversion to struct SessMsgPara, calling frontend_sess_connect, + * or executing the connection request construction and sending operations. + */ +int frontend_sess_connect_by_addrstr(struct FrontendSession *sess, int proto, const char *addr_str){ + struct SessMsgPara para; +/* + * When the frontend session ID is set to FRONTEND_HANDOVER_SESSION_ID, the frontend protocol will allocate + * a frontend session ID before sending the SESSION-CREATION command to the backend proxy. + */ + para.frontend_sess_id = FRONTEND_HANDOVER_SESSION_ID; + para.backend_sess_id = BACKEND_HANDOVER_SESSION_ID; + para.ip_version = SESS_IPV4_PROTO; + para.dev_id = DEV_ID_AUTO_HANDOVER; + para.trans_proto = proto; + IPV4_PORT_STR_TO_TUPLE(addr_str, para.ip_port_tuple.ipv4_port_tuple); + + return frontend_sess_connect(sess, ¶); +} + + +/** + * @brief Build and send a frontend session connection request based on the provided SessMsgPara configuration + * + * This function uses the protocol, IP address, and port information encapsulated in struct SessMsgPara, + * combines it with the frontend session object (struct FrontendSession), to construct a valid connection request. + * It verifies the validity of input parameters first, then delivers the constructed request to the shared memory queue, + * completing the core process of frontend session connection initialization. + * + * @param[in,out] sess Pointer to the frontend session object, used to store session-related configurations, + * runtime status, and connection context. Must point to pre-allocated valid memory (non-NULL). + * @param[in] para Pointer to the SessMsgPara structure that contains connection core parameters: + * - Protocol type (TCP/UDP, consistent with predefined protocol macros) + * - Target IP address (valid IPv4/IPv6 string) + * - Target port number (valid range: 1~65535) + * Must be non-NULL and contain legally valid configuration data. + * + * @return int Function execution result: + * - FRONTEND_PROXY_PROCESS_OK: Successfully verified parameters, constructed the connection request, + * and sent it to the shared memory queue. + * - FRONTEND_PROXY_PROCESS_ERROR: Execution failed (potential causes: sess/para is NULL, invalid protocol + * type in para, illegal IP address/port in para, failed shared memory queue + * operation, or invalid session state in sess, etc.). + * + * @note 1. This function is the core implementation of frontend session connection; frontend_sess_connect_by_addrstr() + * calls this function after converting "IP:PORT" string and protocol to a valid struct SessMsgPara instance. + * 2. The function does not parse or correct the parameters in para (e.g., IP format, port range); the caller + * must ensure para contains legally valid configuration before calling. + * 3. The sess object must be properly initialized (e.g., default state set) before calling; uninitialized + * sess may lead to unexpected behavior or failure. + * 4. The shared memory queue's availability is guaranteed by the underlying module; if the queue is uninitialized + * or full, the function will return FRONTEND_PROXY_PROCESS_ERROR directly. + * + * @warning 1. If sess or para is NULL, the function will immediately return FRONTEND_PROXY_PROCESS_ERROR without + * performing any connection request construction or delivery operations. + * 2. Passing para with illegal parameters (e.g., invalid protocol, out-of-range port, malformed IP) will + * result in request construction failure and return FRONTEND_PROXY_PROCESS_ERROR. + */ +int frontend_sess_connect(struct FrontendSession *sess, struct SessMsgPara *para){ + struct FrontendEngine_ *eng; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *ops; + int ret; + + eng = sess->eng; + sess_pool = eng->sess_pool; + ops = sess_pool->ops; + + if(NULL == ops->create_sess_step1){ + error_print("frontend_sess_connect failed: sess_pool->ops->create_sess_step1 is NULL (required session creation step 1 function not configured)!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + utils_print("In %s, addr is %d.%d.%d.%d port = %d\n", __func__, + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[0], + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[1], + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[2], + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[3], + para->ip_port_tuple.ipv4_port_tuple.port); + + ret = ops->create_sess_step1(sess_pool, sess, para); + + return ret; +} + + +/** + * @brief Calculate the number of message segments required for sending a message (includes header + data) + * + * Each segment can hold up to `bsize` bytes (fixed header + message data). Uses ceil division to ensure full data coverage. + * + * @param dsize Total size of message data to send (bytes, non-negative) + * @param bsize Max capacity of a single message segment (bytes, must be > hsize) + * @param hsize Fixed size of the segment header (bytes, non-negative) + * + * @return size_t Required segment count: + * - 0: Invalid param (bsize <= hsize, no space for data) + * - >=1: Valid count (all data + headers fit in segments) + * + * @note 1. Effective data per segment: bsize - hsize (header occupies fixed space in each segment); + * 2. Ceil division: (dsize + effective_per_seg - 1U) / effective_per_seg to avoid truncation; + * 3. Edge cases: dsize=0 returns 1 (header-only segment if bsize>hsize); dsize <= effective_per_seg returns 1. + */ +#define CALC_MSG_SEG_NUM(dsize, bsize, hsize) \ +( \ + (bsize) > (hsize) ? \ + ((((dsize) + (bsize) - (hsize) - 1U) / ((bsize) - (hsize)))) : 0U \ +) + + +/** + * @brief Write data to the send buffer of a frontend session (for frontend session data transmission) + * + * This function is used to write specified data into the send buffer of a frontend session (FrontendSession). + * The underlying communication mechanism will subsequently be responsible for sending the buffered data to the frontend. + * Data is not sent directly to the network immediately, but first written to the buffer, which is limited by the remaining buffer space. + * + * @param[in] sess Pointer to the frontend session instance, pointing to the frontend session object to operate on; must not be NULL + * @param[in] data Pointer to the buffer of data to be sent, pointing to the start address of the data to be written into the send buffer; must not be NULL + * @param[in] size Length of the data to be sent (unit: bytes); must be greater than 0, otherwise no valid data can be written + * + * @return int Function execution result with the following specific meanings: + * - >0: Number of bytes successfully written to the send buffer (may be less than the requested size, depending on the remaining buffer space) + * - =0: Insufficient send buffer space, no data could be written + * - -1: Send error (e.g., invalid session, invalid parameters, failed underlying buffer operation, etc.) + * + * @note 1. Both parameters sess and data must be valid non-NULL pointers; otherwise, -1 may be returned and undefined behavior may be triggered; + * 2. If the return value is greater than 0 but less than size, it indicates that only part of the data was written to the buffer. The remaining data needs to be retried after the buffer has free space; + * 3. If 0 is returned, it is recommended to retry the call after a short delay; if -1 is returned, the session status and parameter validity should be checked first. + */ +int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t size){ + int block_size, seg_num, data_size, header_size, eff_data_per_seg, snd_size, cur_size, ret; + struct SessMsgSeg *msg_seg; + GeneralProxyMsgHeader data_msg_hdr; + uint8_t *payload, **res_pointer; + uint8_t *res_buf[100] = {NULL}; + + block_size = SESS_GET_QUEUE_BLOCK_SIZE(sess, tx); + + if(0 == block_size){ + error_print("frontend_sess_send failed: the TX queue is not initialized correctly!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + data_size = size; + header_size = PROXY_MSG_HDR_SIZE; + seg_num = CALC_MSG_SEG_NUM(data_size, block_size, header_size); + eff_data_per_seg = block_size - header_size; + + memset(&data_msg_hdr, 0, sizeof(data_msg_hdr)); + + data_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + data_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_DATA; + data_msg_hdr.outer_header.backend_sess_id = sess->backend_sess_id; + data_msg_hdr.outer_header.frontend_sess_id = sess->frontend_sess_id; +/* + * Copy data into message segments. Each message segment carries a PROXY-DATA message whose payload does not exceed block_size - header_size. + */ + snd_size = 0; + payload = data; + res_pointer = res_buf; + + while(seg_num > 0){ + if(data_size > eff_data_per_seg){ + cur_size = eff_data_per_seg; + }else{ + cur_size = data_size; + } + +// msg_seg = sess_msg_seg_alloc_lite(SESS_MSG_SEG_DYNAMIC_ALLOC); + msg_seg = sess_msg_seg_alloc(cur_size, SESS_MSG_SEG_DYNAMIC_ALLOC, NULL, NULL); + + if(NULL == msg_seg){ + error_print("frontend_sess_send failed: insufficient memory for allocating message segment instance!\n"); + goto seg_frag_imcomplete; + } +#if 0 + data_msg_hdr.outer_header.payload_len = cur_size; + ret = build_proxy_general_message(sess->eng, &data_msg_hdr, payload, cur_size, res_pointer, MEMORY_ALLOC_CALLER, NULL); + + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_sess_send failed: build data message error!\n"); + goto seg_frag_error; + } + + msg_seg->data = res_pointer; + msg_seg->len = cur_size + sizeof(ProxyMsgHeader); +#endif + memcpy(msg_seg->data, payload, cur_size); + + SESS_MSG_SEG_INSERT_QUEUE(sess, msg_seg, f2b); + + snd_size += cur_size; + payload += cur_size; + data_size -= cur_size; + + seg_num--; + }// while +/* + * All data is orgnized in frontend-to-backend message queue (msg_f2b). + */ + FRONTEND_SESS_LINK_TO_QUEUE(sess, f2b); + return snd_size; + +seg_frag_imcomplete: + +/* + * snd_size > 0 means at least one message segment has been inserted into the session's frontend-to-backend message queue (msg_f2b). This session should be marked as an active session (link to + * queue_f2b), so the frontend protocol module will process it and send the data in msg_f2b via the shared memory TX queue. + */ + if(snd_size > 0){ + FRONTEND_SESS_LINK_TO_QUEUE(sess, f2b); + } + + return snd_size; + +seg_frag_error: + FRONTEND_SESS_UNLINK_FROM_QUEUE(sess, f2b); + return -1; +} + + +/** + * @brief Read data from the receive buffer of a frontend session (for frontend session data reception) + * This function is used to read specified data from the receive buffer of a frontend session (FrontendSession). + * The underlying communication mechanism has already received data from the frontend and stored it in the receive buffer. + * Data is not read directly from the network immediately, but from the pre-received buffer, which is limited by the amount of available data in the buffer. + * The function is non-blocking: it will not block the calling thread waiting for data arrival or buffer operations, and will return immediately regardless of whether valid data is read. + * If the available space in the receive buffer is smaller than the size of a complete message block, the message block will be truncated, and only the part that fits in the buffer will + * be read. + * + * @param[in] sess Pointer to the frontend session instance, pointing to the frontend session object to operate on; must not be NULL + * @param[inout] data Pointer to the buffer for storing received data, pointing to the start address where the read data will be stored; must not be NULL + * @param[in] size Maximum length of data that can be stored in the receive buffer (unit: bytes); must be greater than 0, otherwise no valid data can be read + * @return int Function execution result with the following specific meanings: + * 0: Number of bytes successfully read from the receive buffer (may be less than the requested size, depending on the available data in the buffer) + * =0: No available data in the receive queue, no data could be read + * -1: Receive error (e.g., invalid session, invalid parameters, failed underlying buffer operation, etc.) + * @note 1. Both parameters sess and data must be valid non-NULL pointers; otherwise, -1 may be returned and undefined behavior may be triggered; + * If the return value is greater than 0 but less than size, it indicates that only part of the available data was read from the buffer. The remaining data can be retried after new data is + * received into the buffer;If 0 is returned, it is recommended to retry the call after a short delay; if -1 is returned, the session status and parameter validity should be checked first. + */ +int frontend_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t size){ + struct SessMsgSeg *cur_seg; + int read_size, ret; + + if(NULL == sess || NULL == data || 0 == size){ + error_print("frontend_sess_recv failed: invalid parameters!\n"); + return -1; + } + + cur_seg = TAILQ_FIRST(&sess->msg_b2f); +/* + * The receive queue is empty. + */ + if(NULL == cur_seg){ + return 0; + } + + read_size = (size > cur_seg->len) ? cur_seg->len : size; + + memcpy(data, cur_seg->data, read_size); + TAILQ_REMOVE(&sess->msg_b2f, cur_seg, entry); + free(cur_seg); + +#if 0 +/* + * queue_b2f: Session queue for sessions with pending data to be received by the frontend from the backend. + * (Pending data refers to data sent from the backend to the frontend, which the frontend needs to read.) + * If the receive queue (msg_b2f) is empty, the session instance should be removed from the queue_b2f list. + */ + if(TAILQ_EMPTY(&sess->msg_b2f)){ + FRONTEND_SESS_UNLINK_FROM_QUEUE(sess, b2f); + } +#endif + return read_size; +} + + +/** + * @brief Send SESSION-CLOSURE command to backend and release the frontend session + * + * This function is responsible for sending a SESSION-CLOSURE command message to the backend, + * notifying it to close the session associated with the given FrontendSession instance. + * After the backend completes the session closure and sends a SESSION-CLOSURE response message, + * the frontend will release all resources occupied by this session. + * + * @param[in] sess Pointer to the FrontendSession instance to be closed; must be a valid non-NULL pointer + * + * @return Operation status code: + * - FRONTEND_PROXY_PROCESS_OK: SESSION-CLOSURE command sent successfully, + * and the session has been released after receiving backend response + * - FRONTEND_PROXY_PROCESS_ERROR: Operation failed, which may be caused by input parameters + * that do not meet the requirements or temporary failure to + * construct the SESSION-CLOSURE command message + * + * @note 1. If a NULL pointer or invalid FrontendSession instance is passed, the function will immediately + * return FRONTEND_PROXY_PROCESS_ERROR; + * 2. After the function returns successfully, the memory space pointed to by 'sess' may be freed + * internally (depending on specific implementation), and subsequent access to this pointer is prohibited; + * 3. Ensure that any necessary pre-processing (e.g., data synchronization) is completed before calling + * this function to avoid data loss during session closure + */ +int frontend_sess_close(struct FrontendSession *sess){ + struct FrontendEngine_ *eng; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *ops; + int ret; + + eng = sess->eng; + sess_pool = eng->sess_pool; + ops = sess_pool->ops; + + if(NULL == ops->close_sess_step1){ + error_print("frontend_sess_close failed: sess_pool->ops->close_sess_step1 is NULL (required session creation step 1 function not configured)!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + ret = ops->close_sess_step1(sess_pool, sess); + + return ret; +} + + +/** + * @brief Binds an event callback function to a specified frontend session instance. + * + * @details This function performs NULL validation on the input parameters, + * and assigns the user-provided event callback handler to the callback + * member of the FrontendSession structure. If any input pointer is NULL, + * an error log will be printed and an error status code will be returned. + * + * @param[in] sess Pointer to the target FrontendSession structure instance, + * cannot be a NULL pointer. + * @param[in] event_callback Function pointer of the session event callback type, + * used to handle session-related events, cannot be a NULL pointer. + * + * @return int Execution status code: + * - @ref FRONTEND_PROXY_PROCESS_OK Operation succeeded, callback bound successfully + * - @ref FRONTEND_PROXY_PROCESS_ERROR Operation failed, invalid NULL input parameters + * + * @note This function only performs parameter validation and direct member assignment, + * without thread synchronization or additional state checking for the session. + */ +int frontend_sess_bind_callback(struct FrontendSession *sess, SESS_EVENT_CALLBACK event_callback){ + if(NULL == sess || NULL == event_callback){ + error_print("frontend_sess_bind_callback failed: input pointer parameters must not be NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess->event_callback = event_callback; + + return FRONTEND_PROXY_PROCESS_OK; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c new file mode 100644 index 0000000..73bf418 --- /dev/null +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -0,0 +1,1243 @@ +#include "engine.h" +#include "frontend_proto.h" + + + +/* + * Functions for building sub-type proxy messages. + * + * - build_proxy_dev_message : Builds a device-specific proxy message + * - build_proxy_strgy_message : Builds a strategy-specific proxy message + * - build_proxy_sess_message : Builds a session-specific proxy message + * - build_proxy_data_message : Builds a data-specific proxy message + */ + + +/** + * @brief Builds a complete proxy device message by combining the device header and payload. + * Builds a complete proxy device message by combining the device message header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * @param[in] dev_hdr Pointer to a DevMsgHeader structure specifying the device message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the device message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * dev_hdr->payload_len (if header contains payload length field) for consistency. + * @param[out] result_msg Double pointer to receive the address of the constructed proxy device message. + * On success, points to a newly allocated buffer containing the complete device message. + * Caller must free this memory with appropriate function when done. + * Must not be NULL. + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful message construction; + * Returns FRONTEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) or memory allocation fails. + */ + int build_proxy_dev_message(DevMsgHeader *dev_hdr, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ + DevMsgHeader *header; + size_t corr_len; + uint8_t *dev_msg; + + + corr_len = DEV_MSG_HEADER_PAYLOAD_LEN(dev_hdr); + utils_print("corr_len = %d, payload_len = %d\n", corr_len, payload_len); + + if(payload_len != corr_len){ + error_print("build_proxy_dev_message failed: payload length does not match expected value based on message type and action type!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(payload_len != dev_hdr->payload_len){ + error_print("build_proxy_dev_message failed: payload length does not match the payload_len field in DevMsgHeader!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + dev_msg = *result_msg; + header = (DevMsgHeader *)dev_msg; + header->version = dev_hdr->version; + header->msg_type = dev_hdr->msg_type; + header->msg_id = dev_hdr->msg_id; + header->action_type = dev_hdr->action_type; + header->payload_len = payload_len; + + dev_msg += sizeof(DevMsgHeader); + + utils_print("In %s, before memcpy\n", __func__); + memcpy(dev_msg, payload, payload_len); + + return FRONTEND_PROXY_PROCESS_OK; + } + + +/** + * @brief Builds a complete proxy strategy message by combining the strategy header and payload. + * Builds a complete proxy strategy message by combining the strategy message header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * @param[in] strgy_hdr Pointer to a StrgyMsgHeader structure specifying the strategy message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the strategy message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * strgy_hdr->payload_len (if header contains payload length field) for consistency. + * @param[out] result_msg Double pointer to receive the address of the constructed proxy strategy message. + * On success, points to a newly allocated buffer containing the complete strategy message. + * Caller must free this memory with appropriate function when done. + * Must not be NULL. + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful message construction; + * Returns FRONTEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) or memory allocation fails. + */ +int build_proxy_strgy_message(StrgyMsgHeader *strgy_hdr, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ + StrgyMsgHeader *header; + size_t corr_len; + uint8_t *strgy_msg; + + corr_len = STRGY_MSG_HEADER_PAYLOAD_LEN(strgy_hdr); + + if(payload_len != corr_len){ + error_print("build_proxy_strgy_message failed: payload length does not match expected value based on message type and action type!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(payload_len != strgy_hdr->payload_len){ + error_print("build_proxy_strgy_message failed: payload length does not match the payload_len field in StrgyMsgHeader!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + strgy_msg = *result_msg; + header = (StrgyMsgHeader *)strgy_msg; + header->version = strgy_hdr->version; + header->msg_type = strgy_hdr->msg_type; + header->msg_id = strgy_hdr->msg_id; + header->action_type = strgy_hdr->action_type; + header->payload_len = payload_len; + + strgy_msg += sizeof(StrgyMsgHeader); + memcpy(strgy_msg, payload, payload_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Builds a complete proxy session message by combining the session header and payload. + * Builds a complete proxy session message by combining the session message header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * @param[in] sess_hdr Pointer to a SessMsgHeader structure specifying the session message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the session message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * sess_hdr->payload_len (if header contains payload length field) for consistency. + * @param[out] result_msg Double pointer to receive the address of the constructed proxy session message. + * On success, points to a newly allocated buffer containing the complete session message. + * Caller must free this memory with appropriate function when done. + * Must not be NULL. + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful message construction; + * Returns FRONTEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) or memory allocation fails. + */ +int build_proxy_sess_message(SessMsgHeader *sess_hdr, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ + utils_print("In %s\n", __func__); + SessMsgHeader *header; + size_t corr_len; + uint8_t *sess_msg; + + corr_len = SESS_MSG_HEADER_PAYLOAD_LEN(sess_hdr); + + if(payload_len != corr_len){ + error_print("build_proxy_sess_message failed: payload length does not match expected value based on message type, action type and IP version!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(payload_len != sess_hdr->payload_len){ + error_print("build_proxy_sess_message failed: payload length does not match the payload_len field in SessMsgHeader!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess_msg = *result_msg; + header = (SessMsgHeader *)sess_msg; + header->version = sess_hdr->version; + header->msg_type = sess_hdr->msg_type; + header->action_type = sess_hdr->action_type; + header->ip_version = sess_hdr->ip_version; + header->payload_len = sess_hdr->payload_len; + + utils_print("In %s, version = %d, msg_type = %d, action_type = %d, ip_version = %d, payload_len = %d, address = %p\n", + __func__, header->version, header->msg_type, header->action_type, header->ip_version, header->payload_len, &header); + + sess_msg += sizeof(SessMsgHeader); + + if(0 == payload_len){ + if(NULL == payload){ + return FRONTEND_PROXY_PROCESS_OK; + }else{ + error_print("build_proxy_sess_message failed: payload_len is 0, but payload is not NULL!\n\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + } + + memcpy(sess_msg, payload, payload_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Builds a complete proxy data message by combining the proxy message header and payload. + * Builds a complete proxy data message by combining the proxy message header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * @param[in] proxy_msg_hdr Pointer to a ProxyMsgHeader structure specifying the proxy data message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the data message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * proxy_msg_hdr->payload_len (if header contains payload length field) for consistency. + * @param[out] result_msg Double pointer to receive the address of the constructed proxy data message. + * On success, points to a newly allocated buffer containing the complete data message. + * Caller must free this memory with appropriate function (e.g., free()) when done. + * Must not be NULL. + * @return int Returns BACKEND_PROXY_PROCESS_OK on successful message construction; + * Returns BACKEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) or memory allocation fails. +*/ +int build_proxy_data_message(ProxyMsgHeader *proxy_msg_hdr, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ + utils_print("In %s\n", __func__); + uint8_t *data_msg; + utils_print("In %s, payload_len = %d, proxy_msg_hdr->payload_len = %d\n", __func__, payload_len, proxy_msg_hdr->payload_len); + utils_print("frontend_sess_id = %d, frontend_sess_id =%d\n", proxy_msg_hdr->frontend_sess_id, proxy_msg_hdr->backend_sess_id); + +#if 0 + if(payload_len != proxy_msg_hdr->payload_len){ + error_print("build_proxy_data_message failed: payload length does not match expected value based on message type, action type and IP version!"); + return BACKEND_PROXY_PROCESS_ERROR; + } +#endif + + utils_print("Address of proxy data header = %p, content =%p, size of ProxyMsgHeader = %d\n", proxy_msg_hdr, *result_msg, sizeof(ProxyMsgHeader)); + + data_msg = *result_msg; + memcpy(data_msg, payload, payload_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + + + +/** + * @brief Builds a complete message by combining the general header and payload. + * + * Builds a complete proxy general message by combining the general header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * + * Constructs a complete proxy general message by integrating the provided header and payload. + * The memory for the output message is managed based on the specified allocation mode: + * For MEMORY_ALLOC_SHARED: Memory is allocated within shared memory, which is organized in a FIFO RING buffer. + * The caller does not need to handle memory deallocation, as the shared memory is managed by the FIFO RING buffer mechanism. + * For MEMORY_ALLOC_CALLER: Memory must be pre-allocated by the caller. The function will directly populate the + * provided buffer without checking its size; the caller is solely responsible for ensuring the buffer is large + * enough to hold the complete message (header + payload). + * + * + * @param[in] engine Pointer to a BackendEngine object containing backend proxy's global context, + * such as runtime configuration, memory allocator handles, or system resources. + * Used for accessing backend-specific settings or memory management during message construction. + * Must not be NULL. + * @param[in] header Pointer to a GeneralProxyMsgHeader structure specifying the message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * header->payload_len (if header contains payload length field) for consistency. + + * @param[out] result_msg Double pointer to receive the address of the constructed proxy message. + For MEMORY_ALLOC_SHARED: On success, points to the message location within the shared FIFO RING buffer. + No caller action is needed for deallocation. + For MEMORY_ALLOC_CALLER: Must point to a pre-allocated buffer. On success, the buffer is populated with + the complete message. Caller must ensure sufficient size.Must not be NULL. + * @param[in] ring_buf Pointer to a struct SharedMemoryPoolQueue. Required and must not be NULL when alloc_mode is MEMORY_ALLOC_SHARED + (used for FIFO RING buffer operations). + Ignored when alloc_mode is MEMORY_ALLOC_CALLER (can be NULL). + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful message construction; + * Returns FRONTEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) + * Returns FRONTEND_PROXY_PROCESS_AGAIN if shared-memory queue is full (for MEMORY_ALLOC_SHARED) + */ + +#if 1 +int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *header, + const uint8_t *payload, size_t payload_len, uint8_t **result_msg, + MemoryAllocMode alloc_mode, struct SharedMemoryPoolQueue *ring_buf){ + uint8_t *msg_buf; + uint64_t mem_addr; + uint16_t proxy_msg_payload_len; + ProxyMsgType outer_msg_type; + ProxyMsgHeader *proxy_msg_hdr; + DevMsgHeader *dev_hdr; + StrgyMsgHeader *strgy_hdr; + SessMsgHeader *sess_hdr; + int ret, alloc_size; + +/* + * Check the validity of the input parameters. + */ + if(NULL == header || NULL == result_msg){ + error_print("build_proxy_general_message failed: input(s) for generating proxy message is/are NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + if(NULL == engine){ + error_print("build_proxy_general_message failed: backend engine is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + outer_msg_type = header->outer_header.proxy_msg_type; + header->inner_header.dev_hdr.payload_len; + + +/* + * Allocate shared-memory for storing the proxy message. + */ + if(MEMORY_ALLOC_SHARED == alloc_mode){ + if(NULL == ring_buf){ + error_print("build_proxy_general_message failed: MEMORY_ALLOC_SHARED mode requires a non-NULL ring buffer (FIFO queue)!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + utils_print("before SHM_POOL_QUEUE_ALLOC_FROM_HEADER, header = %d, tail = %d, virt addr = %lld\n", ring_buf->header, ring_buf->tail, ring_buf->virt_addr1); + SHM_POOL_QUEUE_ALLOC_FROM_HEADER(ring_buf, &mem_addr); + utils_print("after SHM_POOL_QUEUE_ALLOC_FROM_HEADER, header = %d, tail = %d, memaddr = %lld\n", ring_buf->header, ring_buf->tail, mem_addr); + + if(ERROR_SHARED_MEM_ADDR == mem_addr){ + error_print("build_proxy_general_message failed: shared memory FIFO queue is full, cannot allocate new block!\n"); + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + msg_buf = (uint8_t *)mem_addr; + *result_msg = msg_buf; + +// SHM_POOL_QUEUE_LOOKUP_VIRTADDR(ring_buf, 1, 1, &mem_addr); +// SHM_POOL_QUEUE_ALLOC_FROM_HEADER(ring_buf, &mem_addr); + }else if(MEMORY_ALLOC_CALLER == alloc_mode){ +/* + * Frontend protocol should allocate memory dynamically. + */ + alloc_size = sizeof(ProxyMsgHeader) + header->outer_header.payload_len; + msg_buf = malloc(alloc_size); + + if(NULL == msg_buf){ + error_print("insufficient memory for allocation!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + *result_msg = msg_buf; + }else{ + error_print("build_proxy_general_message failed: unsupported allocte mode!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* + * Fill the proxy message header. + */ + proxy_msg_hdr = (ProxyMsgHeader *)msg_buf; + proxy_msg_hdr->version = header->outer_header.version; + proxy_msg_hdr->proxy_msg_type = outer_msg_type; + proxy_msg_hdr->frontend_sess_id = header->outer_header.frontend_sess_id; + proxy_msg_hdr->backend_sess_id = header->outer_header.backend_sess_id; + + utils_print("In %s, version = %d, proxy_msg_type = %d, frontend_sess_id = %d, backend_sess_id = %d, payload_len = %d\n", __func__, + proxy_msg_hdr->version, proxy_msg_hdr->proxy_msg_type, proxy_msg_hdr->frontend_sess_id, proxy_msg_hdr->backend_sess_id, proxy_msg_hdr->payload_len); + msg_buf += sizeof(ProxyMsgHeader); + switch(outer_msg_type) { + case PROXY_MSG_TYPE_DEV: + dev_hdr = &header->inner_header.dev_hdr; + proxy_msg_payload_len = sizeof(DevMsgHeader); + utils_print("In %s, before enter build_proxy_dev_message\n", __func__); + ret = build_proxy_dev_message(dev_hdr, payload, payload_len, &msg_buf); + break; + case PROXY_MSG_TYPE_STRGY: + strgy_hdr = &header->inner_header.strgy_hdr; + proxy_msg_payload_len = sizeof(StrgyMsgHeader); + ret = build_proxy_strgy_message(strgy_hdr, payload, payload_len, &msg_buf); + break; + case PROXY_MSG_TYPE_SESS: + sess_hdr = &header->inner_header.sess_hdr; + proxy_msg_payload_len = sizeof(SessMsgHeader); + utils_print("In %s, before enter build_proxy_sess_message\n", __func__); + ret = build_proxy_sess_message(sess_hdr, payload, payload_len, &msg_buf); + break; + case PROXY_MSG_TYPE_DATA: + proxy_msg_payload_len = 0; + ret = build_proxy_data_message(proxy_msg_hdr, payload, payload_len, &msg_buf); + utils_print("In %s, after build_proxy_data_message, the return value is %d\n", __func__, ret); + break; + default: +/* + * Message type is not supported!. + */ + error_print("build_proxy_general_message failed: message type is not supported!"); + free_shared_mem(engine->mem_pool, mem_addr); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("build_proxy_general_message failed: failed to build proxy message!"); +// free_shared_mem(engine->mem_pool, mem_addr); + SHM_POOL_QUEUE_HEAD_ROLLBACK(ring_buf); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* + * Compute the payload length and fill it into the corresponding field of the proxy message header. + */ + proxy_msg_hdr->payload_len = payload_len + proxy_msg_payload_len; + +/* + * In MEMORY_ALLOC_SHARED mode, the build_proxy_general_message function is responsible for + * enqueuing the constructed message into the shared memory FIFO queue (ring_buf) + */ +#if 0 + if(MEMORY_ALLOC_SHARED == alloc_mode){ + utils_print("before SHMP_QUEUE_ENQUEUE, header = %d, tail = %d\n", ring_buf->header, ring_buf->tail); + SHMP_QUEUE_ENQUEUE(ring_buf, ret); + utils_print("after SHMP_QUEUE_ENQUEUE, header = %d, tail = %d\n", ring_buf->header, ring_buf->tail); + + SHM_POOL_QUEUE_HEAD_ROLLBACK(ring_buf); + utils_print("after SHM_POOL_QUEUE_HEAD_ROLLBACK, header = %d, tail = %d\n", ring_buf->header, ring_buf->tail); + return ret; + } +#endif + + +#if 0 + int debug_cnt; + utils_print("shared queue capacity = %d, header = %d, tail = %d, block_size = %d\n", ring_buf->capacity, ring_buf->header, ring_buf->tail, ring_buf->block_size); + + debug_cnt = ring_buf->header; + + utils_print("Debug shared memory I/O\n"); + utils_print("virt addr = %lld\n", ring_buf->virt_addr1); + while(debug_cnt < ring_buf->capacity + 10){ + uint64_t debug_mem_addr; + SHM_POOL_QUEUE_ALLOC_FROM_HEADER(ring_buf, &debug_mem_addr); + utils_print("shared queue header = %d, tail = %d, addr = %lld, diff = %d\n", ring_buf->header, ring_buf->tail, debug_mem_addr, debug_mem_addr - ring_buf->virt_addr1); + debug_cnt++; + } +#endif + return FRONTEND_PROXY_PROCESS_OK; +} +#endif + + +/** + * @brief Frontend proxy message processing main entry + * + * @details As the core message distribution function of the frontend proxy, it is responsible for parsing the type field of the input proxy message, + * and automatically routing to the corresponding specialized processing function according to the message type to realize differentiated processing of different types of proxy messages. + * Supported message types and their corresponding processing functions are as follows: + * - Device-related messages: Call frontend_proxy_dev_msg_process + * - Strategy-related messages: Call frontend_proxy_strgy_msg_process + * - Session-related messages: Call frontend_proxy_sess_msg_process + * - Data-related messages: Call frontend_proxy_data_msg_process + * + * @param msg Pointer to the proxy message buffer, pointing to a uint8_t array containing the message type identifier and specific message content + * @return int Processing result status code + * - FRONTEND_PROXY_PROCESS_OK: Message distribution succeeded (Note: The specific processing result is guaranteed by the corresponding specialized function) + * - FRONTEND_PROXY_PROCESS_ERROR: Message type parsing failed or an error occurred during the distribution process + * + * @note 1. The input parameter 'msg' must be non-null and point to valid memory; otherwise, undefined behavior may occur + * 2. The parsing rule of the message type must be consistent with that of the backend proxy (backend_proxy_msg_process) + * 3. The return result of the specialized processing function does not affect the return status of the current function; only whether the distribution process is successful is fed back + */ +int frontend_proxy_msg_process(uint8_t *msg){ + utils_print("In %s\n", __func__); + ProxyMsgHeader *proxy_msg_hdr; + int proxy_proto_ver, msg_len, ret; + ProxyMsgType msg_type; + uint16_t frontend_sess_id, backend_sess_id; + uint8_t *msg_ptr; + + struct FrontendSession* sess; + + proxy_msg_hdr = (ProxyMsgHeader *)msg; + +/* + * Currently, the backend protocol stack does not differentiate the protocol version, we reserve the protocol version for future extensions. + */ + proxy_proto_ver = proxy_msg_hdr->version; + frontend_sess_id = proxy_msg_hdr->frontend_sess_id; + backend_sess_id = proxy_msg_hdr->backend_sess_id; + msg_type = proxy_msg_hdr->proxy_msg_type; + msg_len = proxy_msg_hdr->payload_len; + + utils_print("In %s, version = %d, frontend sess id = %d, backend sess id = %d, msg_type = %d, msg_len = %d\n", + __func__, proxy_proto_ver, frontend_sess_id, backend_sess_id, msg_type, msg_len); +/* + * Check the validity of the message type. + */ + if(!PROXY_MSG_TYPE_VALID(msg_type)){ + error_print("frontend_proxy_msg_process failed: unsupported message type!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + }// Unsupported message type. + +/* + * Check the validity of the message length. + */ + if(!PROXY_MSG_LEN_VALID(msg_type)){ + error_print("frontend_proxy_msg_process failed: message length error!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + }// Unsupported message type. + + msg_ptr = (uint8_t *)proxy_msg_hdr; + msg_ptr += PROXY_MSG_HDR_SIZE; + + if(PROXY_MSG_TYPE_DEV == msg_type){ + /* + * The frontend proxy delivers device messages from the frontend admin session to the backend proxy for the backend admin session. + */ + if (frontend_sess_id != FRONTEND_ADMIN_SESSION_ID || backend_sess_id != BACKEND_ADMIN_SESSION_ID){ + error_print("frontend_proxy_msg_process failed: only admin sessions can deliver and process device messages!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + ret = frontend_proxy_dev_msg_process(msg_ptr); + }else if(PROXY_MSG_TYPE_STRGY == msg_type){ + /* + * The frontend proxy delivers strategy messages from the frontend admin session to the backend proxy for the backend admin session. + */ + if (frontend_sess_id != FRONTEND_ADMIN_SESSION_ID || backend_sess_id != BACKEND_ADMIN_SESSION_ID){ + error_print("frontend_proxy_msg_process failed: only admin sessions can deliver and process strategy messages!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + ret = frontend_proxy_strgy_msg_process(msg_ptr); + }else if(PROXY_MSG_TYPE_SESS == msg_type){ + ret = frontend_proxy_sess_msg_process(frontend_sess_id, backend_sess_id, msg_ptr); + }else{ +/* + * When msg_type is PROXY_MSG_TYPE_DATA, the frontend_sess_id and backend_sess_id should be checked to determine whether the session (if it exists) + * is an application session. + */ + if(!APP_SESSION_ID_VALID(frontend_sess_id) || !APP_SESSION_ID_VALID(backend_sess_id)){ + error_print("Both the frontend session ID and backend session ID in the proxy data message must pass the application session ID validation!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + ret = frontend_proxy_data_msg_prosess(frontend_sess_id, backend_sess_id, msg_len, msg_ptr); + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/* + * Device message processing functions. + * frontend_proxy_dev_msg_process + * |->frontend_proxy_dev_msg_process_ver1 + * |->frontend_proxy_dev_msg_process_disable_ver1 + * |->frontend_proxy_dev_msg_process_enable_ver1 + * |->frontend_proxy_dev_msg_process_query_ver1 + */ +int frontend_proxy_dev_msg_process(uint8_t *msg){ + DevMsgHeader *dev_msg_hdr; + uint16_t version, msg_id, payload_len; + DevMsgType msg_type; + ActionType action_type; + int ret; + uint8_t *msg_data; + + ret = FRONTEND_PROXY_PROCESS_ERROR; + + dev_msg_hdr = (DevMsgHeader *)msg; + + if(NULL == dev_msg_hdr){ + error_print("frontend_proxy_dev_msg_process() failed: the msg pointer is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + version = dev_msg_hdr->version; + msg_type = dev_msg_hdr->msg_type; + msg_id = dev_msg_hdr->msg_id; + action_type = dev_msg_hdr->action_type; + payload_len = dev_msg_hdr->payload_len; + msg_data = msg + sizeof(DevMsgHeader); + +/* + * The frontend protocol stack only processes messages where the action_type is ACTION_TYPE_RESPONSE. + */ + if(ACTION_TYPE_RESPONSE != action_type){ + error_print("frontend_proxy_dev_msg_process() failed: the frontend protocol stack only processes the device message of the ACTION_TYPE_RESPONSE type!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } +/* + * Before processing device messages, the protocol stack should check the validity of parameters. + * + * Currently, the protocol stack only processes Version 1 device messages. + */ + + if(PROXY_PROTO_DEV_VERSION_1 == version){ + ret = frontend_proxy_dev_msg_process_ver1(msg_type, msg_id, action_type, payload_len, msg_data); + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_dev_msg_process_ver1(uint16_t msg_type, uint16_t msg_id, uint16_t action_type, uint16_t payload_len, uint8_t *msg_payload){ + int corr_len; + int ret = FRONTEND_PROXY_PROCESS_ERROR; + +/* + * Check whether the payload length matches the message type and signaling type. + */ + corr_len = DEV_MSG_PAYLOAD_LEN(msg_type, action_type); + + utils_print("In %s, corr_len = %d, payload_len = %d\n", __func__, corr_len, payload_len); + + if(PROXY_MSG_INVALID_LEN == corr_len || corr_len != payload_len){ + error_print("frontend_proxy_dev_msg_process_ver1() failed: invalid msg_type or payload length mismatch!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + switch(msg_type) { + case DEV_MSG_DISABLE: + ret = frontend_proxy_dev_msg_process_disable_ver1(payload_len, msg_payload); + break; + case DEV_MSG_ENABLE: + ret = frontend_proxy_dev_msg_process_enable_ver1(payload_len, msg_payload); + break; + case DEV_MSG_QUERY: + ret = frontend_proxy_dev_msg_process_query_ver1(payload_len, msg_payload); + break; + default: +/* + * Nothing to do, because the validation of the msg_type is checked before. + */ + break; + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_dev_msg_process_disable_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_dev_msg_process_enable_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_dev_msg_process_query_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/* + * Strategy message processing functions. + * frontend_proxy_strgy_msg_process + * |->frontend_proxy_strgy_msg_process_ver1 + * |->frontend_proxy_strgy_msg_process_set_ver1 + * |->frontend_proxy_strgy_msg_process_query_ver1 + */ + +int frontend_proxy_strgy_msg_process(uint8_t *msg){ + StrgyMsgHeader *strgymsg_hdr; + uint16_t version, msg_id, payload_len; + StrgyMsgType msg_type; + ActionType action_type; + int ret; + uint8_t *msg_data; + + ret = FRONTEND_PROXY_PROCESS_ERROR; + + if(NULL == strgymsg_hdr){ + error_print("frontend_proxy_strgy_msg_process() failed: the msg pointer is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + version = strgymsg_hdr->version; + msg_type = strgymsg_hdr->msg_type; + msg_id = strgymsg_hdr->msg_id; + action_type = strgymsg_hdr->action_type; + payload_len = strgymsg_hdr->payload_len; + msg_data = msg + sizeof(StrgyMsgHeader); + + +/* + * The frontend protocol stack only processes messages where the action_type is ACTION_TYPE_RESPONSE. + */ + if(ACTION_TYPE_RESPONSE != action_type){ + error_print("frontend_proxy_strgy_msg_process() failed: the frontend protocol stack only processes the strategy messages of type ACTION_TYPE_RESPONSE!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* + * Before processing device messages, the protocol stack should check the validity of parameters. + * + * Currently, the protocol stack only processes Version 1 strategy messages. + */ + if(PROXY_PROTO_STRGY_VERSION_1 == version){ + ret = frontend_proxy_strgy_msg_process_ver1(msg_type, msg_id, action_type, payload_len, msg_data); + } + + return FRONTEND_PROXY_PROCESS_OK; +} + +int frontend_proxy_strgy_msg_response(uint8_t *msg); + + +int frontend_proxy_strgy_msg_process_ver1(uint16_t msg_type, uint16_t msg_id, uint16_t action_type, uint16_t payload_len, uint8_t *msg_payload){ + int corr_len; + int ret = FRONTEND_PROXY_PROCESS_ERROR; + +/* + * Check whether the payload length matches the message type and signaling type. + */ + corr_len = STRGY_MSG_PAYLOAD_LEN(msg_type, action_type); + + utils_print("In %s, corr_len = %d, payload_len = %d\n", __func__, corr_len, payload_len); + + if(PROXY_MSG_INVALID_LEN == corr_len || corr_len != payload_len){ + error_print("backend_proxy_strgy_msg_process_ver1() failed: invalid msg_type or payload length mismatch!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + switch(msg_type) { + case STRGY_MSG_SET: + ret = frontend_proxy_strgy_msg_process_set_ver1(payload_len, msg_payload); + break; + case STRGY_MSG_QUERY: + ret = frontend_proxy_strgy_msg_process_query_ver1(payload_len, msg_payload); + break; + default: +/* + * Nothing to do, because the validation of the msg_type is checked before. + */ + break; + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_strgy_msg_process_set_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + +int frontend_proxy_strgy_msg_process_query_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Processes session messages in the frontend proxy + * @details This function serves as the core handler for session-related communication between front and backend proxies. + * It handles general session message processing by parsing the incoming message, coordinating with the specified + * frontend and backend sessions, and executing appropriate operations based on message content. + * The processing follows a hierarchical call structure: + * frontend_proxy_sess_msg_process + * |-> frontend_proxy_sess_msg_process_ver1 + * |-> frontend_proxy_sess_msg_process_active_create_ver1 + * |-> frontend_proxy_sess_msg_process_close_ver1 + * @param[in] frontend_sess_id 16-bit identifier of the frontend session, used to map the message to + * the corresponding frontend session context. + * @param[in] backend_sess_id 16-bit identifier of the backend session, used to associate the message + * with the relevant backend session state and resources. + * @param[in] msg Pointer to the session message data to be processed, containing the complete + * message content (e.g., operation type, parameters, metadata). Must not be NULL. + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK on successful message processing; + * returns FRONTEND_PROXY_PROCESS_ERROR if message parsing fails, session identifiers are invalid, or + * the requested operation cannot be completed. + */ +int frontend_proxy_sess_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint8_t *msg){ + SessMsgHeader *sess_msg_hdr; + uint16_t version, payload_len; + SessMsgType msg_type; + ActionType action_type; + SessIpProtoVersion ip_version; + int ret; + uint8_t *msg_data; + + ret = FRONTEND_PROXY_PROCESS_ERROR; + + sess_msg_hdr = (SessMsgHeader *)msg; + + + if(NULL == sess_msg_hdr){ + error_print("backend_proxy_sess_msg_process() failed: the msg pointer is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + version = sess_msg_hdr->version; + msg_type = sess_msg_hdr->msg_type; + action_type = sess_msg_hdr->action_type; + ip_version = sess_msg_hdr->ip_version; + payload_len = sess_msg_hdr->payload_len; + msg_data = msg + sizeof(SessMsgHeader); + + utils_print("In %s\n", __func__); + utils_print("In %s, version = %d, msg_type = %d, action type = %d, ip version = %d, payload len = %d, address = %p\n", + __func__, version, msg_type, action_type, ip_version, payload_len, &sess_msg_hdr); + + +#if 0 +/* + * The frontend protocol stack only processes messages where the action_type is ACTION_TYPE_RESPONSE. + */ + if(ACTION_TYPE_RESPONSE != action_type){ + error_print("frontend_proxy_sess_msg_process() failed: the frontend protocol stack only processes session messages of type ACTION_TYPE_RESPONSE!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } +#endif + +/* + * Before processing device messages, the protocol stack should check the validity of parameters. + * + * Currently, the protocol stack only processes Version 1 device messages. + */ + +#if 0 + SessParaIPv4 *debug_hdr = (SessParaIPv4 *)msg_data; + SessIPv4Params *debug_hdr2 = (SessIPv4Params *)msg_data; + utils_print("In %s, type is SessParaIPv4, dev_id = %d, trans_proto = %d, port = %d\n", __func__, debug_hdr->dev_id, debug_hdr->trans_proto, debug_hdr->port); + utils_print("In %s, type is SessIPv4Params, devive_selection = %d, transport_layer_proto = %d\n", + __func__, debug_hdr2->device_selection, debug_hdr2->transport_layer_proto); +#endif + + if(PROXY_PROTO_SESS_VERSION_1 == version){ + ret = frontend_proxy_sess_msg_process_ver1(frontend_sess_id, backend_sess_id, msg_type, action_type, ip_version, payload_len, msg_data); + } + + return ret; +} + + +int frontend_proxy_sess_msg_response(uint8_t *msg); + + +int frontend_proxy_sess_msg_process_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t msg_type, + uint16_t action_type, uint16_t ip_version, uint16_t payload_len, + uint8_t *msg_payload){ + int corr_len, ret; + +/* + * Check whether the payload length matches the message type and signaling type. + */ + + utils_print("In %s\n", __func__); + + corr_len = SESS_MSG_PAYLOAD_LEN(msg_type, action_type, ip_version); + + utils_print("corr_len = %d, payload_len = %d\n", corr_len, payload_len); + + if(PROXY_MSG_INVALID_LEN == corr_len || corr_len != payload_len){ + error_print("frontend_proxy_sess_msg_process_ver1 failed: invalid msg_type or payload length mismatch!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + switch(msg_type) { + case SESS_MSG_CREATE: + if(ACTION_TYPE_RESPONSE == action_type){ + ret = frontend_proxy_sess_msg_process_active_create_ver1(frontend_sess_id, backend_sess_id, ip_version, payload_len, msg_payload); + }else if(ACTION_TYPE_COMMAND == action_type){ + ret = frontend_proxy_sess_msg_process_passive_create_ver1(frontend_sess_id, backend_sess_id, ip_version, payload_len, msg_payload); + }else{ + error_print("frontend_proxy_sess_msg_process_ver1 failed: unsupported action type (neither ACTION_TYPE_RESPONSE nor ACTION_TYPE_COMMAND)!\n"); + ret = FRONTEND_PROXY_PROCESS_ERROR; + } + + break; + case SESS_MSG_CLOSE: + ret = frontend_proxy_sess_msg_process_close_ver1(frontend_sess_id, backend_sess_id, payload_len, msg_payload); + break; + default: +/* + * Nothing to do, because the validation of the msg_type is checked before. + */ + break; + } + + return ret; +} + + +/** + * @brief Processes version 1 of **active** session creation messages in the frontend proxy + * @details This function handles the processing logic for version 1 **active** session creation messages in the frontend proxy layer. + * It parses the incoming message payload, performs parameter validation (e.g., payload length check, IP version validity), + * and executes frontend-side **active** session creation operations. It is responsible for establishing the mapping between + * the frontend session and backend session, and ensuring the correct transmission of **active** session creation details + * for the version 1 message format. + * @param[in] frontend_sess_id 16-bit identifier of the frontend session, uniquely marks the session on the frontend side + * for associating with client requests and backend session mapping + * @param[in] backend_sess_id 16-bit identifier of the backend session, used by the frontend to track and associate with + * the corresponding backend-side session instance + * @param[in] ip_version 16-bit value indicating the IP protocol version (e.g., IPv4 = 4, IPv6 = 6) adopted by the current session + * @param[in] payload_len 16-bit length of the message payload (in bytes), specifies the valid data size in the msg_payload buffer + * @param[in] msg_payload Pointer to the buffer storing the version 1 **active** session creation message payload, containing + * detailed configuration parameters required for **active** session establishment + * @return int Execution result: Typically returns FRONTEND_PROXY_PROCESS_OK on successful processing (including payload parsing, + * validation passed, and **active** session creation completed), or FRONTEND_PROXY_PROCESS_ERROR if parameter invalidation, + * payload mismatch, or **active** session creation failure occurs. + */ +int frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload){ + return __frontend_proxy_sess_msg_process_active_create_ver1(frontend_sess_id, backend_sess_id, ip_version, payload_len, msg_payload); +} + + + + +int __frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload){ + struct FrontendSessionPool *pool; + struct FrontendSessionPoolOps *sess_pool_ops; + struct FrontendSession *sess; + SessOpRespData *resp_data; +#if 0 + SessParaIPv4 *para_ipv4; + SessParaIPv6 *para_ipv6; + IPv4PortTuple *ipv4_port_tuple; + IPv6PortTuple *ipv6_port_tuple; + struct IPv4Address *ipv4_addr; + struct IPv6Address *ipv6_addr; + struct SessMsgPara sess_para; +#endif + bool ip_ver_valid = true; + int ret; + +/* + * The main body of the session creation procedure lies in the function which the create_sess_step2 pointer points to. + * In __frontend_proxy_sess_msg_process_active_create_ver1, this function parses the session parameters and calls the function pointed to by the create_sess_step2 pointer to establish a new session. + */ + pool = frontend_get_high_speed_pool(); + + if(NULL == pool || NULL == pool->ops || NULL == pool->ops->search_sess){ + error_print("__frontend_proxy_sess_msg_process_active_create_ver1 failed: the high-speed session pool is not initialized correctly!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess_pool_ops = pool->ops; + sess = sess_pool_ops->search_sess(pool, frontend_sess_id); + + if(NULL == sess){ + error_print("__frontend_proxy_sess_msg_process_active_create_ver1 failed: search session failed, no matching frontend session found in high-speed session pool.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(NULL == sess_pool_ops->create_sess_step2){ + error_print("__frontend_proxy_sess_msg_process_active_create_ver1 failed: high-speed session pool operation function 'create_sess_step2' is not initialized (NULL pointer). Session creation step 2 cannot be executed.\n"); + } + + resp_data = (SessOpRespData *)msg_payload; + ret = sess_pool_ops->create_sess_step2(pool, sess, backend_sess_id, resp_data); + + if(FRONTEND_PROXY_PROCESS_OK == ret){ + sess->event_callback(sess, FRONTEND_SESS_EVENT_CONN); + }else{ + sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNOMAL); + } + + + return ret; +} + + +/** + * @brief Processes version 1 of **passive** session creation messages in the frontend proxy + * @details This function handles the processing logic for version 1 **passive** session creation messages in the frontend proxy layer. + * It parses the incoming message payload, performs parameter validation (e.g., payload length check, IP version validity), + * and executes frontend-side **passive** session creation operations. It is responsible for establishing the mapping between + * the frontend session and backend session, and ensuring the correct transmission of **passive** session creation details + * for the version 1 message format. + * @param[in] frontend_sess_id 16-bit identifier of the frontend session, which is specifically **FRONTEND_HANDOVER_SESSION_ID** + * for associating with client requests and backend session mapping during passive session establishment + * @param[in] backend_sess_id 16-bit identifier of the backend session, used by the frontend to track and associate with + * the corresponding backend-side session instance + * @param[in] ip_version 16-bit value indicating the IP protocol version (e.g., IPv4 = SESS_IPV4_PROTO, IPv6 = SESS_IPV6_PROTO) adopted + * by the current session + * @param[in] payload_len 16-bit length of the message payload (in bytes), specifies the valid data size in the msg_payload buffer + * @param[in] msg_payload Pointer to the buffer storing the version 1 **passive** session creation message payload, containing + * detailed configuration parameters required for **passive** session establishment + * @return int Execution result: Typically returns FRONTEND_PROXY_PROCESS_OK on successful processing (including payload parsing, + * validation passed, and **passive** session creation completed), or FRONTEND_PROXY_PROCESS_ERROR if parameter invalidation, + * payload mismatch, or **passive** session creation failure occurs. + */ +int frontend_proxy_sess_msg_process_passive_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload){ + return __frontend_proxy_sess_msg_process_passive_create_ver1(frontend_sess_id, backend_sess_id, ip_version, payload_len, msg_payload); +} + + +int __frontend_proxy_sess_msg_process_passive_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload){ + FrontendEngine *eng; + struct SharedMemoryPoolQueue *rx_queue, *tx_queue; + struct FrontendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *new_sess = NULL; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + GeneralProxyMsgHeader proxy_msg_hdr; + struct SessMsgPara msg_para; + SessParaIPv4 *para_ipv4; + SessParaIPv6 *para_ipv6; + IPv4PortTuple *ipv4_port_tuple; + IPv6PortTuple *ipv6_port_tuple; + SessOpRespData resp_data; + struct IPv4Address *ipv4_addr; + uint8_t *proxy_msg; + uint32_t msg_size; + int new_sess_id = 0; + int ret = FRONTEND_PROXY_PROCESS_OK; + + eng = frontend_get_global_engine(); + + if(NULL == eng || NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: frontend engine instance has not been successfully initialized!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess_pool = eng->sess_pool; + sess_pool_ops = sess_pool->ops; + + if(SESS_IPV4_PROTO != ip_version){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: IP protocol version not supported, only IPv4 is supported currently, \ + IPv6 support will be expanded in the future!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_PARAMETER_INVALID; + goto create_sess_error; + } + +/* + * For the session-creation command sent from the backend to the frontend, + * the valid value of the frontend session ID in the message shall be FRONTEND_HANDOVER_SESSION_ID (0xFF). + */ + if(FRONTEND_HANDOVER_SESSION_ID != frontend_sess_id){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: invalid session creation command!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_PARAMETER_INVALID; + goto create_sess_error; + } + + +/* + * Alloc resource for the new creating session. + */ + new_sess = malloc(sizeof(struct FrontendSession)); + + if(NULL == new_sess){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: failed to allocate memory!"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_RESOURCE_INSUFFICIENT; + goto create_sess_error; + } + + new_sess->event_callback = default_session_event_callback; + + new_sess_id = allocate_id(&sess_pool->id_queue); + if(0 == new_sess_id){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: failed to allocate session ID!"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_RESOURCE_INSUFFICIENT; + goto create_sess_error; + } + + proxy_msg_hdr.outer_header.frontend_sess_id = new_sess_id; + + +/* + * Fill the fields of the SessMsgPara structure instance. + * This function/module is responsible for populating all required parameters into the SessMsgPara struct, + * which provides core data support for subsequent session creation operations. + */ + para_ipv4 = (SessParaIPv4 *)msg_payload; + memset(&msg_para, 0, sizeof(struct SessMsgPara)); + msg_para.frontend_sess_id = frontend_sess_id; + msg_para.backend_sess_id = backend_sess_id; + msg_para.dev_id = para_ipv4->dev_id; + msg_para.ip_version = ip_version; + msg_para.trans_proto = para_ipv4->trans_proto; + msg_para.ip_port_tuple.ipv4_port_tuple.port = para_ipv4->port; + + ipv4_addr = &msg_para.ip_port_tuple.ipv4_port_tuple.ipv4_addr; + memcpy(ipv4_addr, ¶_ipv4->ipv4_addr, sizeof(struct IPv4Address)); + + ret = sess_pool_ops->create_sess_passive(sess_pool, new_sess, &msg_para); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: "); + goto create_sess_error; + } + + return ret; + +create_sess_error: +/* + * Reclaim resources. + */ + + + if(NULL == new_sess){ + free(new_sess); + } + + if(0 != new_sess_id){ + release_id(&sess_pool->id_queue, new_sess_id); + } + + return FRONTEND_PROXY_PROCESS_ERROR; +} + +/** + * @brief Processes version 1 of session close messages in the frontend proxy + * @details This function handles the processing logic for version 1 session close messages in the frontend proxy layer. + * It parses the message payload, validates the validity of frontend and backend session identifiers, + * executes frontend-side session termination operations, and cleans up associated resources (e.g., session context, + * connection mappings). It is specifically designed for the version 1 session close message format, + * coordinating the termination of the frontend-backend associated session and ensuring proper resource release. + * @param[in] frontend_sess_id 16-bit identifier of the frontend session, used to locate and target the frontend session to be closed + * @param[in] backend_sess_id 16-bit identifier of the backend session, used to associate and notify the corresponding backend session for termination + * @param[in] payload_len 16-bit length of the message payload (in bytes), indicating the valid data size in the msg_payload buffer + * @param[in] msg_payload Pointer to the session close message payload, containing detailed parameters for the close operation (e.g., termination reason). Must not be NULL. + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK on successful processing of the close message, session termination, and resource cleanup; + * Returns FRONTEND_PROXY_PROCESS_ERROR if payload is invalid, session identifiers are incorrect, parsing fails, or session closure/resource cleanup encounters errors. + */ +int frontend_proxy_sess_msg_process_close_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t payload_len, uint8_t *msg_payload){ + return __frontend_proxy_sess_msg_process_close_ver1(frontend_sess_id, backend_sess_id, payload_len, msg_payload); +} + + + +int __frontend_proxy_sess_msg_process_close_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t payload_len, uint8_t *msg_payload){ + struct FrontendSessionPool *pool; + struct FrontendSessionPoolOps *sess_pool_ops; + struct FrontendSession *sess; + SessOpRespData *resp_data; + uint8_t status, code; + + pool = frontend_get_high_speed_pool(); + + if(NULL == pool || NULL == pool->ops || NULL == pool->ops->search_sess){ + error_print("__frontend_proxy_sess_msg_process_close_ver1 failed: the high-speed session pool is not initialized correctly!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess_pool_ops = pool->ops; + sess = sess_pool_ops->search_sess(pool, frontend_sess_id); + + if(NULL == sess){ + error_print("__frontend_proxy_sess_msg_process_close_ver1 failed: search session failed, no matching frontend session found in high-speed session pool.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + resp_data = (SessOpRespData *)msg_payload; + + sess_pool_ops->close_sess_step2(pool, sess, resp_data); + +#if 0 + resp_data = (SessOpRespData *)msg_payload; + status = resp_data->status; + + if(SESS_OP_STATUS_SUCCESS != status){ + utils_print("In %s, the session creating procedurec failed, the error code is %d\n", __func__, code); + sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNOMAL); + }else{ + sess->event_callback(sess, FRONTEND_SESS_EVENT_CLOSE); + } +#endif + + sess_pool_ops->delete_sess(pool, sess); + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Processes proxy data messages for frontend proxy (between frontend and backend sessions) + * This function handles the processing of data messages that need to be proxied between a frontend session and its + * corresponding backend session on the frontend proxy side. It involves message validation, protocol format adaptation, + * and routing to the target session (frontend or backend) based on the provided session identifiers and message content, + * ensuring seamless data transmission across the proxy link. + * @param frontend_sess_id Unique identifier of the frontend session (source/destination of the message) + * @param backend_sess_id Unique identifier of the backend session (counterpart session for proxying) + * @param data_len Length of the message data in bytes (specifies valid range of the msg buffer) + * @param msg Pointer to the message data buffer (uint8_t array) to be processed/proxied + * @return int Processing result status: + * FRONTEND_PROXY_PROCESS_OK: Message processed and proxied successfully + * FRONTEND_PROXY_PROCESS_ERROR: Failed to process or proxy the message (e.g., invalid session IDs, + * invalid message format, protocol adaptation failure, or forwarding failure) + * @note 1. The message buffer (msg) is assumed to contain valid data conforming to the proxy protocol; its length + * should match the actual data length specified by data_len to avoid out-of-bounds access; + * Callers must ensure frontend_sess_id and backend_sess_id refer to active, valid sessions (the frontend + * session is managed by the frontend proxy, and the backend session is the associated counterpart), + * otherwise processing errors will occur; + * This function does not take ownership of the msg buffer; the caller is responsible for managing its + * lifecycle (e.g., allocation and release) to prevent memory leaks. + */ +int frontend_proxy_data_msg_prosess(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t data_len, uint8_t *msg){ +/* + * STEP 1. Search for the destination frontend session instance in the session pool using backend_sess_id. If it fails to find + * the appropriate session instance, frontend_proxy_data_msg_process shall return FRONTEND_PROXY_PROCESS_ERROR; + * otherwise, proceed to STEP 2. + * STEP 2. Construct a struct SessMsgSeg object, bind the data message to this SessMsgSeg object, and then link this SessMsgSeg object + * to the msg_b2f queue of the session instance. + * STEP 3. Link the frontend session instance to the queue_b2f of the session pool instance to which the session instance belongs. + * The frontend proxy protocol will process all sessions in queue_b2f and forward all data messages in each msg_b2f queue after + * it receives all the data messages in the shared-memory queue. This procedure exists outside frontend_proxy_data_msg_process; + * we just make a note here to help readers maintain a consistent understanding. + */ + struct FrontendEngine_ *eng; + struct FrontendSessionPool *s_pool; + struct FrontendSessionPoolOps *ops; + struct FrontendSession *sess; + struct SharedMemoryPool *mem_pool; + struct SessMsgSeg *msg_seg; + int ret; + + eng = frontend_get_global_engine(); + + if(NULL == eng || NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("frontend_proxy_data_msg_prosess failed: eng, eng->sess_pool, or eng->sess_pool->ops is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + s_pool = eng->sess_pool; + ops = eng->sess_pool->ops; + mem_pool = eng->mem_pool; + + if(NULL == ops->search_sess){ + error_print("frontend_proxy_data_msg_prosess failed: ops->search_sess (session searching function) is not initialized!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess = ops->search_sess(s_pool, frontend_sess_id); + + if(NULL == sess){ + error_print("frontend_proxy_data_msg_prosess failed: no frontend session found for the specified frontend_sess_id!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + msg_seg = sess_msg_seg_alloc(data_len, SESS_MSG_SEG_DYNAMIC_ALLOC, msg, mem_pool); + + if(NULL == msg_seg){ + error_print("frontend_sess_send failed: insufficient memory for allocating message segment instance!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + utils_print("In %s, after sess_msg_seg_alloc\n", __func__); +/* + * Insert the message segment into the back-to-front message queue. + */ + SESS_MSG_SEG_INSERT_QUEUE(sess, msg_seg, b2f); + utils_print("In %s, after SESS_MSG_SEG_INSERT_QUEUE\n", __func__); + + FRONTEND_SESS_LINK_TO_QUEUE(sess, b2f); + + utils_print("In %s, after FRONTEND_SESS_LINK_TO_QUEUE\n", __func__); + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_data_msg_recv(struct FrontendSession *sess, uint8_t *msg); +int frontend_proxy_data_msg_send(struct FrontendSession *sess, uint8_t *msg); \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c new file mode 100644 index 0000000..f468d93 --- /dev/null +++ b/projects/sel4test/apps/front/src/main.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "frontend_api.h" +#include "senario_test.h" + +extern void tailq_test(); +extern void uthash_test(); +int main(void){ + printf("Running tests\n"); + FrontendEngine *eng; + struct FrontendSession *sess; + int ret; +#if 0 + seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); + unsigned long long *vaddrs=(unsigned long long *)*Ptr2ShmemCommBuff; + int* a = malloc(10*sizeof(int)); + *a = 1; + printf("a = %d, addr of a = %p\n", *a, a); + tailq_test(); + uthash_test(); +#endif + frontend_engine_init(); + + eng = frontend_get_global_engine(); + sess = frontend_sess_new(eng); + ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.100:8080"); + + test_proxy_scenario_multi_type_msg_build_frontend(eng); + test_proxy_scenario_msg_read_from_rx_queue_frontend(eng); + + return 0; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c new file mode 100644 index 0000000..27c7c76 --- /dev/null +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -0,0 +1,445 @@ +#include "senario_test.h" + +/* + * GeneralProxyMsgHeader dev_enable_msg_hdr, strgy_query_msg_hdr, sess_create_msg_hdr, data_msg_hdr; + */ +GeneralProxyMsgHeader dev_enable_msg_hdr = { + .outer_header = { + .version = PROXY_PROTO_VERSION_1, // Protocol version, fixed to 1 as specified + .proxy_msg_type = PROXY_MSG_TYPE_DEV, // Proxy message type: Device message (0) + .frontend_sess_id = FRONTEND_ADMIN_SESSION_ID, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy + .backend_sess_id = BACKEND_ADMIN_SESSION_ID, // Backend admin session ID, used to match backend-backend sessions in frontend proxy + .payload_len = sizeof(DevMsgHeader) + sizeof(DevMsgReport) // Payload length, set according to actual payload + }, + .inner_header.dev_hdr = { // Use device message inner header + .version = PROXY_PROTO_DEV_VERSION_1, // Protocol version, fixed to 1 + .msg_type = DEV_MSG_ENABLE, // Message type: Enable (1) + .msg_id = 0, // Message ID, used for command-response matching + .action_type = ACTION_TYPE_RESPONSE, // Signaling type: Response (1) + .payload_len = sizeof(DevMsgReport) // Payload length, set according to actual payload + } +}; + + +// Strategy set message header +GeneralProxyMsgHeader strgy_set_msg_hdr = { + .outer_header = { + .version = PROXY_PROTO_VERSION_1, // Protocol version, fixed to 1 as specified + .proxy_msg_type = PROXY_MSG_TYPE_STRGY, // Proxy message type: Strategy message (1) + .frontend_sess_id = FRONTEND_ADMIN_SESSION_ID, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy + .backend_sess_id = BACKEND_ADMIN_SESSION_ID, // Backend admin session ID, used to match backend-backend sessions in frontend proxy + .payload_len = sizeof(StrgyMsgHeader) + sizeof(StrgyMsgReport) // Payload length, set according to actual payload + }, + .inner_header.strgy_hdr = { // Use strategy message inner header + .version = PROXY_PROTO_STRGY_VERSION_1, // Protocol version, fixed to 1 + .msg_type = STRGY_MSG_SET, // Message type: Set (0) + .msg_id = 0, // Message ID, used for command-response matching + .action_type = ACTION_TYPE_RESPONSE, // Signaling type: Response (0) + .payload_len = sizeof(StrgyMsgReport) // Payload length, set according to actual payload + } +}; + + +// Session create message header +GeneralProxyMsgHeader sess_create_msg_hdr = { + .outer_header = { + .version = PROXY_PROTO_VERSION_1, // Protocol version, fixed to 1 as specified + .proxy_msg_type = PROXY_MSG_TYPE_SESS, // Proxy message type: Session message (2) + .frontend_sess_id = 1, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy + .backend_sess_id = 1, // Backend admin session ID, used to match backend-backend sessions in backend proxy + .payload_len = sizeof(SessMsgHeader) + sizeof(SessOpRespData) // Payload length, set according to actual payload + }, + .inner_header.sess_hdr = { // Use session message inner header + .version = PROXY_PROTO_SESS_VERSION_1, // Protocol version, fixed to 1 + .msg_type = SESS_MSG_CREATE, // Message type: Create (1) + .action_type = ACTION_TYPE_RESPONSE, // Signaling type: RESPONSE (1) + .ip_version = SESS_IPV4_PROTO, // IP version: IPv4 (4), can be changed to IPv4 (6) if needed + .payload_len = sizeof(SessOpRespData) // Payload length, set according to actual payload + } +}; + + +// data message header +GeneralProxyMsgHeader proxy_data_msg_hdr = { + .outer_header = { + .version = PROXY_PROTO_VERSION_1, // Protocol version, fixed to 1 as specified + .proxy_msg_type = PROXY_MSG_TYPE_DATA, // Proxy message type: Session message (3) + .frontend_sess_id = 1, // Frontend session ID, used to match frontend-backend sessions in frontend proxy + .backend_sess_id = 1, // Backend session ID, used to match backend-backend sessions in frontend proxy + .payload_len = 0 // Payload length, set according to actual payload + }, +}; + +/** + * @brief Simulate backend-end service responses, construct proxy messages via build_proxy_general_message, and inject them into the shared memory RX queue of the back-end engine + * @details Implements the core responsibility of "scenario functions" in the "Backend Protocol Stack Unit Test.doc": + * 1. Calls the existing build_proxy_general_message to construct complete proxy messages (supports both shared memory and caller-allocated memory modes); + * 2. Injects the constructed messages into the front-end engine's shared memory RX queue in compliance with FIFO read-write rules (avoids queue overflow or data overwriting); + * 3. Returns detailed injection results to locate issues like queue initialization exceptions or message construction failures, + * and triggers the front-end engine's request processing logic (e.g., engine_run reads from RX queue). + * + * @param[in] engine Pointer to a FrontendEngine object containing the front-end proxy's global context (e.g., runtime configuration, memory pool handles). + * Required by build_proxy_general_message for message construction (e.g., accessing memory management resources). + * Must not be NULL (consistent with build_proxy_general_message's parameter constraint). + * @param[in] msg_header Pointer to a GeneralProxyMsgHeader structure specifying the header of the injected message (e.g., message type, payload length). + * Must not be NULL (passed to build_proxy_general_message as the "header" parameter) and comply with the protocol specification in the document. + * @param[in] msg_payload Pointer to the const uint8_t buffer containing the message payload (business data like device status, strategy config). + * Can be NULL only if msg_payload_len is 0 (consistent with build_proxy_general_message's payload constraint). + * @param[in] msg_payload_len Length of msg_payload in bytes. Must be non-negative and match msg_header->payload_len (if the header has a payload length field) + * to ensure message consistency (follows build_proxy_general_message's parameter rule). + * @param[in] alloc_mode Memory allocation mode for message construction, using the same MemoryAllocMode enum as build_proxy_general_message: + * - MEMORY_ALLOC_SHARED: Allocates memory in the shared memory FIFO ring buffer (rx_queue acts as ring_buf for build_proxy_general_message); + * - MEMORY_ALLOC_CALLER: Requires the caller to pre-allocate memory (build_proxy_general_message populates the pre-allocated buffer). + * @param[out] result_msg Double pointer to receive the address of the constructed message (consistent with build_proxy_general_message's "result_msg" parameter): + * - MEMORY_ALLOC_SHARED: Points to the message in the rx_queue's FIFO ring buffer (no caller deallocation needed); + * - MEMORY_ALLOC_CALLER: Points to the caller's pre-allocated buffer (populated with the complete message). + * Must not be NULL. + * @param[out] result_desc Buffer to store detailed injection results (e.g., "Request injected successfully into RX queue", "RX queue full, injection failed"). + * Helps locate test startup issues (document's "injection status feedback" requirement). Must not be NULL. + * @param[in] desc_len Maximum length of the result_desc buffer to prevent buffer overflow (ensures safe result storage). + * + * @return int Status code following the back-end protocol stack's unified specification (consistent with build_proxy_general_message's return type): + * - BACKEND_PROXY_PROCESS_OK: Message constructed successfully and injected into RX queue; + * - BACKEND_PROXY_PROCESS_ERROR: General error (e.g., build_proxy_general_message fails, rx_queue uninitialized); + * + * @note 1. Depends on the existing build_proxy_general_message function (must ensure its correctness before using this injection function); + * 2. Before injection, confirm FrontendEngine is initialized (engine_init returns BACKEND_PROXY_PROCESS_OK) and rx_queue is ready (document's test prerequisite); + * 3. For MEMORY_ALLOC_SHARED, ensure rx_queue is a valid SharedMemoryPoolQueue (used for FIFO ring buffer operations in build_proxy_general_message); + * 4. For MEMORY_ALLOC_CALLER, the caller must guarantee the pre-allocated buffer (result_msg) is large enough to hold the complete message (header + payload) + * (follows build_proxy_general_message's constraint for this mode); + * 5. Supports all message types defined in the protocol document by configuring msg_header->message_type. + */ +int scenario_msg_inject_frontend(FrontendEngine *engine, + GeneralProxyMsgHeader *msg_header, + const uint8_t *msg_payload, + size_t msg_payload_len, + MemoryAllocMode alloc_mode, + uint8_t **result_msg, + char *result_desc, + size_t desc_len){ + int ret; + + // 1. Check if BackendEngine pointer is NULL (engine initialization is a prerequisite per "Backend Protocol Stack Unit Test.doc") + if (engine == NULL) + { + error_print("scenario_msg_inject_frontend failed: BackendEngine pointer is NULL, engine must be initialized!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 2. Check if engine->rx_queue (shared memory RX queue) is NULL (queue initialization is required for message injection, per document) + if (engine->rx_queue == NULL) + { + error_print("scenario_msg_inject_frontend failed: engine->rx_queue (SharedMemoryPoolQueue) is NULL, RX queue must be initialized via engine_init_shared_mem_queue"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 3. Check if GeneralProxyMsgHeader pointer is NULL (valid header is required for message parsing, per document's message handling rules) + if (msg_header == NULL) + { + error_print("scenario_msg_inject_frontend failed: GeneralProxyMsgHeader pointer is NULL, valid message header is required"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 4. Check if result_msg double pointer is NULL (used to store constructed message address, per document's scenario function requirements) + if (result_msg == NULL) + { + error_print("scenario_msg_inject_frontend failed: result_msg double pointer is NULL, cannot store address of constructed message"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 5. Check if result_desc pointer is NULL (used to feedback injection status, per document's "injection status feedback" requirement) + if (result_desc == NULL) + { + error_print("scenario_msg_inject_frontend failed: result_desc pointer is NULL, cannot store injection result description"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 6. Check consistency between msg_payload and msg_payload_len (empty payload requires len=0, per document's data message integrity rules) + if (msg_payload == NULL && msg_payload_len > 0) + { + error_print("scenario_msg_inject_frontend failed: msg_payload is NULL but msg_payload_len > 0, violates payload integrity rules"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 7. Check if MemoryAllocMode is valid (covers undefined modes, aligns with document's "shared memory/caller-allocated dual mode") + if (alloc_mode != MEMORY_ALLOC_SHARED && alloc_mode != MEMORY_ALLOC_CALLER) + { + error_print("scenario_msg_inject failed: invalid MemoryAllocMode, only MEMORY_ALLOC_SHARED and MEMORY_ALLOC_CALLER are supported"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 8. Check if desc_len is valid (prevents buffer overflow when writing result_desc, per safe coding practices in document context) + if (desc_len == 0) + { + error_print("scenario_msg_inject failed: desc_len is 0, insufficient buffer size for result_desc"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* ------------------------------ + * Normal logic starts here (e.g., call build_proxy_general_message, inject to engine->rx_queue) + *------------------------------ + * Example: Initialize result_desc with success info first (if no errors) + * strncpy(result_desc, "scenario_msg_inject: request injection started", desc_len - 1); + * result_desc[desc_len - 1] = '\0'; + * ... (call build_proxy_general_message, handle queue injection, etc.) + */ + + utils_print("In %s, before enter build_proxy_general_message, build message type = %d, msg_payload_len = %d\n", __func__, msg_header->outer_header.proxy_msg_type, msg_payload_len); + ret = build_proxy_general_message(engine, msg_header, msg_payload, msg_payload_len, result_msg, alloc_mode, engine->rx_queue); + + return ret; +} + + +/** + * @brief Inject a device message into the frontend engine + * @details Handles injection of device-related messages, processing according to + * device management logic and returning results via output parameters. + * + * @param engine Pointer to the BackendEngine instance + * @return int Return code indicating processing result: + * - FRONTEND_PROXY_PROCESS_OK: Message injected and processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to inject or process the message + */ +int device_msg_inject_frontend(FrontendEngine *engine){ + GeneralProxyMsgHeader *dev_msg_hdr; + DevMsgReport dev_msg_resp; + int ret, desc_len = 100; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + + dev_msg_hdr = &dev_enable_msg_hdr; + + dev_msg_resp.status = SESS_OP_STATUS_SUCCESS; + dev_msg_resp.error = SESS_OP_CODE_SUCCESS; + dev_msg_resp.data = 0xFF; + + + res_string = res_buf; + desc_string = desc_buf; + + utils_print("In %s, before enter scenario_msg_inject\n", __func__); + ret = scenario_msg_inject_frontend(engine, dev_msg_hdr, &dev_msg_resp, sizeof(dev_msg_resp), MEMORY_ALLOC_SHARED, res_string, desc_string, desc_len); + + return ret; +} + + + +/** + * @brief Inject a strategy message into the frpmtend engine + * @details Handles injection of strategy/policy-related messages, processing according to + * strategy management logic and returning results via output parameters. + * + * @param engine Pointer to the BackendEngine instance + * @return int Return code indicating processing result: + * - FRONTEND_PROXY_PROCESS_OK: Message injected and processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to inject or process the message + */ +int strategy_msg_inject_frontend(FrontendEngine *engine){ + GeneralProxyMsgHeader *strgy_msg_hdr; + StrgyMsgReport strgy_resp; + int ret, desc_len = 100; + + uint8_t **res_string; + + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + + + strgy_msg_hdr = &strgy_set_msg_hdr; + strgy_resp.error = STRGY_OP_STATUS_SUCCESS; + strgy_resp.status = STRGY_OP_CODE_SUCCESS; + res_string = res_buf; + desc_string = desc_buf; + + ret = scenario_msg_inject_frontend(engine, strgy_msg_hdr, &strgy_resp, sizeof(strgy_resp), MEMORY_ALLOC_SHARED, res_string, desc_string, desc_len); + + return ret; +} + + +/** + * @brief Inject a session message into the frontend engine + * @details Handles injection of session-related messages, processing according to + * session management logic and returning results via output parameters. + * + * @param engine Pointer to the BackendEngine instance + * @return int Return code indicating processing result: + * - FRONTEND_PROXY_PROCESS_OK: Message injected and processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to inject or process the message + */ +int session_msg_inject_frontend(FrontendEngine *engine){ + GeneralProxyMsgHeader *sess_msg_hdr; + SessOpRespData sess_msg_resp; + int ret, desc_len = 100; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + + sess_msg_hdr = &sess_create_msg_hdr; + sess_msg_resp.status = SESS_OP_STATUS_SUCCESS; + sess_msg_resp.code = SESS_OP_CODE_SUCCESS; + res_string = res_buf; + desc_string = desc_buf; + + ret = scenario_msg_inject_frontend(engine, sess_msg_hdr, &sess_msg_resp, sizeof(sess_msg_resp), MEMORY_ALLOC_SHARED, res_string, desc_string, desc_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Inject a data message into the frontend engine + * @details Handles injection of data/content-related messages, processing according to + * data processing logic and returning results via output parameters. + * + * @param engine Pointer to the BackendEngine instance + * @return int Return code indicating processing result: + * - FRONTEND_PROXY_PROCESS_OK: Message injected and processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to inject or process the message + */ +int data_msg_inject_frontend(FrontendEngine *engine){ + GeneralProxyMsgHeader *data_msg_hdr; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + char data_buf[100]; + int ret, desc_len = 100; + + memset(data_buf, 0, sizeof(data_buf)); + snprintf(data_buf, sizeof(data_buf), "test msg"); + + utils_print("strlen(test msg) = %d\n", strlen("test msg")); + utils_print("content of data_buf = %s\n", data_buf); + + data_msg_hdr = &proxy_data_msg_hdr; + res_string = res_buf; + desc_string = desc_buf; + + data_msg_hdr->outer_header.payload_len = strlen("test msg"); + + utils_print("outer_header.payload_len = %d\n", data_msg_hdr->outer_header.payload_len); + utils_print("In %s, version = %d, type = %d\n", __func__, data_msg_hdr->outer_header.version, data_msg_hdr->outer_header.proxy_msg_type); + DUMP_BUFFER_CONTENT(data_buf, 8, "%c"); + + ret = scenario_msg_inject_frontend(engine, data_msg_hdr, data_buf, strlen(data_buf), MEMORY_ALLOC_SHARED, res_string, desc_string, desc_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int test_proxy_scenario_multi_type_msg_build_frontend(FrontendEngine *engine){ + device_msg_inject_frontend(engine); + strategy_msg_inject_frontend(engine); + session_msg_inject_frontend(engine); + data_msg_inject_frontend(engine); + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Upper-layer scenario-based test function for the frontend proxy layer, which uniformly reads multi-type proxy messages from the shared memory RX queue + * and simulates the data reading process. + * @details Designed based on the core responsibilities of "Scenario Functions", serving as a standardized entry for unit testing: + * Automatically reads all core proxy message types that may exist in the queue, including device messages, strategy messages, session messages, and data messages + * (the actual types and quantities are uncertain, depending on the injected content); + * Calls the underlying message reading functions (e.g., frontend_engine_rx_queue_get, scenario_msg_read) to complete the parsing and extraction of structured messages; + * Obtains the shared memory RX queue handle from the input FrontendEngine global context (which must be initialized in advance), and reads messages from the queue following FIFO rules; + * Does not require external input of message parameters (e.g., expected msg type, msg ID). All test verification logic (such as checking message structure validity, matching preset parameters) is + * based on the test scenarios (e.g., verifying that session messages contain preset session ID = 1, data messages match preset payload length) to ensure test consistency. + * @param[in] engine Pointer to the FrontendEngine global context, which must meet the requirements in the document: + * Must be successfully initialized via the engine_init function (returning FRONTEND_PROXY_PROCESS_OK) to ensure that resources such as the memory pool handle and + * shared memory RX queue (engine->rx_queue) are ready + * The context must contain valid runtime configurations (e.g., shared memory queue size, message parsing rules) to avoid resource unavailability errors during message reading or parsing + * @return int Follows the unified error code specification in the document, with return value meanings as follows: + * FRONTEND_PROXY_PROCESS_OK: All existing types of proxy messages in the RX queue are successfully read and parsed; + * FRONTEND_PROXY_PROCESS_ERROR: All abnormal scenarios are covered, including uninitialized FrontendEngine, NULL internal resources (e.g., rx_queue), empty shared memory RX queue (when messages + * are expected), and failed calls to underlying message reading/parsing functions + * @note 1. Precondition: The engine must be initialized before calling this function, and messages must be pre-injected into the RX queue (e.g., via test_proxy_scenario_multi_type_msg_build_frontend) (refer to the normal scenario process in "Test Process - Message Reading Test" of "Frontend Proxy Layer Unit Test.doc"), otherwise a process error will be returned directly; + * Message coverage: The current version supports reading 4 core message types, which may exist in any combination (one or more types) depending on the injection scenario. + * Device messages: Verify consistency with preset "device status query" commands; + * Strategy messages: Verify consistency with preset "query strategy configuration" commands (matching the "strategy configuration command parsing" scenario); + * Session messages: Verify consistency with preset "create session" (valid device ID) and "close session" (valid session ID) commands (matching the "session command parsing" scenario); + * Data messages: Verify consistency with preset boundary scenarios (empty payload and maximum-length payload (4088 bytes)); + * Result feedback: The function internally uses error_print to print detailed logs of message reading/parsing (e.g., "Device message read successfully", "Data message parsing failed: + * invalid payload length"), + * and the log format complies with the log specification for "message processing failure"; + * Subsequent verification: After message reading is completed, the function may trigger internal verification logic (e.g., checking message count, comparing with injected content) to confirm + * the correctness of the reading process, thus completing the full test link of "inject-read-verify". + */ +int test_proxy_scenario_msg_read_from_rx_queue_frontend(FrontendEngine *engine){ + struct SharedMemoryPoolQueue *rx_queue, *tx_queue; + struct BackendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *cur_sess, *next_sess; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + uint8_t *proxy_msg, **p_proxy_msg; + uint8_t content[8]; + ProxyMsgHeader *proxy_msg_hdr; + uint32_t msg_size; + int ret; + + rx_queue = engine->rx_queue; +// proxy_msg = content; + p_proxy_msg = &proxy_msg; + + if(NULL == rx_queue){ + error_print("test_proxy_scenario_msg_read_from_rx_queue_frontend failed: the rx_queue of the engine is not initialized!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +#if 1 + proxy_msg = frontend_engine_rx_queue_get_msg(rx_queue, PROXY_MSG_HDR_PLUS_MAX_SIZE, &ret, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get_msg\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + frontend_proxy_msg_process(proxy_msg); + + proxy_msg = frontend_engine_rx_queue_get_msg(rx_queue, PROXY_MSG_HDR_PLUS_MAX_SIZE, &ret, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get_msg\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + frontend_proxy_msg_process(proxy_msg); + + proxy_msg = frontend_engine_rx_queue_get_msg(rx_queue, PROXY_MSG_HDR_PLUS_MAX_SIZE, &ret, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get_msg\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + frontend_proxy_msg_process(proxy_msg); + + proxy_msg = frontend_engine_rx_queue_get_msg(rx_queue, PROXY_MSG_HDR_PLUS_MAX_SIZE, &ret, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get_msg\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + frontend_proxy_msg_process(proxy_msg); +#endif + +#if 0 + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + +// DUMP_BUFFER_CONTENT(proxy_msg, msg_size, "%d"); + + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); +// frontend_proxy_msg_process(proxy_msg); + + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + + + +#endif + return FRONTEND_PROXY_PROCESS_OK; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c new file mode 100644 index 0000000..52ef346 --- /dev/null +++ b/projects/sel4test/apps/front/src/session.c @@ -0,0 +1,246 @@ + +#include "session.h" +#include "common_utils.h" + +/** + * @brief Allocates and initializes a SessMsgSeg structure + * + * @param len Length of the data buffer (in bytes) + * @param type Memory source type of the data buffer (dynamic allocation or shared memory) + * @param shared_data Pointer to shared memory data (valid only when data_src is SESS_MSG_SEG_SHARED_MEM) + * @param mem_pool Pointer to the SharedMemoryPool instance (valid only when type is SESS_MSG_SEG_SHARED_MEM). + Used to associate the SessMsgSeg with the shared memory pool for management (e.g., validation, release tracking). + * + * @return Pointer to the newly allocated SessMsgSeg on success; NULL on failure + * + * @note - If data_src is SESS_MSG_SEG_DYNAMIC_ALLOC: allocates data buffer with malloc() + * - If data_src is SESS_MSG_SEG_SHARED_MEM: uses shared_data directly (does not allocate new memory) + * - Initializes TAILQ entry to default state + */ +struct SessMsgSeg *sess_msg_seg_alloc(size_t len, SessMsgSegType type, uint8_t *shared_data, struct SharedMemoryPool *mem_pool){ + struct SessMsgSeg *msg_seg; + msg_seg = malloc(sizeof(struct SessMsgSeg)); + + if(NULL == msg_seg){ + error_print("sess_msg_seg_alloc failed: insurficient memory resource for message segment!"); + return NULL; + } + + switch(type) { + case SESS_MSG_SEG_DYNAMIC_ALLOC: + msg_seg->data = malloc(len); + + if(NULL == msg_seg->data){ + error_print("sess_msg_seg_alloc failed: insurficient memory resource for message data!"); + goto msg_alloc_error; + } + break; + case SESS_MSG_SEG_SHARED_MEM: +/* + * The memory for storing data is prealloc from the memory pool. + */ + if(NULL == mem_pool){ + error_print("sess_msg_seg_alloc failed: Shared memory pool (mem_pool) cannot be NULL when using SESS_MSG_SEG_SHARED_MEM type!"); + goto msg_alloc_error; + } + + msg_seg->data = shared_data; + msg_seg->mem_pool = mem_pool; + break; + + default: +/* + * Unsupported message segment type. + */ + error_print("sess_msg_seg_alloc failed: unsupported message segment type!"); + goto msg_alloc_error; + } + + msg_seg->type = type; + msg_seg->len = len; + + return msg_seg; +msg_alloc_error: + free(msg_seg); + return NULL; +} + + + +/** + * @brief Lightweight allocation and initialization of a SessMsgSeg structure + * + * A minimal version of message segment allocation that only initializes the core type field, + * with no data buffer allocation or data copy operations, ensuring ultra-low overhead. + * + * @param type Type of the message segment (must be a valid enumeration value < SESS_MSG_SEG_TYPE_MAX) + * + * @return Pointer to the newly allocated and initialized SessMsgSeg on success; NULL on failure + * (e.g., invalid type, insufficient memory for the SessMsgSeg structure itself) + * + * @note - Lightweight design: Does not allocate data buffers, not associate shared data, + * and only initializes the `type` field and default state of basic structure members. + * - Default initializations: The `len` field is set to 0, `shared_data` to NULL, + * reference count (`ref_cnt`) to 1, and linked list pointer (`next`) to NULL. + * - If data association is required (e.g., binding shared data), use supplementary interfaces + * or the full-version `sess_msg_seg_alloc()` function. + * - The structure's memory is allocated via the default lightweight memory mechanism + * (no external memory pool dependency, ensuring minimal allocation overhead). + */ +struct SessMsgSeg* sess_msg_seg_alloc_lite(SessMsgSegType type){ + struct SessMsgSeg *msg_seg; + + if(SESS_MSG_SEG_DYNAMIC_ALLOC != type || SESS_MSG_SEG_SHARED_MEM != type){ + error_print("sess_msg_seg_alloc failed: invalid! message segment type!\n"); + return NULL; + } + + msg_seg = malloc(sizeof(struct SessMsgSeg)); + + if(NULL == msg_seg){ + error_print("sess_msg_seg_alloc failed: insurficient memory resource for message segment!\n"); + return NULL; + } + + msg_seg->type = type; + + return msg_seg; +} + +/** + * @brief Releases a SessMsgSeg structure and its associated resources + * + * @param seg_ptr Double pointer to the SessMsgSeg to be released (will be set to NULL after release) + * + * @note - If type is SESS_MSG_SEG_DYNAMIC_ALLOC: frees the data buffer with free() + * - If type is SESS_MSG_SEG_SHARED_MEM: does not free data (managed by shared memory system) + * - Safely handles NULL input (no operation performed) + */ +void sess_msg_seg_free(struct SessMsgSeg **seg_ptr){ + struct SessMsgSeg *msg_seg; + + if(NULL == seg_ptr || NULL == *seg_ptr){ + error_print("sess_msg_seg_free failed: input pointer is invalid!"); + return; + } + + msg_seg = *seg_ptr; + + + switch(msg_seg->type) { + case SESS_MSG_SEG_DYNAMIC_ALLOC: + if(NULL == msg_seg->data){ + return; + } + free(msg_seg->data); + break; + case SESS_MSG_SEG_SHARED_MEM: +/* + * The memory belongs to the shared memory pool. + */ + break; + + default: +/* + * Unsupported message segment type. + */ + error_print("sess_msg_seg_free failed: unsupported message segment type!"); + } + + free(msg_seg); +} + + +/** + * @brief Release all SessMsgSeg elements in the SessMsgQueue + * + * This function traverses the SessMsgQueue, releases each SessMsgSeg element + * according to its memory type, and finally clears the queue. + * + * For dynamic allocation type (SESS_MSG_SEG_DYNAMIC_ALLOC): + * - Free the data buffer allocated by malloc() + * - Free the SessMsgSeg structure itself + * + * For shared memory type (SESS_MSG_SEG_SHARED_MEM): + * - Do not free the shared data buffer (managed by SharedMemoryPool) + * - Only free the SessMsgSeg structure itself + * + * @param queue Pointer to the SessMsgQueue to be cleared + */ +void sess_msg_queue_free_all(struct SessMsgQueue *queue) { + if (queue == NULL) { + return; // Avoid null pointer operation + } + + struct SessMsgSeg *seg, *next_seg; + + + TAILQ_FOREACH_SAFE(seg, queue, entry, next_seg) { + /* 1. Remove the segment from the queue */ + TAILQ_REMOVE(queue, seg, entry); + + /* 2. Deallocate memory based on segment type */ + if (seg->type == SESS_MSG_SEG_DYNAMIC_ALLOC) { + // Free dynamically allocated data buffer + free(seg->data); + } else if (seg->type == SESS_MSG_SEG_SHARED_MEM) { + + // if (current_seg->mem_pool) { + // shared_memory_pool_release(current_seg->mem_pool, current_seg->data); + // } + } + /* 3. Free the segment structure itself */ + free(seg); + + }// TAILQ_FOREACH_SAFE + + +#if 0 + TAILQ_FOREACH(seg, queue, entry){ + // Manually save the next node before releasing current node + next_seg = TAILQ_NEXT(seg, entry); + + // Remove current segment from the queue + TAILQ_REMOVE(queue, seg, entry); + + // Free resources based on memory type + if (SESS_MSG_SEG_DYNAMIC_ALLOC == seg->type) { + // Free dynamically allocated data buffer + if (NULL != seg->data) { + free(seg->data); + seg->data = NULL; + } + } + + if (SESS_MSG_SEG_SHARED_MEM == seg->type) { + // Free dynamically allocated data buffer + if (NULL != seg->data || NULL != seg->mem_pool) { + free_shared_mem(seg->mem_pool, (uint64_t)seg->data); + } + } + + + // Shared memory data is managed by SharedMemoryPool, no need to free here + + // Free the SessMsgSeg structure itself + free(seg); + + // Move to next node (since current node is freed) + seg = next_seg; + } +#endif + + TAILQ_INIT(queue); +} + + +int session_send(struct FrontendSession* sess, const uint8_t* data, uint32_t size) +{ + return 0; +} + + +int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size) +{ + return 0; +} diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c new file mode 100644 index 0000000..5d53be7 --- /dev/null +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -0,0 +1,801 @@ +#include +#include + +#include "session_pool.h" +#include "message.h" +#include "engine.h" +#include "shared_mem_io.h" +#include "common_utils.h" + +FrontendEngine *p_g_fr_eng = NULL; +FrontendEngine g_fr_eng; +struct FrontendSessionPool *front_high_speed_pool = NULL; +//ops +// int frontend_high_speed_create_sess(struct FrontendSessionPool *s_pool, struct FrontendSession **sess, struct SessMsgPara *para); +int frontend_high_speed_create_sess_step1(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para); +int frontend_high_speed_create_sess_step2(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, uint16_t sess_id, SessOpRespData *resp); +int frontend_high_speed_create_sess_passive(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para); +int frontend_high_speed_insert_sess(struct FrontendSessionPool* s_pool, struct FrontendSession *sess); +struct FrontendSession* frontend_high_speed_search_sess(struct FrontendSessionPool *s_pool, uint16_t id); +int frontend_high_speed_delete_sess(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); +void frontend_high_speed_destroy_pool(struct FrontendSessionPool *s_pool); +int frontend_high_speed_close_sess_step1(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); +int frontend_high_speed_close_sess_step2(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, SessOpRespData *resp); + + + + +struct FrontendSessionPoolOps fr_high_speed_pool_ops = { + .create_sess_step1 = frontend_high_speed_create_sess_step1, + .create_sess_step2 = frontend_high_speed_create_sess_step2, + .insert_sess = frontend_high_speed_insert_sess, + .search_sess = frontend_high_speed_search_sess, + .delete_sess = frontend_high_speed_delete_sess, + .data_process_b2f = frontend_high_speed_data_process_b2f, + .data_process_f2b = frontend_high_speed_data_process_f2b, + .close_sess_step1 = frontend_high_speed_close_sess_step1, + .close_sess_step2 = frontend_high_speed_close_sess_step2, + .destroy_pool = frontend_high_speed_destroy_pool +}; + +/** + * @brief Get the global FrontendEngine instance + * + * This function provides a global access point and returns a pre-initialized + * FrontendEngine singleton pointer. It is suitable for scenarios where the same + * frontend engine instance needs to be shared throughout the entire program. + * + * @return FrontendEngine* Pointer to the global FrontendEngine instance, guaranteed + * to be non-null (provided the global instance is properly initialized) + * @note 1. The global instance `p_g_fr_eng` must be initialized before calling this function; + * otherwise, a null pointer or wild pointer may be returned. + * 2. This function is not thread-safe. In a multi-threaded environment, ensure the + * instance is fully initialized before invoking this function. + * 3. Directly modifying the state of the instance pointed to by the returned pointer + * is not recommended. If modifications are required, use the interfaces provided + * by the FrontendEngine class. + */ +FrontendEngine *frontend_get_global_engine(){ + return p_g_fr_eng; +} + +struct FrontendSessionPool *frontend_get_high_speed_pool(){ + return front_high_speed_pool; +} + +//helper func +void fill_id_queue(struct FrontendSessionIDQueue *id_q) +{ + uint16_t q_num = 1024; + for (uint16_t i = 1; i <= q_num; i++) + { + struct FrontendSessionID* id_e = (struct FrontendSessionID*)malloc( + sizeof(struct FrontendSessionID)); + if (!id_e) { + printf("Memory allocate failed!\n"); + exit(1); + } + id_e->id = i; + TAILQ_INSERT_TAIL(id_q, id_e, entry); + // printf("push %p %d\n", id_e, id_e->id); + } +} + +uint16_t allocate_id(struct FrontendSessionIDQueue *id_q) +{ + uint16_t res = 0; + + if(!TAILQ_EMPTY(id_q)) + { + struct FrontendSessionID *id_e = TAILQ_FIRST(id_q); + res = id_e->id; + // printf("pop %p %d\n", id_e, res); + TAILQ_REMOVE(id_q, id_e, entry); + free(id_e); + } + return res; +} + +void release_id(struct FrontendSessionIDQueue *id_q, uint16_t id) +{ + struct FrontendSessionID* id_e = (struct FrontendSessionID*)malloc( + sizeof(struct FrontendSessionID)); + if (!id_e) { + printf("Memory allocate failed!\n"); + exit(1); + } + id_e->id = id; + TAILQ_INSERT_TAIL(id_q, id_e, entry); + // printf("push %p %d\n", id_e, id_e->id); +} + + +/** + * @brief Initialize the frontend high-speed session pool + * + * This function initializes the `FrontendSessionPool` instance, including validating input parameters, + * setting core pool attributes, initializing internal linked queues, and binding pool operation functions. + * The initialization process follows these key steps: + * 1. Check if the input `pool` pointer is non-NULL (avoid null pointer dereference); + * 2. Set fixed pool name, default capacity (1024 sessions), and initialize current session count to 0; + * 3. Initialize three internal TAILQ linked queues (ID management queue, f2b/b2f data queues); + * 4. Populate the ID queue with available session IDs via `fill_id_queue()`; + * 5. Bind the pre-defined pool operation set `fr_high_speed_pool_ops` to the pool; + * 6. Initialize the hash table pointer to NULL (to be allocated or initialized later if needed). + * + * @param[in,out] pool Pointer to the FrontendSessionPool instance to be initialized. + * Must point to a pre-allocated memory block (cannot be NULL). + * + * @return int Return code indicating initialization result: + * - FRONTEND_PROXY_PROCESS_OK: Initialization succeeded; + * - FRONTEND_PROXY_PROCESS_ERROR: Initialization failed (input `pool` is NULL). + * + * @note 1. The `pool` instance must be pre-allocated (static or dynamic memory) before calling this function; + * 2. The pool capacity is fixed to 1024 in this implementation (modify `pool->capacity` manually if dynamic adjustment is needed); + * 3. The TAILQ queues (`id_queue`, `queue_f2b`, `queue_b2f`) rely on the system's TAILQ macro implementation (ensure relevant headers are included); + * 4. The `fr_high_speed_pool_ops` must be a pre-defined valid operation set (contains pool-related operation interfaces like session allocation/release); + * 5. The `pool->engine` assignment is commented out; uncomment it if the pool needs to associate with the global FrontendEngine instance, + * and ensure `get_global_frontend_engine()` is called after `frontend_engine_init()` to avoid null pointer risks. + * @warning Passing a NULL `pool` pointer will directly return an error and print a log via `error_print()`. + */ +int frontend_high_speed_init_pool(struct FrontendSessionPool *pool) +{ + if(NULL == pool){ + error_print("frontend_high_speed_init_pool failed: the point of pool is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + pool->pool_name = "frontend_high_speed_pool"; + pool->capacity = 1024; + pool->sess_num = 0; + + TAILQ_INIT(&pool->id_queue); + fill_id_queue(&pool->id_queue); + + TAILQ_INIT(&pool->queue_f2b); + TAILQ_INIT(&pool->queue_b2f); + + pool->htable = NULL; + pool->ops = &fr_high_speed_pool_ops; + pool->engine = frontend_get_global_engine(); + return FRONTEND_PROXY_PROCESS_OK; +} + + +void inc_sess_num(struct FrontendSessionPool *pool) +{ + pool->sess_num++; +} + +void dec_sess_num(struct FrontendSessionPool *pool) +{ + pool->sess_num--; +} + +void print_pool(struct FrontendSessionPool *s_pool) { + struct FrontendSession *s; + + for (s = s_pool->htable; s != NULL; s = s->hh.next) { + printf("sess id %d\n", s->backend_sess_id); + } +} + +void frontend_high_speed_delete_all_sess(struct FrontendSessionPool *s_pool) +{ + struct FrontendSession *current_sess, *tmp; + + HASH_ITER(hh, s_pool->htable, current_sess, tmp) { + HASH_DEL(s_pool->htable, current_sess); /* delete it */ + free(current_sess); /* free it */ + } +} + +//ops + +/** + * @brief Step 1 of high-speed session creation process in the frontend proxy + * @details This function serves as the first step in the frontend high-speed session creation workflow. + * It extracts necessary parameters from the input SessMsgPara structure, constructs a session creation Command message, + * and sends the constructed message to the backend proxy through the shared memory queue. + * The function focuses on message construction and inter-proxy communication, laying the foundation for subsequent + * session establishment steps between the frontend and backend. + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing frontend session-related resources + * (e.g., session pool initialization status, resource allocation constraints) + * @param[in,out] sess Double pointer to FrontendSession, used to return the pointer of the to-be-created high-speed session + * (the function may initialize the session context and assign it to this pointer for subsequent operations) + * @param[in] para Pointer to the SessMsgPara structure, containing core parameters required for constructing the session creation Command message + * (e.g., session identifiers, protocol configuration, connection parameters) + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the session creation Command message is successfully constructed + * and placed into the shared memory queue; Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/para is NULL, parameter validation fails, + * message construction fails, or the shared memory queue send operation encounters exceptions. + */ + int frontend_high_speed_create_sess_step1(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, + struct SessMsgPara *para){ + FrontendEngine *engine; + struct FrontendSessionPoolOps *sess_pool_ops; + uint16_t frontend_sess_id, new_sess_id, dev_id; + GeneralProxyMsgHeader proxy_msg_hdr; + SessIPv4Params ipv4_para; + uint8_t *msg_payload; + int ret; + uint8_t **res_msg, *res_buf[100] = {NULL}; + + + engine = s_pool->engine; + sess_pool_ops = s_pool->ops; + res_msg = res_buf; + + utils_print("In %s\n", __func__); + utils_print("The address of engine is %p\n", engine); + utils_print("The address of engine ops is %p\n", engine->ops); + utils_print("The address of session pool ops is %p\n", sess_pool_ops); +#if 0 + utils_print("In %s, the address of the engine is %p, ops is %p, hs_backend_eng_ops address is %p, and chooes_dev is %p\n", + __func__, engine, engine->ops, get_hs_backend_engine_ops(), engine->ops->choose_dev); +#endif + + if(NULL == engine){ + error_print("frontend_high_speed_create_sess_step1 fails: the session pool does not belong to any engine, or the engine is not initialized successfully!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + if(NULL == sess_pool_ops || NULL == sess_pool_ops->insert_sess){ + error_print("frontend_high_speed_create_sess_step1 fails: the session pool operation function set is not initialized correctly!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + + new_sess_id = allocate_id(&s_pool->id_queue); + if(0 == new_sess_id){ + error_print("frontend_high_speed_create_sess_step1 failed: failed to allocating session ID!"); + goto create_sess_error; + } + + +/* + * Initialize session object. + */ + sess->frontend_sess_id = new_sess_id; + sess->backend_sess_id = para->backend_sess_id; + sess->ip_version = para->ip_version; + sess->state_f2b &= FRONTEND_SESS_LINKED_TO_QUEUE; + sess->state_b2f &= FRONTEND_SESS_LINKED_TO_QUEUE; + sess->sess_state = FRONTEND_SESS_INITIALIZE; +// sess->data_process_callback = callback; + TAILQ_INIT(&sess->msg_f2b); + utils_print("In %s, TAILQ_EMPTY(&new_sess->msg_f2b) returns %d\n", __func__, TAILQ_EMPTY(&sess->msg_f2b)); + TAILQ_INIT(&sess->msg_b2f); + utils_print("In %s, TAILQ_EMPTY(&new_sess->msg_b2f) returns %d\n", __func__, TAILQ_EMPTY(&sess->msg_b2f)); + + +/* + * Construct a SESSION-CREATION command message and send it to the backend proxy to notify the latter of session creation. + */ + memset(&proxy_msg_hdr, 0, sizeof(GeneralProxyMsgHeader)); + memset(&ipv4_para, 0, sizeof(SessIPv4Params)); + + proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_SESS, + proxy_msg_hdr.outer_header.frontend_sess_id = sess->frontend_sess_id; + proxy_msg_hdr.outer_header.backend_sess_id = sess->backend_sess_id; + proxy_msg_hdr.outer_header.payload_len = sizeof(SessMsgHeader) + sizeof(SessIPv4Params); + + proxy_msg_hdr.inner_header.sess_hdr.version = PROXY_PROTO_SESS_VERSION_1; + proxy_msg_hdr.inner_header.sess_hdr.msg_type = SESS_MSG_CREATE; + proxy_msg_hdr.inner_header.sess_hdr.action_type = ACTION_TYPE_COMMAND; + proxy_msg_hdr.inner_header.sess_hdr.ip_version = para->ip_version; + proxy_msg_hdr.inner_header.sess_hdr.payload_len = sizeof(SessIPv4Params); + + ipv4_para.device_selection = para->dev_id; + ipv4_para.transport_layer_proto = para->trans_proto; + memcpy(&ipv4_para.dest_endpoint, ¶->ip_port_tuple.ipv4_port_tuple, sizeof(ipv4_para.dest_endpoint)); + + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &ipv4_para, sizeof(ipv4_para), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + + if(ret != FRONTEND_PROXY_PROCESS_OK){ + error_print("frontend_high_speed_create_sess_step1 failed: failed to build proxy general message or send to shared memory TX queue!"); + goto create_sess_error; + } +/* + * Insert the session instance into the session pool. + */ + sess->sess_state = FRONTEND_SESS_CONNECTING; + ret = sess_pool_ops->insert_sess(s_pool, sess); + + if(ret != FRONTEND_PROXY_PROCESS_OK){ + error_print("frontend_high_speed_create_sess_step1 failed: failed to insert the session instance into the session pool!"); + goto create_sess_error; + } + + return FRONTEND_PROXY_PROCESS_OK; + +create_sess_error: +/* + * Reclaim resources. + * The memory occupied by the session instance is reclaimed in the function pointed to by the delete_sess pointer. + */ + + if(0 != new_sess_id){ + release_id(&s_pool->id_queue, new_sess_id); + } + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + +/** + * @brief Step 2 of high-speed session creation process in the frontend proxy + * @details This function serves as the second step in the frontend high-speed session creation workflow, focusing on processing the backend proxy's response. + * If the response (resp) indicates successful session creation, the function updates the backend_sess_id member of the target FrontendSession + * with the backend-assigned sess_id. If the response returns a failure, the function prints a log message displaying the failure reason + * maintained in resp, reclaims the resources occupied by the target session (sess), and removes the session from the FrontendSessionPool (s_pool). + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing frontend session resources (e.g., removing invalid sessions from the pool) + * @param[in,out] sess Pointer to FrontendSession, the target high-speed session to be updated or resource-reclaimed; + * On success, its backend_sess_id is updated with sess_id; On failure, the session resources are reclaimed + * @param[in] sess_id 16-bit session ID assigned by the backend proxy, used to update the backend_sess_id of the target FrontendSession when the response is successful + * @param[in] resp Pointer to the SessOpRespData structure, containing the backend's session creation response status (success/failure) and corresponding failure reason + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the response is processed successfully (either session update or resource reclamation completed); + * Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/sess/resp is NULL, session update fails, or resource reclamation/removal from the pool encounters exceptions. + */ +int frontend_high_speed_create_sess_step2(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, uint16_t sess_id, SessOpRespData *resp){ + FrontendEngine *engine; + struct FrontendSessionPoolOps *sess_pool_ops; + uint16_t frontend_sess_id, new_sess_id, dev_id; + uint8_t status, code; + + status = resp->status; + code = resp->code; + sess_pool_ops = s_pool->ops; +/* + * The backend proxy cannot set up a session. It replies to the frontend proxy with the SESS_OP_STATUS_ERROR status and the errno-based reason. + * The frontend proxy, per the protocol, should record the errno-based reason, remove the session instance from the session pool, and release its resources. + */ + if(SESS_OP_STATUS_SUCCESS != status){ + utils_print("In %s, the session creating procedurec failed, the error code is %d\n", __func__, code); + release_id(&s_pool->id_queue, sess->frontend_sess_id); + } + + sess->backend_sess_id = sess_id; + sess->sess_state = FRONTEND_SESS_CONNECTED; + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Passive creation processing function for high-speed session in the frontend proxy + * @details This function is the core processing entry for the **passive creation workflow** of high-speed sessions in the frontend proxy, + * which is distinguished from the active session creation process. It is triggered by the session creation request/command from the backend proxy, + * extracts the necessary core parameters from the input SessMsgPara structure, and performs strict validity verification on all key parameters + * related to passive session creation, including the specified IP protocol version and core session parameters (e.g., frontend session ID, protocol configuration parameters). + * Meanwhile, it manages the frontend session resources through the FrontendSessionPool instance, initializes the context of the high-speed session to be passively created + * based on the validated IP protocol version (IPv4/IPv6), and assigns the relevant session attributes, resource information and network layer configuration + * to the FrontendSession structure. This function focuses on the core logic of passive trigger-based session parameter verification (including IP version validation), + * IP version-based session context initialization and frontend session resource allocation, and completes the key processing of high-speed session passive creation + * in the frontend proxy, laying a solid foundation for the subsequent establishment of the front-backend session link and the normal data interaction of the high-speed session. + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing frontend session-related resources + * (e.g., session pool initialization status, resource allocation constraints, session life cycle management, valid session quantity limit) + * @param[in,out] sess Pointer to the FrontendSession structure, used to initialize the context of the passively created high-speed session + * (the function will assign the relevant session attributes, resource information, protocol configuration and IP version-related network layer settings to this pointer + * for subsequent session link maintenance and data interaction operations) + * @param[in] para Pointer to the SessMsgPara structure, containing core session parameters required for passive creation of the high-speed session + * (e.g., session identifiers delivered by the backend, protocol configuration parameters, front-backend connection parameters, valid session ID range) + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the high-speed session passive creation logic is processed successfully, + * including all parameter (IP version + core session parameters) verification passed, IP version-based session context initialized completely, + * frontend session resource allocated normally and successful response to the backend for sending the new session creation command; + * Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/para is NULL, ip_version takes an invalid value, + * core session parameter validity verification fails, session context initialization fails, frontend session pool resource is insufficient, + * or other exceptions occur during the passive creation processing of the high-speed session. + */ +int frontend_high_speed_create_sess_passive(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para){ + FrontendEngine *engine; + struct FrontendSessionPoolOps *sess_pool_ops; + GeneralProxyMsgHeader proxy_msg_hdr; + SessOpRespData resp_data; + uint8_t **res_msg, *res_buf[100] = {NULL}; + int ret; + +/* + * Check input parameters. + */ + if(NULL == s_pool || NULL == s_pool->engine || NULL == s_pool->engine->tx_queue || NULL == s_pool->ops || NULL == s_pool->ops->insert_sess || + NULL == s_pool->ops->search_sess || NULL == sess || NULL == para){ + error_print("frontend_high_speed_create_sess_passive failed: null value detected in critical input (s_pool/engine/tx_queue/ops/insert_sess/search_sess/sess/para)!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + engine = s_pool->engine; + sess_pool_ops = s_pool->ops; + +/* + * Fill information for constructing the session-creation response message. + */ + memset(&proxy_msg_hdr, 0, sizeof(GeneralProxyMsgHeader)); + proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_SESS, + proxy_msg_hdr.outer_header.backend_sess_id = para->backend_sess_id; + proxy_msg_hdr.outer_header.frontend_sess_id = para->frontend_sess_id; + proxy_msg_hdr.outer_header.payload_len = sizeof(SessMsgHeader) + sizeof(SessOpRespData); + + proxy_msg_hdr.inner_header.sess_hdr.version = PROXY_PROTO_SESS_VERSION_1; + proxy_msg_hdr.inner_header.sess_hdr.msg_type = SESS_MSG_CREATE; + proxy_msg_hdr.inner_header.sess_hdr.action_type = ACTION_TYPE_RESPONSE; + proxy_msg_hdr.inner_header.sess_hdr.ip_version = para->ip_version; + proxy_msg_hdr.inner_header.sess_hdr.payload_len = sizeof(SessOpRespData); + + if(SESS_IPV4_PROTO != para->ip_version){ + error_print("frontend_high_speed_create_sess_passive failed: IP protocol version not supported, only IPv4 is supported currently, \ + IPv6 support will be expanded in the future!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_PARAMETER_INVALID; + goto create_sess_passive_error; + } + +/* + * Check whether the frontend session ID exists in the session pool. + */ + if(sess_pool_ops->search_sess(s_pool, sess->frontend_sess_id)){ + error_print("frontend_high_speed_create_sess_passive failed: frontend session ID already exists in session pool!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_PARAMETER_INVALID; + goto create_sess_passive_error; + } + + + sess->state_f2b &= FRONTEND_SESS_LINKED_TO_QUEUE; + sess->sess_state = FRONTEND_SESS_CONNECTED; +// new_sess->data_process_callback = callback; + TAILQ_INIT(&sess->msg_f2b); + utils_print("In %s, TAILQ_EMPTY(&new_sess->msg_f2b) returns %d\n", __func__, TAILQ_EMPTY(&sess->msg_f2b)); + TAILQ_INIT(&sess->msg_b2f); + utils_print("In %s, TAILQ_EMPTY(&new_sess->msg_b2f) returns %d\n", __func__, TAILQ_EMPTY(&sess->msg_b2f)); + + ret = sess_pool_ops->insert_sess(s_pool, sess); + + if(ret != FRONTEND_PROXY_PROCESS_OK){ + error_print("frontend_high_speed_create_sess_passive failed: failed to insert the session instance into the session pool!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_RESOURCE_INSUFFICIENT; + goto create_sess_passive_error; + } + + resp_data.status = SESS_OP_STATUS_SUCCESS; + resp_data.code = SESS_OP_CODE_SUCCESS; + + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_high_speed_create_sess_passive: failed to build proxy response message for passive session creation!\n"); + } + + return ret; + +create_sess_passive_error: + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_high_speed_create_sess_passive: failed to build error notification message!\n"); + } + + return FRONTEND_PROXY_PROCESS_ERROR; +} + + +int frontend_high_speed_insert_sess(struct FrontendSessionPool *s_pool, struct FrontendSession *sess) +{ + struct FrontendSession *s; + HASH_FIND(hh, s_pool->htable, &sess->frontend_sess_id, sizeof(uint16_t), s); + if(s == NULL) + { + HASH_ADD(hh, s_pool->htable, frontend_sess_id, sizeof(uint16_t), sess); + inc_sess_num(s_pool); + printf("add %d\n", sess->frontend_sess_id); + }else{ + goto insert_error; + } + return FRONTEND_PROXY_PROCESS_OK; +insert_error: + return FRONTEND_PROXY_PROCESS_ERROR; +} + +struct FrontendSession *frontend_high_speed_search_sess(struct FrontendSessionPool *s_pool, uint16_t id) +{ + struct FrontendSession* s = NULL; + HASH_FIND(hh, s_pool->htable, &id, sizeof(uint16_t), s); + return s; +} + +int frontend_high_speed_delete_sess(struct FrontendSessionPool *s_pool, struct FrontendSession *sess) +{ + uint16_t frontend_sess_id, backend_sess_id; + int ret, ip_version; + FrontendEngine* *eng; + + + if(NULL == s_pool || NULL == sess || NULL == s_pool->engine){ + error_print("high_speed_delete_sess failed: session pool (s_pool), session (sess), or engine (s_pool->engine) is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + frontend_sess_id = sess->frontend_sess_id; + backend_sess_id = sess->backend_sess_id; + ip_version = sess->ip_version; + + HASH_DEL(s_pool->htable, sess); + release_id(&s_pool->id_queue, frontend_sess_id); + + dec_sess_num(s_pool); + + FRONTEND_SESS_UNLINK_FROM_QUEUE(sess, f2b); + + sess_msg_queue_free_all(&sess->msg_f2b); + sess_msg_queue_free_all(&sess->msg_b2f); + + free(sess); + +/* + * TODO: send a close message to backend +*/ + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Step 1 of high-speed session closure process in the frontend proxy + * @details This function serves as the first step in the frontend high-speed session closure workflow. + * Its core responsibilities include validating the validity of the target session, marking the session state as "closing", + * preparing core parameters required for the closure process (e.g., session ID, connection context), + * and laying the foundation for subsequent closure coordination with the backend (such as sending closure notifications, + * resource synchronization). The function focuses on preprocessing and state initialization before session closure, + * ensuring the orderly initiation of the entire closure process. + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing global resources of the frontend session pool + * (e.g., session state tracking, resource allocation constraints, overall pool state verification) + * @param[in,out] sess Pointer to the FrontendSession instance to be closed; the function will modify the session's state + * (e.g., mark as closing) and read its core information for closure initialization. Must point to a valid session + * in an active or closable state. + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the closure initialization is successful + * (session validity verified, state marked as closing, core parameters prepared); + * Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/sess is NULL, the session is in an illegal state (e.g., already closed/invalid), + * session state marking fails, or core closure parameter preparation encounters exceptions. + * @note This function is only executed when the frontend actively initiates a session closure. For scenarios where the backend + * actively closes the session, only the frontend_high_speed_close_sess_step2 function will be invoked instead. + */ +int frontend_high_speed_close_sess_step1(struct FrontendSessionPool *s_pool, struct FrontendSession *sess){ + FrontendEngine *engine; + struct FrontendSessionPoolOps *sess_pool_ops; + uint16_t frontend_sess_id, backend_sess_id; + GeneralProxyMsgHeader proxy_msg_hdr; + uint8_t *msg_payload; + int ret; + uint8_t **res_msg, *res_buf[100] = {NULL}; + + + engine = s_pool->engine; + sess_pool_ops = s_pool->ops; + res_msg = res_buf; + + if(NULL == engine || NULL == engine->ops){ + error_print("frontend_high_speed_close_sess_step1 fails: the session pool does not belong to any engine, or the engine is not initialized successfully!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + if(NULL == sess_pool_ops){ + error_print("frontend_high_speed_close_sess_step1 fails: the session pool operation function set is not initialized correctly!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + memset(&proxy_msg_hdr, 0, sizeof(GeneralProxyMsgHeader)); + + proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_SESS, + proxy_msg_hdr.outer_header.frontend_sess_id = sess->frontend_sess_id; + proxy_msg_hdr.outer_header.backend_sess_id = sess->backend_sess_id; + proxy_msg_hdr.outer_header.payload_len = sizeof(SessMsgHeader) + sizeof(SessIPv4Params); + + proxy_msg_hdr.inner_header.sess_hdr.version = PROXY_PROTO_SESS_VERSION_1; + proxy_msg_hdr.inner_header.sess_hdr.msg_type = SESS_MSG_CLOSE; + proxy_msg_hdr.inner_header.sess_hdr.action_type = ACTION_TYPE_COMMAND; + proxy_msg_hdr.inner_header.sess_hdr.ip_version = sess->ip_version; + proxy_msg_hdr.inner_header.sess_hdr.payload_len = 0; + + ret = build_proxy_general_message(engine, &proxy_msg_hdr, NULL, 0, res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + + if(ret != FRONTEND_PROXY_PROCESS_OK){ + error_print("frontend_high_speed_close_sess_step1 failed: failed to build proxy general message or send to shared memory TX queue!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Step 2 of high-speed session closure process in the frontend proxy + * @details This function serves as the second step in the frontend high-speed session closure workflow, focusing on processing the backend proxy's session closure response. + * Its core responsibilities include validating the validity of the backend response data, releasing all resources occupied by the target session (e.g., connection handles, memory buffers, + * shared memory mappings), removing the session from the FrontendSessionPool, and synchronizing the final closure state. If the backend response (resp) indicates successful closure, + * the function performs orderly resource reclamation and pool cleanup; if the response returns a failure, the function logs the failure reason contained in resp and executes forced + * resource release to avoid leaks, ensuring the session is completely invalidated. + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing frontend session resources (e.g., removing the closed session from the pool, verifying pool validity) + * @param[in,out] sess Pointer to the FrontendSession instance to be closed; the function will release its occupied resources, update its final state to "closed", + * and remove it from the session pool. Must point to a valid session that has initiated the closure process. + * @param[in] resp Pointer to the SessOpRespData structure, containing the backend's session closure response status (success/failure) and corresponding failure reason + * (e.g., abnormal backend session termination, resource release failure) + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the backend response is processed successfully (resource reclamation, session removal from pool completed); + * Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/sess/resp is NULL, response data validation fails, session resource reclamation fails, or session removal from the pool encounters exceptions. + * @note This function is only invoked when the backend actively initiates a session closure. For scenarios where the frontend actively closes the session, this function will not be executed + * (only frontend_high_speed_close_sess_step1 will be used in the frontend-initiated closure process). + */ +int frontend_high_speed_close_sess_step2(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, SessOpRespData *resp){ + uint8_t status, code; + int ret; + + status = resp->code; + + if(SESS_OP_STATUS_SUCCESS != status){ + utils_print("In %s, the session creating procedurec failed, the error code is %d\n", __func__, code); + sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNOMAL); + ret = FRONTEND_PROXY_PROCESS_ERROR; + }else{ + sess->event_callback(sess, FRONTEND_SESS_EVENT_CLOSE); + ret = FRONTEND_PROXY_PROCESS_OK; + } + + return ret; +} + + +int frontend_high_speed_data_process_b2f(struct FrontendSession *sess){ + + struct SessMsgSeg *cur_seg, *next_seg; + + TAILQ_FOREACH_SAFE(cur_seg, &sess->msg_b2f, entry, next_seg) { + + utils_print("cur_seg->len = %d, msg = %s\n", cur_seg->len, cur_seg->data); + if (cur_seg->data && cur_seg->len > 0){ + uint8_t buf[cur_seg->len+1]; + utils_print("seg size = %d\n", cur_seg->len); + memcmp(buf, cur_seg->data, cur_seg->len); + + /* + * TODO: link to callback function, and pass the buf data to callback function + */ + sess->data_process_callback(sess, buf, cur_seg->len); + + }else{ + /* Unable to recv the data in the queue */ + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + /* 2. Remove the segment from the queue */ + TAILQ_REMOVE(&sess->msg_b2f, cur_seg, entry); + + /* 3. Deallocate memory based on segment type */ + if (cur_seg->type == SESS_MSG_SEG_DYNAMIC_ALLOC) { + // Free dynamically allocated data buffer + free(cur_seg->data); + } else if (cur_seg->type == SESS_MSG_SEG_SHARED_MEM) { + + // if (current_seg->mem_pool) { + // shared_memory_pool_release(current_seg->mem_pool, current_seg->data); + // } + } + /* 4. Free the segment structure itself */ + free(cur_seg); + + }// TAILQ_FOREACH_SAFE + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_high_speed_data_process_f2b(struct FrontendSession *sess){ + struct SessMsgSeg *cur_seg, *next_seg; + GeneralProxyMsgHeader data_msg_hdr; + uint8_t *payload, **res_pointer; + uint8_t *res_buf[100] = {NULL}; + int ret; + + memset(&data_msg_hdr, 0 , sizeof(data_msg_hdr)); + + data_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + data_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_DATA; + data_msg_hdr.outer_header.backend_sess_id = sess->backend_sess_id; + data_msg_hdr.outer_header.frontend_sess_id = sess->frontend_sess_id; + res_pointer = res_buf; + +/* + * Core Logic: Safely traverse the f2b message queue, convert data in the queue to data-type messages + * and send them via the shared-memory TX queue by calling the build_proxy_general_message function. + * Safe traversal of the session's f2b message queue (TAILQ doubly linked list) to avoid iteration exceptions + * caused by node deletion during traversal. + * Parameter Description: + * cur_seg: Current traversed message segment node + * &sess->msg_f2b: Message queue head (f2b direction: frontend to backend message queue) + * entry: Member used for linking nodes in the list (standard field of TAILQ linked list nodes) + * next_seg: Temporarily stores the next node to ensure traversal can continue after deleting the current node + */ + TAILQ_FOREACH_SAFE(cur_seg, &sess->msg_f2b, entry, next_seg) { + data_msg_hdr.outer_header.payload_len = cur_seg->len; +/* + * Call the proxy general message construction function to assemble the data-type message + * Parameters: Engine instance, message header pointer, data segment pointer, data length, + * result storage pointer, memory allocation mode (shared memory), additional parameters + */ + ret = build_proxy_general_message(sess->eng, &data_msg_hdr, cur_seg->data, cur_seg->len, res_pointer, MEMORY_ALLOC_SHARED, sess->eng->tx_queue); + + if(FRONTEND_PROXY_PROCESS_OK == ret){ + TAILQ_REMOVE(&sess->msg_f2b, cur_seg, entry); + sess_msg_seg_free(cur_seg); + }else if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + error_print("frontend_high_speed_data_process_f2b stops: the shared-memory TX queue is full!\n"); + return FRONTEND_PROXY_PROCESS_AGAIN; + }else{ + error_print("frontend_high_speed_data_process_f2b failed: fail to build data message!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + }// TAILQ_FOREACH_SAFE(cur_seg, &sess->msg_f2b, entry, next_seg) + + return FRONTEND_PROXY_PROCESS_OK; +} + + +void frontend_high_speed_destroy_pool(struct FrontendSessionPool *s_pool) +{ + s_pool->pool_name = NULL; + s_pool->capacity = 0; + s_pool->sess_num = 0; + s_pool->ops = NULL; + frontend_high_speed_delete_all_sess(s_pool); + + //todo, clear queue +} + + + +/** + * @brief Default event callback function for frontend sessions + * + * This is the default callback implementation for handling frontend session events, + * which is invoked by the session framework when specific events (defined in + * FrontendSessionEvent enumeration) occur during the lifecycle of a FrontendSession. + * It provides basic event processing logic as a fallback when no custom callback is specified. + * + * @param[in] sess Pointer to the FrontendSession instance that triggered the event; + * must be a valid non-NULL pointer pointing to an active session + * @param[in] event The specific event type that occurred, one of the values in + * FrontendSessionEvent enumeration (FRONTEND_SESS_EVENT_CONN, + * FRONTEND_SESS_EVENT_RECVDATA, FRONTEND_SESS_EVENT_TIMEOUT, FRONTEND_SESS_EVENT_CLOSE) + * + * @note 1. This callback is called automatically by the session management framework, + * and should not be invoked manually by the user; + * 2. The default implementation typically includes basic operations such as + * logging, simple resource handling, or triggering default business flows + * (e.g., initiating connection setup on FRONTEND_SESS_EVENT_CONN, processing received + * data on FRONTEND_SESS_EVENT_RECVDATA, or preparing for session closure on FRONTEND_EVENT_SESS_CLOSE); + * 3. Users can replace this default callback with a custom implementation to + * achieve personalized event processing logic; + * 4. Ensure the 'sess' pointer is valid when the callback is triggered (the framework + * guarantees this under normal circumstances), invalid pointers may lead to undefined behavior; + * 5. The processing logic in this callback should be non-blocking to avoid blocking the + * session framework's event loop. + */ +void default_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event){ + if(FRONTEND_SESS_EVENT_CONN == event){ + + }else if(FRONTEND_SESS_EVENT_RECVDATA == event){ + + }else if(FRONTEND_SESS_EVENT_CLOSE == event){ + + }else if(FRONTEND_SESS_EVENT_ABNOMAL == event){ + + } + + return; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/shared_mem_io.c b/projects/sel4test/apps/front/src/shared_mem_io.c new file mode 100644 index 0000000..9ab1c89 --- /dev/null +++ b/projects/sel4test/apps/front/src/shared_mem_io.c @@ -0,0 +1,397 @@ +#include "shared_mem_io.h" +#include "frontend_proto.h" + + + +int init_shared_mem_pool(struct SharedMemoryPool *mem_pool){ + return FRONTEND_PROXY_PROCESS_OK; +} + +void free_shared_mem_pool(struct SharedMemoryPool *mem_pool){ + +} + + +uint64_t alloc_shared_mem(struct SharedMemoryPool *mem_pool){ + if(NULL == mem_pool) + return ERROR_SHARED_MEM_ADDR; + + return 0; +} + + +void free_shared_mem(struct SharedMemoryPool *mem_pool, uint64_t addr){ + +} + + + +int init_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + +void free_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool){ + +} + + + +int fetch_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +int release_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + + +/** + * Create and initialize a shared memory pool queue in frontend side. + * + * @param config Pointer to the SharedMemoryPoolQueueConfig structure containing configuration + * parameters (e.g., underlying shared memory pool, lock settings) for queue creation. + * @return Pointer to the newly created SharedMemoryPoolQueue on success; NULL on failure + * (e.g., invalid config, insufficient memory for queue instance) + */ +struct SharedMemoryPoolQueue *shared_mem_pool_queue_create_frontend(const SharedMemoryPoolQueueConfig *config){ + struct SharedMemoryPoolQueue *queue; + int fd; + void *virt_addr; + + if(!IS_VALID_SHM_CONF_MAP_MODE(config)){ + error_print("shared_mem_pool_queue_create_frontend failed: invalid config (NULL pointer), or invalid map mode\n"); + return NULL; + } + + queue = malloc(sizeof(struct SharedMemoryPoolQueue)); + + if(NULL == queue){ + error_print("shared_mem_pool_queue_create_frontend failed: failed to allocate memory for SharedMemoryPoolDeque instance\n"); + return NULL; + } + +// queue->pool = pool; +// queue->length = 0; + + queue->map_mode1 = config->map_mode; + queue->pool = config->pool; + queue->block_size = config->block_size; + queue->capacity = config->capacity; + queue->virt_addr1 = config->virt_addr; + queue->phy_addr = config->phy_addr; + queue->header = 0; + queue->tail = 0; + +#if 0 + fd = open("/dev/mem", O_RDWR | O_SYNC); + + if(fd < 0){ + error_print("shared_mem_pool_queue_create_frontend failed: failed to open /dev/mem"); + free(queue); + return NULL; + } +#endif + // Calculate page-aligned physical address (mmap requires the offset to be a multiple of the page size) + off_t phys_page_off = queue->phy_addr & ~(sysconf(_SC_PAGESIZE) - 1); + size_t page_offset = queue->phy_addr - phys_page_off; + + utils_print("phys_page_off = %d, page_offset = %d\n", phys_page_off, page_offset); + + // Map physical memory to user space +#if 0 + virt_addr = mmap( + NULL, // Let the kernel automatically allocate virtual address + queue->capacity * queue->block_size + page_offset, // Mapping size (including intra-page offset) +// queue->block_size + page_offset, // Mapping size (including intra-page offset) +// PROT_READ | PROT_WRITE, // Read and write permissions + PROT_READ, // Read and write permissions +// MAP_SHARED, // Shared mapping + MAP_SHARED , // Shared mapping + fd, // File descriptor for /dev/mem + phys_page_off // Page-aligned physical address + ); + + utils_print("errno = %d, reasion is %s\n", errno, strerror(errno)); + + if (virt_addr == MAP_FAILED) { + error_print("shared_mem_pool_queue_create_frontend failed: mmap failed"); + close(fd); + free(queue); + return NULL; + } +#endif + + virt_addr = malloc(config->block_size * (config->capacity + 1)); + + if(NULL == virt_addr){ + error_print("shared_mem_pool_queue_create_frontend failed: failed to allocate memory for the queue!"); + close(fd); + free(queue); + return NULL; + } + + queue->virt_addr1 = virt_addr; + + utils_print("queue->virt_addr1 = %lld\n", queue->virt_addr1); + + return queue; +} + + + + +/** + * @brief Initializes a SharedMemoryPoolQueue instance with specified configuration + * + * This function initializes all members of a SharedMemoryPoolQueue structure using parameters + * provided in the configuration. It validates input, copies base parameters, calculates derived + * properties (like max element count), and sets initial queue state (empty state with zero length). + * + * @param queue Pointer to the SharedMemoryPoolQueue instance to be initialized + * @param config Pointer to a SharedMemoryPoolQueueConfig structure containing initialization parameters + * + * @return int Returns FRONTEND_PROXY_PROCESS_OK if initialization succeeds; + * Returns FRONTEND_PROXY_PROCESS_ERROR if input parameters are invalid (NULL pointers) + * + * @note Derived parameters (capacity, surplus) are calculated based on + * capacity and block_size to ensure consistent queue state. Invalid inputs + * prevent any modification to the queue instance. + */ +int shared_mem_pool_queue_initialize(struct SharedMemoryPoolQueue *queue, const SharedMemoryPoolQueueConfig *config){ + // Validate input parameters to prevent null pointer dereference + if (NULL == queue || NULL == config || NULL == config->pool) { + error_print("shared_mem_pool_queue_initialize failed: input parameter(s) is/are NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Validate block size is positive to avoid division by zero in capacity calculation + if(0 == config->block_size){ + error_print("shared_mem_pool_queue_initialize failed: block size should be a positive number!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Initialize base members from configuration parameters + queue->pool = config->pool; + queue->phy_addr = config->phy_addr; + queue->virt_addr1 = config->virt_addr; + queue->capacity = config->capacity; + queue->block_size = config->block_size; + + // Calculate maximum number of items (avoid division by zero) + queue->capacity = config->block_size; + + // Initialize queue to empty state + queue->header = 0; // Start with head at initial position + queue->tail = 0; // Start with tail at initial position +// queue->length = 0; // No elements in empty queue +// queue->surplus = config->capacity; // All slots available initially + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Perform address mapping for physical memory pages allocated to the shared memory pool queue + * + * This function establishes address mapping relationships for the physical memory pages allocated to the shared memory pool queue, + * adapting to the address space differences between the frontend (microkernel-OS) and backend (Linux): + * - Frontend (microkernel-OS): Updates the corresponding address mapping table (table2) or virtual address (virt_addr2) + * based on the queue's map_mode2 configuration. + * - Backend (Linux): Updates the corresponding address mapping table (table1) or virtual address (virt_addr1) + * based on the queue's map_mode1 configuration. + * After successful mapping, both the frontend and backend can access the queue's shared physical memory through their respective + * virtual addresses or mapping tables, ensuring memory accessibility for FIFO data transmission. + * + * @param[in,out] queue Pointer to the shared memory pool queue instance for which physical pages need to be mapped. + * Must contain valid configuration information such as mapping modes (map_mode1/map_mode2) and + * address mapping tables (table1/table2). The mapping results will be updated to the virtual address + * fields (virt_addr1/virt_addr2) or address mapping tables of this structure. + * @param[in] page_num Total number of physical pages to be mapped (unsigned 32-bit integer). Must be greater than 0 and + * consistent with the length of the page_phy array, serving as the count basis for the mapping operation. + * @param[in] page_phy Array storing the physical addresses of the pages to be mapped. Each element corresponds to the + * starting physical address of a physical page. The length of the array must be equal to page_num, + * and the addresses must be valid physical addresses allocated from the shared memory pool. + * + * @return FRONTEND_PROXY_PROCESS_OK Mapping succeeded. All specified physical pages have completed front-end and back-end + * address association, and the relevant address fields of the queue have been updated. + * @return FRONTEND_PROXY_PROCESS_ERROR Mapping failed. Possible reasons include: + * 1. queue is a null pointer or page_num == 0; + * 2. page_phy is a null pointer or contains invalid physical addresses; + * 3. Invalid configuration of mapping modes (map_mode1/map_mode2); + * 4. Failure to execute address space mapping operations on the front-end or back-end; + * 5. Insufficient storage space in the address mapping tables (table1/table2). + * + * @note 1. The frontend runs on microkernel-OS and the backend runs on Linux; the mapping operation must adapt to the memory + * management mechanisms of both sides respectively. + * 2. The mapping logic depends on the queue's map_mode1 (Linux side) and map_mode2 (microkernel side) configurations, + * which must be initialized in advance. + * 3. Physical page addresses must be allocated from the shared memory pool associated with the queue (pool field) to + * ensure address validity and shared accessibility. + * 4. If the mapping mode is SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH, virt_addr1/virt_addr2 will be updated; + * if it is SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL, the table1/table2 mapping tables will be populated. + */ +int shared_mem_pool_queue_frontend_setup_pages(struct SharedMemoryPoolQueue *queue, uint32_t page_num, uint64_t page_phy[]){ + int page_cnt = 0; +/* + * Validate core parameter validity: queue/physical address array is non-null, page count is in valid range (1~MAX_MAP_TABLE_ENTRY_COUNT) + */ + if(NULL == queue || NULL == page_phy || 0 == page_num || page_num > MAX_MAP_TABLE_ENTRY_COUNT){ + error_print("shared_mem_pool_queue_frontend_setup_pages failed: invalid input parameters - queue/page_phy NULL or invalid page_num!\n"); + } + + + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * Destroy a shared memory pool queue and release associated resources. + * + * @param queue Pointer to the SharedMemoryPoolDeque to destroy. Passing NULL is safe (no operation). + * @return FRONTEND_PROXY_PROCESS_OK on success; + * FRONTEND_PROXY_PROCESS_ERROR on failure. + */ +int shared_mem_pool_queue_destroy(struct SharedMemoryPoolQueue* queue){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + + +/** + * @brief Sends a variable-size block (up to block_size) to the SharedMemoryPoolQueue: One Copy + * + * Stores a variable-length data block (with size ≤ block_size) into the queue using one-copy semantics. + * The data is transferred to a shared memory slot through a single copy operation. Regardless of the + * actual data size, the entire block_size space in shared memory is occupied to maintain fixed-size + * slot management. Explicitly handles full queue cases. + * + * @param queue Pointer to the SharedMemoryPoolQueue instance + * @param data Input pointer to the variable-length data block to be sent. Must point to valid memory + * containing the data (not a pointer to a pointer). + * @param data_size Size of the input data block (must be > 0 and ≤ queue->block_size for valid operation) + * + * @return int Returns: + * - FRONTEND_PROXY_PROCESS_ERROR: Success, data was copied to shared memory and queue state updated + * - FRONTEND_PROXY_PROCESS_AGAIN: Queue is full (next tail position equals header) + * - FRONTEND_PROXY_PROCESS_OK: Invalid input parameters (NULL pointers for queue or data), + * invalid block_size (0 bytes), data_size = 0, or data_size > queue->block_size + * + * @note The input data block (pointed to by data) must remain valid until the copy operation completes. + * One-copy semantics involve a single data transfer (e.g., via memcpy) from the input buffer to the + * target shared memory slot. + * Critical behavior: Even if data_size < block_size, the entire block_size space in shared memory is + * reserved and counted as occupied (surplus decreases by block_size, not data_size). + * Queue state (tail, length, surplus) is updated via the SHMP_QUEUE_ENQUEUE macro after the copy completes. + * Address calculation: Target slot address is derived from queue->virt_addr + (tail * block_size). + */ +int shared_mem_pool_queue_send_oc(struct SharedMemoryPoolQueue *queue, + const void *data, + size_t data_size) +{ + uint8_t *base_addr; + int ret; + + // Validate input parameters + if (NULL == queue || NULL == data) { + error_print("shared_mem_pool_queue_send_oc failed: invalid input parameters (NULL pointers)!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Validate block size configuration + if (queue->block_size == 0) { + error_print("shared_mem_pool_queue_send_oc failed: invalid block size (0 bytes)!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Validate input data size range + if (data_size == 0 || data_size > queue->block_size) { + error_print("shared_mem_pool_queue_send_oc failed: invalid data size!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + /* + * Check if queue is full (next header position equals tail) + * Queue uses header for writing and tail for reading: (header+1) % max == tail means full + */ + if ((queue->header + 1) % queue->capacity == queue->tail) { + error_print("shared_mem_pool_queue_send_oc: queue is full!"); + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + // Calculate target address in shared memory (write to header position) + base_addr = (uint8_t *)queue->virt_addr1; + + // Perform one-copy operation to shared memory slot + memcpy(base_addr + queue->header * queue->block_size, data, data_size); + + // Update queue state via macro (moves header forward and adjusts length/surplus) + SHMP_QUEUE_ENQUEUE(queue, ret); + + return ret; +} + + + +/** + * @brief Receives a fixed-size block from the SharedMemoryPoolQueue: Zero Copy + * + * Retrieves one fixed-size memory block (of size block_size) from the front of the queue using zero-copy semantics. + * The data is directly accessed from the shared memory slot without copying by returning a pointer to the memory + * location in the shared pool. Explicitly handles empty queue cases. + * + * @param queue Pointer to the SharedMemoryPoolQueue instance + * @param buffer Output parameter to store the pointer to the received block. Points directly to shared memory. + * @param out_data_size Output parameter to store the actual size of the received block (always equal to queue->block_size) + * + * @return int Returns: + * - FRONTEND_PROXY_PROCESS_OK: Success, a block was retrieved and queue state updated + * - FRONTEND_PROXY_PROCESS_AGAIN: Queue is empty (header == tail) + * - FRONTEND_PROXY_PROCESS_ERROR: Invalid input parameters (NULL pointers) or invalid block size (0 bytes) + * + * @note The returned buffer MUST NOT be freed, as it points to shared memory. The buffer's validity is managed by + * queue operations. out_data_size is always set to queue->block_size for successful operations. + * Queue state (header, length, surplus) is updated via the SHMP_QUEUE_DEQUEUE macro after retrieving the block. + * Address calculation: Block address is derived from queue->virt_addr + (tail * block_size). + */ +int shared_mem_pool_queue_recv_zc(struct SharedMemoryPoolQueue *queue, + void **buffer, + size_t *out_data_size) { + uint8_t *base_addr; + int ret; + // Validate input parameters + if (NULL == queue || NULL == buffer || NULL == out_data_size) { + error_print("shared_mem_pool_queue_recv_zc failed: invalid input parameters (NULL pointers)"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Validate block size (should never happen if init was successful) + if (queue->block_size == 0) { + error_print("shared_mem_pool_queue_recv_zc failed: invalid block size (0 bytes)"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Check if queue is empty (header == tail and length == 0) + if (queue->header == queue->tail) { + error_print("shared_mem_pool_queue_recv_zc: queue is empty"); + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + base_addr = (uint8_t *)queue->virt_addr1; + *buffer = base_addr + (queue->tail + 1) * queue->block_size; + *out_data_size = queue->block_size; + + utils_print("In %s, message address is %p\n", __func__, *buffer); + + SHMP_QUEUE_DEQUEUE(queue, ret); + + return ret; +} -- Gitee From f27aaf57588ac63494bd87d5576b339b8a0da5ac Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:35:35 +0800 Subject: [PATCH 034/133] upload source files --- .../sel4test/apps/front/unittest/queue_test.c | 395 +++++++++++++++ .../apps/front/unittest/uthash_test.c | 455 ++++++++++++++++++ 2 files changed, 850 insertions(+) create mode 100644 projects/sel4test/apps/front/unittest/queue_test.c create mode 100644 projects/sel4test/apps/front/unittest/uthash_test.c diff --git a/projects/sel4test/apps/front/unittest/queue_test.c b/projects/sel4test/apps/front/unittest/queue_test.c new file mode 100644 index 0000000..f1f743f --- /dev/null +++ b/projects/sel4test/apps/front/unittest/queue_test.c @@ -0,0 +1,395 @@ +#include +#include +#include +#include "queue.h" + +typedef struct test_node { + int id; + char name[32]; + TAILQ_ENTRY(test_node) entries; +} test_node_t; + +// 定义队列头 +TAILQ_HEAD(test_queue, test_node); +typedef struct test_queue test_queue_t; + +// 打印队列内容的函数 +void print_queue(test_queue_t *head, const char *description) { + test_node_t *np; + printf("%s: [", description); + int first = 1; + TAILQ_FOREACH(np, head, entries) { + if (!first) printf(", "); + printf("(%d:%s)", np->id, np->name); + first = 0; + } + printf("]\n"); +} + +// 释放队列中所有节点 +void clear_queue(test_queue_t *head) { + test_node_t *np; + while (!TAILQ_EMPTY(head)) { + np = TAILQ_FIRST(head); + TAILQ_REMOVE(head, np, entries); + free(np); + } +} + +int test_tailq_insert_head() { + test_queue_t head; + test_node_t *node1, *node2; + + printf("\n=== Testing TAILQ_INSERT_HEAD ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + // 插入第一个节点 + TAILQ_INSERT_HEAD(&head, node1, entries); + print_queue(&head, "After inserting first node"); + + // 插入第二个节点到头部 + TAILQ_INSERT_HEAD(&head, node2, entries); + print_queue(&head, "After inserting second node at head"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_insert_tail() { + test_queue_t head; + test_node_t *node1, *node2, *node3; + + printf("\n=== Testing TAILQ_INSERT_TAIL ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 插入节点到尾部 + TAILQ_INSERT_TAIL(&head, node1, entries); + print_queue(&head, "After inserting first node at tail"); + + TAILQ_INSERT_TAIL(&head, node2, entries); + print_queue(&head, "After inserting second node at tail"); + + TAILQ_INSERT_TAIL(&head, node3, entries); + print_queue(&head, "After inserting third node at tail"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_insert_after() { + test_queue_t head; + test_node_t *node1, *node2, *node3; + + printf("\n=== Testing TAILQ_INSERT_AFTER ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 先插入前两个节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + print_queue(&head, "After inserting first two nodes"); + + // 在第一个节点后插入第三个节点 + TAILQ_INSERT_AFTER(&head, node1, node3, entries); + print_queue(&head, "After inserting third node after first"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_remove() { + test_queue_t head; + test_node_t *node1, *node2, *node3, *node4; + + printf("\n=== Testing TAILQ_REMOVE ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + node4 = malloc(sizeof(test_node_t)); + node4->id = 4; + strcpy(node4->name, "Fourth"); + + // 插入所有节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + TAILQ_INSERT_TAIL(&head, node3, entries); + TAILQ_INSERT_TAIL(&head, node4, entries); + print_queue(&head, "After inserting all nodes"); + + // 删除中间节点 + TAILQ_REMOVE(&head, node2, entries); + print_queue(&head, "After removing second node"); + free(node2); + + // 删除头部节点 + TAILQ_REMOVE(&head, node1, entries); + print_queue(&head, "After removing first node"); + free(node1); + + // 删除尾部节点 + TAILQ_REMOVE(&head, node4, entries); + print_queue(&head, "After removing last node"); + free(node4); + + // 清理剩余节点 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_FOREACH() { + test_queue_t head; + test_node_t *node1, *node2, *node3, *np; + int count; + + printf("\n=== Testing TAILQ_FOREACH ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 插入节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + TAILQ_INSERT_TAIL(&head, node3, entries); + print_queue(&head, "After inserting nodes"); + + // 遍历并计数 + count = 0; + TAILQ_FOREACH(np, &head, entries) { + count++; + } + printf("Counted %d nodes using TAILQ_FOREACH\n", count); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_FOREACH_REVERSE() { + test_queue_t head; + test_node_t *node1, *node2, *node3, *np; + int count; + + printf("\n=== Testing TAILQ_FOREACH_REVERSE ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 插入节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + TAILQ_INSERT_TAIL(&head, node3, entries); + print_queue(&head, "After inserting nodes"); + + // 反向遍历并打印 + printf("Reverse traversal: ["); + count = 0; + TAILQ_FOREACH_REVERSE(np, &head, test_queue, entries) { + if (count > 0) printf(", "); + printf("(%d:%s)", np->id, np->name); + count++; + } + printf("]\n"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_empty_and_first() { + test_queue_t head; + test_node_t *node, *first; + + printf("\n=== Testing TAILQ_EMPTY and TAILQ_FIRST ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 检查空队列 + if (TAILQ_EMPTY(&head)) { + printf("Queue is empty as expected\n"); + } + + first = TAILQ_FIRST(&head); + if (first == NULL) { + printf("TAILQ_FIRST returned NULL for empty queue as expected\n"); + } + + // 添加一个节点 + node = malloc(sizeof(test_node_t)); + node->id = 1; + strcpy(node->name, "Only"); + TAILQ_INSERT_TAIL(&head, node, entries); + print_queue(&head, "After inserting one node"); + + // 检查非空队列 + if (!TAILQ_EMPTY(&head)) { + printf("Queue is not empty as expected\n"); + } + + first = TAILQ_FIRST(&head); + if (first != NULL) { + printf("TAILQ_FIRST returned node (%d:%s)\n", first->id, first->name); + } + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_next() { + test_queue_t head; + test_node_t *node1, *node2, *node3, *np; + + printf("\n=== Testing TAILQ_NEXT ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 插入节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + TAILQ_INSERT_TAIL(&head, node3, entries); + print_queue(&head, "After inserting nodes"); + + // 使用TAILQ_NEXT遍历 + printf("Traversal using TAILQ_NEXT: ["); + int count = 0; + for (np = TAILQ_FIRST(&head); np != NULL; np = TAILQ_NEXT(np, entries)) { + if (count > 0) printf(", "); + printf("(%d:%s)", np->id, np->name); + count++; + } + printf("]\n"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +void tailq_test(){ + printf("Testing TAILQ operations\n"); + + test_tailq_insert_head(); + test_tailq_insert_tail(); + test_tailq_insert_after(); + test_tailq_remove(); + test_tailq_FOREACH(); + test_tailq_FOREACH_REVERSE(); + test_tailq_empty_and_first(); + test_tailq_next(); + + printf("\nAll TAILQ tests completed!\n"); +} + \ No newline at end of file diff --git a/projects/sel4test/apps/front/unittest/uthash_test.c b/projects/sel4test/apps/front/unittest/uthash_test.c new file mode 100644 index 0000000..937c5bb --- /dev/null +++ b/projects/sel4test/apps/front/unittest/uthash_test.c @@ -0,0 +1,455 @@ +#include +#include +#include +#include "uthash.h" + +// 定义测试结构体 +typedef struct test_item { + int id; + char name[32]; + UT_hash_handle hh; +} test_item_t; + +typedef struct string_item { + char key[32]; + int value; + UT_hash_handle hh; +} string_item_t; + +typedef struct ptr_item { + void *ptr_key; + int data; + UT_hash_handle hh; +} ptr_item_t; + +// 打印哈希表内容的函数 +void print_hash_int(test_item_t *head, const char *description) { + test_item_t *current; + printf("%s: [", description); + int first = 1; + for (current = head; current != NULL; current = current->hh.next) { + if (!first) printf(", "); + printf("(%d:%s)", current->id, current->name); + first = 0; + } + printf("]\n"); +} + +void print_hash_str(string_item_t *head, const char *description) { + string_item_t *current; + printf("%s: [", description); + int first = 1; + for (current = head; current != NULL; current = current->hh.next) { + if (!first) printf(", "); + printf("(%s:%d)", current->key, current->value); + first = 0; + } + printf("]\n"); +} + +void print_hash_ptr(ptr_item_t *head, const char *description) { + ptr_item_t *current; + printf("%s: [", description); + int first = 1; + for (current = head; current != NULL; current = current->hh.next) { + if (!first) printf(", "); + printf("(%p:%d)", current->ptr_key, current->data); + first = 0; + } + printf("]\n"); +} + +// 释放哈希表中所有节点 +void clear_hash_int(test_item_t **head) { + test_item_t *current, *tmp; + HASH_ITER(hh, *head, current, tmp) { + HASH_DEL(*head, current); + free(current); + } +} + +void clear_hash_str(string_item_t **head) { + string_item_t *current, *tmp; + HASH_ITER(hh, *head, current, tmp) { + HASH_DEL(*head, current); + free(current); + } +} + +void clear_hash_ptr(ptr_item_t **head) { + ptr_item_t *current, *tmp; + HASH_ITER(hh, *head, current, tmp) { + HASH_DEL(*head, current); + free(current); + } +} + +int test_hash_add_find_int() { + test_item_t *users = NULL; + test_item_t *item1, *item2, *item3, *found; + + printf("\n=== Testing HASH_ADD_INT and HASH_FIND_INT ===\n"); + print_hash_int(users, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + item2 = malloc(sizeof(test_item_t)); + item2->id = 2; + strcpy(item2->name, "Bob"); + + item3 = malloc(sizeof(test_item_t)); + item3->id = 3; + strcpy(item3->name, "Charlie"); + + // 添加节点 + HASH_ADD_INT(users, id, item1); + print_hash_int(users, "After adding first item"); + + HASH_ADD_INT(users, id, item2); + print_hash_int(users, "After adding second item"); + + HASH_ADD_INT(users, id, item3); + print_hash_int(users, "After adding third item"); + + // 查找节点 + int search_id = 2; + HASH_FIND_INT(users, &search_id, found); + if (found) { + printf("Found item with id %d: %s\n", found->id, found->name); + } + + // 查找不存在的节点 + search_id = 5; + HASH_FIND_INT(users, &search_id, found); + if (!found) { + printf("Item with id %d not found as expected\n", search_id); + } + + // 清理 + clear_hash_int(&users); + print_hash_int(users, "After clearing"); + + return 0; +} + +int test_hash_add_find_str() { + string_item_t *items = NULL; + string_item_t *item1, *item2, *item3, *found; + + printf("\n=== Testing HASH_ADD_STR and HASH_FIND_STR ===\n"); + print_hash_str(items, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(string_item_t)); + strcpy(item1->key, "apple"); + item1->value = 10; + + item2 = malloc(sizeof(string_item_t)); + strcpy(item2->key, "banana"); + item2->value = 20; + + item3 = malloc(sizeof(string_item_t)); + strcpy(item3->key, "cherry"); + item3->value = 30; + + // 添加节点 + HASH_ADD_STR(items, key, item1); + print_hash_str(items, "After adding first item"); + + HASH_ADD_STR(items, key, item2); + print_hash_str(items, "After adding second item"); + + HASH_ADD_STR(items, key, item3); + print_hash_str(items, "After adding third item"); + + // 查找节点 + HASH_FIND_STR(items, "banana", found); + if (found) { + printf("Found item with key %s: %d\n", found->key, found->value); + } + + // 查找不存在的节点 + HASH_FIND_STR(items, "orange", found); + if (!found) { + printf("Item with key 'orange' not found as expected\n"); + } + + // 清理 + clear_hash_str(&items); + print_hash_str(items, "After clearing"); + + return 0; +} + +int test_hash_add_find_ptr() { + ptr_item_t *items = NULL; + ptr_item_t *item1, *item2, *item3, *found; + int dummy1, dummy2, dummy3; + void *search_ptr; + + printf("\n=== Testing HASH_ADD_PTR and HASH_FIND_PTR ===\n"); + print_hash_ptr(items, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(ptr_item_t)); + item1->ptr_key = &dummy1; + item1->data = 100; + + item2 = malloc(sizeof(ptr_item_t)); + item2->ptr_key = &dummy2; + item2->data = 200; + + item3 = malloc(sizeof(ptr_item_t)); + item3->ptr_key = &dummy3; + item3->data = 300; + + // 添加节点 + HASH_ADD_PTR(items, ptr_key, item1); + print_hash_ptr(items, "After adding first item"); + + HASH_ADD_PTR(items, ptr_key, item2); + print_hash_ptr(items, "After adding second item"); + + HASH_ADD_PTR(items, ptr_key, item3); + print_hash_ptr(items, "After adding third item"); + + // 查找节点 + search_ptr = &dummy2; + HASH_FIND_PTR(items, &search_ptr, found); + if (found) { + printf("Found item with ptr_key %p: %d\n", found->ptr_key, found->data); + } + + // 查找不存在的节点 + search_ptr = NULL; + HASH_FIND_PTR(items, &search_ptr, found); + if (!found) { + printf("Item with ptr_key NULL not found as expected\n"); + } + + // 清理 + clear_hash_ptr(&items); + print_hash_ptr(items, "After clearing"); + + return 0; +} + +int test_hash_delete() { + test_item_t *users = NULL; + test_item_t *item1, *item2, *item3, *found; + int search_id; + + printf("\n=== Testing HASH_DEL ===\n"); + print_hash_int(users, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + item2 = malloc(sizeof(test_item_t)); + item2->id = 2; + strcpy(item2->name, "Bob"); + + item3 = malloc(sizeof(test_item_t)); + item3->id = 3; + strcpy(item3->name, "Charlie"); + + // 添加所有节点 + HASH_ADD_INT(users, id, item1); + HASH_ADD_INT(users, id, item2); + HASH_ADD_INT(users, id, item3); + print_hash_int(users, "After adding all items"); + + // 删除中间节点 + search_id = 2; + HASH_FIND_INT(users, &search_id, found); + if (found) { + HASH_DEL(users, found); + free(found); + printf("Deleted item with id %d\n", search_id); + } + print_hash_int(users, "After deleting middle item"); + + // 删除第一个节点 + search_id = 1; + HASH_FIND_INT(users, &search_id, found); + if (found) { + HASH_DEL(users, found); + free(found); + printf("Deleted item with id %d\n", search_id); + } + print_hash_int(users, "After deleting first item"); + + // 清理剩余节点 + clear_hash_int(&users); + print_hash_int(users, "After clearing"); + + return 0; +} + +int test_hash_count() { + test_item_t *users = NULL; + test_item_t *item1, *item2, *item3; + int count; + + printf("\n=== Testing HASH_COUNT ===\n"); + print_hash_int(users, "Initial hash"); + + count = HASH_COUNT(users); + printf("Initial count: %d\n", count); + + // 创建测试节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + item2 = malloc(sizeof(test_item_t)); + item2->id = 2; + strcpy(item2->name, "Bob"); + + item3 = malloc(sizeof(test_item_t)); + item3->id = 3; + strcpy(item3->name, "Charlie"); + + // 添加节点并检查计数 + HASH_ADD_INT(users, id, item1); + count = HASH_COUNT(users); + printf("Count after adding first item: %d\n", count); + print_hash_int(users, "Hash after adding first item"); + + HASH_ADD_INT(users, id, item2); + count = HASH_COUNT(users); + printf("Count after adding second item: %d\n", count); + print_hash_int(users, "Hash after adding second item"); + + HASH_ADD_INT(users, id, item3); + count = HASH_COUNT(users); + printf("Count after adding third item: %d\n", count); + print_hash_int(users, "Hash after adding third item"); + + // 删除节点并检查计数 + HASH_DEL(users, item2); + free(item2); + count = HASH_COUNT(users); + printf("Count after deleting one item: %d\n", count); + print_hash_int(users, "Hash after deleting one item"); + + // 清理 + clear_hash_int(&users); + count = HASH_COUNT(users); + printf("Count after clearing: %d\n", count); + print_hash_int(users, "After clearing"); + + return 0; +} + +int test_hash_iter() { + test_item_t *users = NULL, *current, *tmp; + test_item_t *item1, *item2, *item3; + int count; + + printf("\n=== Testing HASH_ITER ===\n"); + print_hash_int(users, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + item2 = malloc(sizeof(test_item_t)); + item2->id = 2; + strcpy(item2->name, "Bob"); + + item3 = malloc(sizeof(test_item_t)); + item3->id = 3; + strcpy(item3->name, "Charlie"); + + // 添加节点 + HASH_ADD_INT(users, id, item1); + HASH_ADD_INT(users, id, item2); + HASH_ADD_INT(users, id, item3); + print_hash_int(users, "After adding items"); + + // 使用HASH_ITER遍历并计数 + count = 0; + HASH_ITER(hh, users, current, tmp) { + count++; + printf("Visited item: (%d:%s)\n", current->id, current->name); + } + printf("Total items visited: %d\n", count); + + // 清理 + clear_hash_int(&users); + print_hash_int(users, "After clearing"); + + return 0; +} + + +int test_hash_replace() { + test_item_t *users = NULL; + test_item_t *item1, *item2, *replaced; + int search_id; + + printf("\n=== Testing HASH_REPLACE ===\n"); + print_hash_int(users, "Initial hash"); + + // 创建初始节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + // 添加初始节点 + HASH_ADD_INT(users, id, item1); + print_hash_int(users, "After adding initial item"); + + // 创建替换节点 + item2 = malloc(sizeof(test_item_t)); + item2->id = 1; // 相同的ID + strcpy(item2->name, "Alicia"); // 不同的名字 + + // 替换节点 + HASH_REPLACE_INT(users, id, item2, replaced); + if (replaced) { + printf("Replaced item: (%d:%s)\n", replaced->id, replaced->name); + free(replaced); + } + print_hash_int(users, "After replacing item"); + + // 尝试替换不存在的节点 + test_item_t *item3 = malloc(sizeof(test_item_t)); + item3->id = 2; + strcpy(item3->name, "Bob"); + + HASH_REPLACE_INT(users, id, item3, replaced); + if (replaced) { + printf("Replaced item: (%d:%s)\n", replaced->id, replaced->name); + free(replaced); + } else { + printf("No item replaced, added new item instead\n"); + } + print_hash_int(users, "After attempting to replace non-existent item"); + + // 清理 + clear_hash_int(&users); + print_hash_int(users, "After clearing"); + + return 0; +} + +void uthash_test() { + printf("Testing UTHASH operations\n"); + + test_hash_add_find_int(); + test_hash_add_find_str(); + test_hash_add_find_ptr(); + test_hash_delete(); + test_hash_count(); + test_hash_iter(); + test_hash_replace(); + printf("\nAll UTHASH tests completed!\n"); +} \ No newline at end of file -- Gitee From 50579608783779bb35c26d45fabb2c6beae89d1e Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:36:39 +0800 Subject: [PATCH 035/133] upload source files --- projects/sel4test/apps/front/CMakeLists.txt | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 projects/sel4test/apps/front/CMakeLists.txt diff --git a/projects/sel4test/apps/front/CMakeLists.txt b/projects/sel4test/apps/front/CMakeLists.txt new file mode 100644 index 0000000..cee3b63 --- /dev/null +++ b/projects/sel4test/apps/front/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.16.0) + +project(front C) + +find_package(musllibc REQUIRED) +find_package(util_libs REQUIRED) +find_package(seL4_libs REQUIRED) + +# Setup build environment +musllibc_setup_build_environment_with_sel4runtime() +sel4_import_libsel4() +util_libs_import_libraries() +sel4_libs_import_libraries() + +# Build front application +file(GLOB + deps + src/main.c + src/common_utils.c + src/frontend_proto.c + src/session.c + src/session_pool.c + src/shared_mem_io.c + src/dev.c + src/engine.c + src/frontend_api.c + src/senario_test.c + unittest/*.c +) +# list(SORT deps) + +add_executable(front EXCLUDE_FROM_ALL ${deps}) + +# Add include directories for front headers (本地 include 目录) +target_include_directories(front PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +target_link_libraries(front + PUBLIC + sel4_autoconf + muslc + sel4 + sel4runtime + sel4utils + sel4platsupport + sel4muslcsys + sel4allocman + PRIVATE + utils +) + +# Set executable properties +target_compile_options(front PRIVATE -g -O2) +set_property(TARGET front PROPERTY C_STANDARD 99) + +# Set this image as the rootserver +include(rootserver) +DeclareRootserver(front) \ No newline at end of file -- Gitee From d2b6440024c454b83960fa7e6df28d015ea4e7bc Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 16:21:02 +0800 Subject: [PATCH 036/133] adding hyperamp shared memory queue codes --- projects/sel4test/CMakeLists.txt | 2 + projects/sel4test/apps/front/CMakeLists.txt | 1 + .../apps/front/include/hyperamp_shm_queue.h | 309 +++++++++++++++ .../apps/front/src/hyperamp_shm_queue.c | 368 ++++++++++++++++++ .../sel4test/apps/front/src/shared_mem_io.c | 2 +- 5 files changed, 681 insertions(+), 1 deletion(-) create mode 100644 projects/sel4test/apps/front/include/hyperamp_shm_queue.h create mode 100644 projects/sel4test/apps/front/src/hyperamp_shm_queue.c diff --git a/projects/sel4test/CMakeLists.txt b/projects/sel4test/CMakeLists.txt index 1f8fafe..5d52c60 100755 --- a/projects/sel4test/CMakeLists.txt +++ b/projects/sel4test/CMakeLists.txt @@ -36,6 +36,8 @@ if(Sel4testApp STREQUAL "hello-world") add_subdirectory(apps/hello-world) elseif(Sel4testApp STREQUAL "hyperamp-server") add_subdirectory(apps/hyperamp-server) +elseif(Sel4testApp STREQUAL "front") + add_subdirectory(apps/front) else() add_subdirectory(apps/sel4test-driver) endif() diff --git a/projects/sel4test/apps/front/CMakeLists.txt b/projects/sel4test/apps/front/CMakeLists.txt index cee3b63..ef69efe 100644 --- a/projects/sel4test/apps/front/CMakeLists.txt +++ b/projects/sel4test/apps/front/CMakeLists.txt @@ -21,6 +21,7 @@ file(GLOB src/session.c src/session_pool.c src/shared_mem_io.c + src/hyperamp_shm_queue.c src/dev.c src/engine.c src/frontend_api.c diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h new file mode 100644 index 0000000..5430bba --- /dev/null +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -0,0 +1,309 @@ +/** + * @file hyperamp_shm_queue.h + * @brief HyperAMP Shared Memory Queue – seL4 version + * + * This is a streamlined seL4 version, copied and modified from the hvisor-tool project. + */ + +#ifndef HYPERAMP_SHM_QUEUE_SEL4_H +#define HYPERAMP_SHM_QUEUE_SEL4_H + +#include +#include + +/* ==================== Constant Definitions ==================== */ + +#define HYPERAMP_ERROR_ADDR UINT64_MAX +#define HYPERAMP_MAX_MAP_TABLE_ENTRIES 125 /* Makes the queue control region exactly 4KB (1 page) */ + +#define HYPERAMP_OK 0 +#define HYPERAMP_ERROR (-1) + +/* Memory mapping modes */ +typedef enum { + HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH = 0, + HYPERAMP_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL +} HyperampMapMode; + +/* Message constants */ +#define HYPERAMP_MSG_HDR_SIZE 8 +#define HYPERAMP_MSG_MIN_SIZE 1 +#define HYPERAMP_MSG_MAX_SIZE 4088 +#define HYPERAMP_MSG_HDR_PLUS_MAX_SIZE (HYPERAMP_MSG_HDR_SIZE + HYPERAMP_MSG_MAX_SIZE) + +/* Queue magic number */ +#define HYPERAMP_QUEUE_MAGIC 0x48415150 // "HAQP" + +/* ==================== Memory Barriers and Cache Operations ==================== */ + +#if defined(__aarch64__) || defined(__arm__) + #define HYPERAMP_DMB() __asm__ volatile("dmb sy" ::: "memory") + #define HYPERAMP_DSB() __asm__ volatile("dsb sy" ::: "memory") + #define HYPERAMP_ISB() __asm__ volatile("isb" ::: "memory") + + /* Data cache clean – flush cache lines to main memory (used for shared memory writes) */ + static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { + volatile char *p = (volatile char *)addr; + volatile char *end = p + size; + /* ARM64 cache line size is typically 64 bytes */ + for (; p < end; p += 64) { + __asm__ volatile("dc cvac, %0" : : "r"(p) : "memory"); + } + __asm__ volatile("dsb sy" ::: "memory"); + } + + /* Data cache invalidate – discard cached contents and force reload from memory (used for shared memory reads) */ + static inline void hyperamp_cache_invalidate(volatile void *addr, size_t size) { + volatile char *p = (volatile char *)addr; + volatile char *end = p + size; + for (; p < end; p += 64) { + __asm__ volatile("dc civac, %0" : : "r"(p) : "memory"); + } + __asm__ volatile("dsb sy" ::: "memory"); + } +#else + #define HYPERAMP_DMB() __asm__ volatile("mfence" ::: "memory") + #define HYPERAMP_DSB() __asm__ volatile("mfence" ::: "memory") + #define HYPERAMP_ISB() __asm__ volatile("" ::: "memory") + + static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { + (void)addr; (void)size; + __asm__ volatile("mfence" ::: "memory"); + } + + static inline void hyperamp_cache_invalidate(volatile void *addr, size_t size) { + (void)addr; (void)size; + __asm__ volatile("mfence" ::: "memory"); + } +#endif + +#define HYPERAMP_BARRIER() do { HYPERAMP_DMB(); HYPERAMP_DSB(); } while(0) + +/* ==================== Software Spinlock ==================== */ + +typedef struct { + volatile uint32_t lock_value; + volatile uint32_t owner_zone_id; + volatile uint32_t lock_count; + volatile uint32_t contention_count; +} __attribute__((packed)) HyperampSpinlock; + +static inline void hyperamp_spinlock_init(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + + volatile uint8_t *p = (volatile uint8_t *)lock; + for (size_t i = 0; i < sizeof(HyperampSpinlock); i++) { + p[i] = 0; + } + HYPERAMP_BARRIER(); + + /* Flush the lock state to main memory */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +} + +static inline void hyperamp_spinlock_lock(volatile HyperampSpinlock *lock, uint32_t zone_id) +{ + if (!lock) return; + + int spin_count = 0; + const int max_spin = 100000; + + while (1) { + HYPERAMP_BARRIER(); + + volatile uint32_t current = lock->lock_value; + + if (current == 0) { + lock->lock_value = 1; + HYPERAMP_BARRIER(); + + volatile uint32_t verify = lock->lock_value; + if (verify == 1) { + lock->owner_zone_id = zone_id; + lock->lock_count++; + HYPERAMP_BARRIER(); + + /* Flush the updated lock state to main memory, + ensuring other cores/VMs observe that the lock is held */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); + return; + } + } + + lock->contention_count++; + spin_count++; + + if (spin_count > max_spin) { + spin_count = 0; +#if defined(__aarch64__) || defined(__arm__) + __asm__ volatile("yield" ::: "memory"); +#else + __asm__ volatile("pause" ::: "memory"); +#endif + } + + for (volatile int i = 0; i < 100; i++) { + HYPERAMP_BARRIER(); + } + } +} + +static inline void hyperamp_spinlock_unlock(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + + HYPERAMP_BARRIER(); + lock->owner_zone_id = 0; + lock->lock_value = 0; + HYPERAMP_BARRIER(); + + /* Critical: flush the unlocked state to main memory, + ensuring other cores/VMs observe that the lock is released */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +} + +/* ==================== Address Mapping Table Entry ==================== */ + +typedef struct { + uint64_t virt_addr; + uint64_t phy_addr; +} __attribute__((packed)) HyperampMapTableEntry; + +/* ==================== Shared Memory Pool Queue ==================== */ + +typedef struct { + uint8_t map_mode1; + uint8_t map_mode2; + uint16_t header; + uint16_t tail; + uint16_t capacity; + uint16_t block_size; + uint16_t _reserved; + + uint64_t phy_addr; + uint64_t virt_addr1; + uint64_t virt_addr2; + + HyperampMapTableEntry table1[HYPERAMP_MAX_MAP_TABLE_ENTRIES]; + HyperampMapTableEntry table2[HYPERAMP_MAX_MAP_TABLE_ENTRIES]; + + HyperampSpinlock queue_lock; + + uint32_t magic; + uint32_t version; + uint32_t enqueue_count; + uint32_t dequeue_count; + +} __attribute__((packed)) HyperampShmQueue; + +/* ==================== Message Header Structure ==================== */ + +typedef struct { + uint8_t version; + uint8_t proxy_msg_type; + uint16_t frontend_sess_id; + uint16_t backend_sess_id; + uint16_t payload_len; +} __attribute__((packed)) HyperampMsgHeader; + +/* Message types */ +typedef enum { + HYPERAMP_MSG_TYPE_DEV = 0, + HYPERAMP_MSG_TYPE_STRGY = 1, + HYPERAMP_MSG_TYPE_SESS = 2, + HYPERAMP_MSG_TYPE_DATA = 3, + HYPERAMP_MSG_TYPE_SERVICE = 0x10, + HYPERAMP_MSG_TYPE_BULK = 0x20 // Bulk data transfer (payload is a descriptor) +} HyperampMsgType; + +// Bulk Transfer configuration +#define BULK_BUFFER_OFFSET 0x100000 // Offset at 1MB +#define BULK_BUFFER_SIZE (2 * 1024 * 1024) // 2MB buffer size + +// Bulk Transfer descriptor (transmitted as payload) +typedef struct { + uint32_t offset; // Data offset + uint32_t length; // Data length + uint32_t service_id; // Service ID (1=Encrypt, 2=Decrypt) + int32_t status; // 0=Request, 1=Success, <0=Error +} HyperampBulkDescriptor; + +// ==================== Signature Verification ==================== + +// Service IDs +#define SERVICE_ECHO 0 +#define SERVICE_ENCRYPT 1 +#define SERVICE_DECRYPT 2 +#define SERVICE_VERIFY_ONLY 3 // Signature verification only +#define SERVICE_VERIFY_ENCRYPT 4 // Verify signature then encrypt +#define SERVICE_VERIFY_DECRYPT 5 // Verify signature then decrypt +#define SERVICE_VALIDATE_ENCRYPT 7 // Validate fields then encrypt (for object detection data) +#define SERVICE_VALIDATE_DECRYPT 8 // Validate fields then decrypt (for object detection data) + +// Field validation status codes +#define VALIDATE_OK 0 +#define VALIDATE_FAILED_MISSING -10 // Required field missing + +// Signature verification status codes +#define AUTH_OK 0 +#define AUTH_FAILED_BAD_MAGIC -1 +#define AUTH_FAILED_BAD_SIG -2 +#define AUTH_FAILED_BAD_LEN -3 + +// Signature header magic +#define SIG_MAGIC 0x53494731 // "SIG1" + +// Simplified signature header (for prototype verification) +typedef struct { + uint32_t magic; // Must be SIG_MAGIC (0x53494731) + uint16_t sig_len; // Signature length (ECDSA: 70–72 bytes) + uint16_t reserved; + uint32_t payload_len; // Original data length + uint8_t signature[72]; // ECDSA-P256 signature (max 72 bytes) +} __attribute__((packed)) HyperampSignedHeader; + + + +/* ==================== Queue Configuration Structure ==================== */ + +/** + * @brief Queue initialization configuration + */ +typedef struct { + uint16_t map_mode; // Memory mapping mode + uint16_t capacity; // Queue capacity + uint16_t block_size; // Block size + uint16_t _reserved; + uint64_t phy_addr; // Physical address + uint64_t virt_addr; // Virtual address +} HyperampQueueConfig; + + +int hyperamp_queue_is_initialized(volatile HyperampShmQueue *queue); + + + +int hyperamp_queue_init(volatile HyperampShmQueue *queue, + const HyperampQueueConfig *config, + int is_creator); + + +int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + const void *data, + size_t data_len, + volatile void *virt_base); + + +int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + void *data, + size_t max_len, + size_t *actual_len, + volatile void *virt_base); + + + + +#endif /* HYPERAMP_SHM_QUEUE_SEL4_H */ \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c new file mode 100644 index 0000000..892b801 --- /dev/null +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -0,0 +1,368 @@ +#include "hyperamp_shm_queue.h" + +// Trusted public key (user-generated ECDSA-P256 public key in DER format) +static const unsigned char TRUSTED_PUBKEY_DER[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0x1b, 0x2e, 0xcc, 0x7e, 0x35, 0xd0, 0xbe, 0xda, 0x02, + 0xce, 0x77, 0x25, 0xf0, 0xbf, 0xa6, 0x87, 0x4f, 0x40, 0xa4, 0xe4, 0xff, + 0xaf, 0xee, 0xb4, 0x5d, 0x12, 0xee, 0x5c, 0x4a, 0x87, 0x07, 0xfe, 0x07, + 0xbf, 0x40, 0xce, 0xb0, 0xb4, 0xa7, 0xcc, 0x7d, 0x7a, 0x85, 0xaf, 0xd3, + 0x23, 0x8d, 0x16, 0xf1, 0x8c, 0x1a, 0x89, 0xca, 0x0c, 0x79, 0x07, 0x43, + 0xca, 0xb9, 0x28, 0xf1, 0xfb, 0xfb, 0x43 +}; +static const unsigned int TRUSTED_PUBKEY_DER_LEN = 91; + + + +/* ==================== Secure Memory Operations ==================== */ + +static inline void hyperamp_safe_memset(volatile void *dst, uint8_t val, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)dst; + for (size_t i = 0; i < len; i++) { + p[i] = val; + } + HYPERAMP_BARRIER(); +} + +static inline void hyperamp_safe_memcpy(volatile void *dst, const volatile void *src, size_t len) +{ + volatile uint8_t *d = (volatile uint8_t *)dst; + const volatile uint8_t *s = (const volatile uint8_t *)src; + for (size_t i = 0; i < len; i++) { + d[i] = s[i]; + } + HYPERAMP_BARRIER(); +} + +static inline uint16_t hyperamp_safe_read_u16(const volatile void *addr, size_t offset) +{ + const volatile uint8_t *p = (const volatile uint8_t *)addr; + uint16_t val = 0; + for (int i = 0; i < 2; i++) { + val |= ((uint16_t)p[offset + i]) << (i * 8); + } + HYPERAMP_BARRIER(); + return val; +} + +static inline uint32_t hyperamp_safe_read_u32(const volatile void *addr, size_t offset) +{ + const volatile uint8_t *p = (const volatile uint8_t *)addr; + uint32_t val = 0; + for (int i = 0; i < 4; i++) { + val |= ((uint32_t)p[offset + i]) << (i * 8); + } + HYPERAMP_BARRIER(); + return val; +} + +static inline uint64_t hyperamp_safe_read_u64(const volatile void *addr, size_t offset) +{ + const volatile uint8_t *p = (const volatile uint8_t *)addr; + uint64_t val = 0; + for (int i = 0; i < 8; i++) { + val |= ((uint64_t)p[offset + i]) << (i * 8); + } + HYPERAMP_BARRIER(); + return val; +} + +/* ==================== Queue Operation Functions (Minimal Version, No printf) ==================== */ + + + +/** + * @brief Check whether the queue has been initialized + */ +int hyperamp_queue_is_initialized(volatile HyperampShmQueue *queue) +{ + if (!queue) return 0; + + HYPERAMP_BARRIER(); + + // Avoid using magic field (offset 4052 > 4096, crosses page boundary); + // instead use capacity field (offset 6) + size_t capacity_offset = offsetof(HyperampShmQueue, capacity); + volatile uint8_t *p = (volatile uint8_t *)queue; + + uint16_t capacity = 0; + for (int i = 0; i < 2; i++) { + capacity |= ((uint16_t)p[capacity_offset + i]) << (i * 8); + } + + HYPERAMP_BARRIER(); + // Initialization indicator: capacity > 0 + return (capacity > 0); +} + +/** + * @brief Enqueue data into the shared memory queue + * @param queue [in/out] Pointer to the shared memory queue control structure (header and enqueue_count will be updated internally) + * @param zone_id [in] Caller's zone ID (used for acquiring the spinlock) + * @param data [in] Source data buffer containing the data to write + * @param data_len [in] Number of bytes to write (must be <= queue block_size) + * @param virt_base [in] Base virtual address of the shared memory data region (used to compute write offset) + * @return HYPERAMP_OK on success, HYPERAMP_ERROR on failure (invalid args, oversized data, or full queue) + */ +int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + const void *data, + size_t data_len, + volatile void *virt_base) +{ + if (!queue || !data || data_len == 0) return HYPERAMP_ERROR; + + // Safely read block_size and capacity + uint16_t block_size = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, block_size)); + uint16_t capacity = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, capacity)); + + if (data_len > block_size) return HYPERAMP_ERROR; + + // Acquire lock + hyperamp_spinlock_lock(&queue->queue_lock, zone_id); + + // Safely read header and tail + uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); + uint16_t tail = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, tail)); + + // Compute new header + uint16_t new_header = header + 1; + if (new_header >= capacity) { + new_header -= capacity; + } + + // Check if queue is full + if (new_header == tail) { + hyperamp_spinlock_unlock(&queue->queue_lock); + return HYPERAMP_ERROR; + } + + // Compute write address + uint64_t write_addr = (uint64_t)virt_base + (uint64_t)(header + 1) * block_size; + + // Write data + hyperamp_safe_memcpy((volatile void *)write_addr, data, data_len); + + // Update header (byte-by-byte) + volatile uint8_t *p = (volatile uint8_t *)queue; + size_t header_offset = offsetof(HyperampShmQueue, header); + p[header_offset] = new_header & 0xFF; + p[header_offset + 1] = (new_header >> 8) & 0xFF; + + // Update enqueue_count (byte-by-byte) + size_t enqueue_offset = offsetof(HyperampShmQueue, enqueue_count); + uint32_t enqueue_count = hyperamp_safe_read_u32(queue, enqueue_offset); + enqueue_count++; + for (int i = 0; i < 4; i++) { + p[enqueue_offset + i] = (enqueue_count >> (i * 8)) & 0xFF; + } + + HYPERAMP_BARRIER(); + + /* Flush written data to memory */ + hyperamp_cache_clean((volatile void *)write_addr, data_len); + /* Flush queue control block to memory */ + hyperamp_cache_clean((volatile void *)queue, 64); /* Only flush first 64 bytes of control fields */ + + // Release lock + hyperamp_spinlock_unlock(&queue->queue_lock); + + return HYPERAMP_OK; +} + +/** + * @brief Dequeue data from the shared memory queue + * @param queue [in/out] Pointer to the shared memory queue control structure (tail and dequeue_count will be updated internally) + * @param zone_id [in] Caller's zone ID (used for acquiring the spinlock) + * @param data [out] Destination buffer to store dequeued data + * @param max_len [in] Maximum capacity of the destination buffer (to prevent overflow) + * @param actual_len [out] Actual number of bytes read (can be NULL if not needed) + * @param virt_base [in] Base virtual address of the shared memory data region (used to compute read offset) + * @return HYPERAMP_OK on success, HYPERAMP_ERROR on failure (invalid args or empty queue) + */ +int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + void *data, + size_t max_len, + size_t *actual_len, + volatile void *virt_base) +{ + if (!queue || !data || max_len == 0) return HYPERAMP_ERROR; + + /* Invalidate cache before reading to ensure latest data is fetched */ + hyperamp_cache_invalidate((volatile void *)queue, 64); + + // Acquire lock + hyperamp_spinlock_lock(&queue->queue_lock, zone_id); + + // Safely read header, tail, block_size, capacity + uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); + uint16_t tail = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, tail)); + uint16_t block_size = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, block_size)); + uint16_t capacity = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, capacity)); + + // Check if queue is empty + if (tail == header) { + hyperamp_spinlock_unlock(&queue->queue_lock); + return HYPERAMP_ERROR; + } + + // Compute read address + uint64_t read_addr = (uint64_t)virt_base + (uint64_t)(tail + 1) * block_size; + + /* Invalidate data region cache to ensure fresh data is read */ + hyperamp_cache_invalidate((volatile void *)read_addr, block_size); + + // Determine actual read length + size_t read_len = (max_len < block_size) ? max_len : block_size; + + // Read data + hyperamp_safe_memcpy(data, (const volatile void *)read_addr, read_len); + + if (actual_len) { + *actual_len = read_len; + } + + // Update tail (byte-by-byte) + uint16_t new_tail = tail + 1; + if (new_tail >= capacity) { + new_tail -= capacity; + } + + volatile uint8_t *p = (volatile uint8_t *)queue; + size_t tail_offset = offsetof(HyperampShmQueue, tail); + p[tail_offset] = new_tail & 0xFF; + p[tail_offset + 1] = (new_tail >> 8) & 0xFF; + + // Update dequeue_count (byte-by-byte) + size_t dequeue_offset = offsetof(HyperampShmQueue, dequeue_count); + uint32_t dequeue_count = hyperamp_safe_read_u32(queue, dequeue_offset); + dequeue_count++; + for (int i = 0; i < 4; i++) { + p[dequeue_offset + i] = (dequeue_count >> (i * 8)) & 0xFF; + } + + HYPERAMP_BARRIER(); + + // Release lock + hyperamp_spinlock_unlock(&queue->queue_lock); + + return HYPERAMP_OK; +} + +/** + * @brief Initialize the shared memory queue (seL4 can now initialize queues itself!) + * @param queue Pointer to the queue + * @param config Configuration parameters + * @param is_creator Whether this process is the queue creator (creator initializes all fields) + * @return HYPERAMP_OK on success, HYPERAMP_ERROR on failure + */ +int hyperamp_queue_init(volatile HyperampShmQueue *queue, + const HyperampQueueConfig *config, + int is_creator) +{ + if (!queue || !config) return HYPERAMP_ERROR; + if (config->block_size == 0 || config->capacity == 0) return HYPERAMP_ERROR; + + if (is_creator) { + // Creator: use safe byte-by-byte writes + volatile uint8_t *p = (volatile uint8_t *)queue; + + // Write map_mode1 and map_mode2 + p[0] = config->map_mode; + p[1] = config->map_mode; + HYPERAMP_BARRIER(); + + // Write header (uint16_t, offset 2) + p[2] = 0; + p[3] = 0; + HYPERAMP_BARRIER(); + + // Write tail (uint16_t, offset 4) + p[4] = 0; + p[5] = 0; + HYPERAMP_BARRIER(); + + // Write capacity (uint16_t, offset 6) + uint16_t cap = config->capacity; + p[6] = cap & 0xFF; + p[7] = (cap >> 8) & 0xFF; + HYPERAMP_BARRIER(); + + // Write block_size (uint16_t, offset 8) + uint16_t bs = config->block_size; + p[8] = bs & 0xFF; + p[9] = (bs >> 8) & 0xFF; + HYPERAMP_BARRIER(); + + // Write _reserved (uint16_t, offset 10) + p[10] = 0; + p[11] = 0; + HYPERAMP_BARRIER(); + + // Write phy_addr (uint64_t, offset 12) – byte-by-byte + uint64_t pa = config->phy_addr; + for (int i = 0; i < 8; i++) { + p[12 + i] = (pa >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + // Write virt_addr1 (uint64_t, offset 20) + uint64_t va = config->virt_addr; + for (int i = 0; i < 8; i++) { + p[20 + i] = (va >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + // Write virt_addr2 (uint64_t, offset 28) – zero out + for (int i = 0; i < 8; i++) { + p[28 + i] = 0; + } + HYPERAMP_BARRIER(); + + // Initialize spinlock + size_t lock_offset = offsetof(HyperampShmQueue, queue_lock); + volatile HyperampSpinlock *lock = (volatile HyperampSpinlock *)&p[lock_offset]; + hyperamp_spinlock_init(lock); + + // Write magic (uint32_t) + size_t magic_offset = offsetof(HyperampShmQueue, magic); + uint32_t magic = HYPERAMP_QUEUE_MAGIC; + for (int i = 0; i < 4; i++) { + p[magic_offset + i] = (magic >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + // Write version (uint32_t) + uint32_t version = 1; + for (int i = 0; i < 4; i++) { + p[magic_offset + 4 + i] = (version >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + // Write enqueue_count (uint32_t) – zero out + for (int i = 0; i < 4; i++) { + p[magic_offset + 8 + i] = 0; + } + HYPERAMP_BARRIER(); + + // Write dequeue_count (uint32_t) – zero out + for (int i = 0; i < 4; i++) { + p[magic_offset + 12 + i] = 0; + } + HYPERAMP_BARRIER(); + + /* Critical: flush entire queue structure to memory, + ensuring other CPUs/Zones can observe the initialization */ + hyperamp_cache_clean((volatile void *)queue, sizeof(HyperampShmQueue)); + + } else { + // Non-creator: only set own virtual address + queue->virt_addr2 = config->virt_addr; + HYPERAMP_BARRIER(); + } + + return HYPERAMP_OK; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/shared_mem_io.c b/projects/sel4test/apps/front/src/shared_mem_io.c index 9ab1c89..e5b1537 100644 --- a/projects/sel4test/apps/front/src/shared_mem_io.c +++ b/projects/sel4test/apps/front/src/shared_mem_io.c @@ -1,6 +1,6 @@ #include "shared_mem_io.h" #include "frontend_proto.h" - +// #include "hyperamp_shm_queue.h" int init_shared_mem_pool(struct SharedMemoryPool *mem_pool){ -- Gitee From c524e1e930dfce5656999b7537ebd0f6b2a81c2f Mon Sep 17 00:00:00 2001 From: xj009 Date: Tue, 10 Feb 2026 13:51:07 +0800 Subject: [PATCH 037/133] hyper shared queue --- projects/sel4test/apps/front/include/engine.h | 12 +-- .../apps/front/include/hyperamp_shm_queue.h | 75 ------------------ .../apps/front/include/shared_mem_io.h | 4 + .../apps/front/src/hyperamp_shm_queue.c | 76 +++++++++++++++++++ 4 files changed, 87 insertions(+), 80 deletions(-) diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index f299d05..2d02579 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -83,11 +83,13 @@ typedef struct FrontendEngine_{ * failed by backend). The state will be restored to FRONTEND_CMD_READY after proper handling of FRONTEND_CMD_OK and FRONTEND_CMD_ERROR. */ FrontendCmdState eng_cmd_state; - struct FrontendSessionPool *sess_pool; // Session pool - struct SharedMemoryPool *mem_pool; // Shared memory pool - struct SharedMemoryPoolLock *mem_pool_lock; // Shared memory pool lock - struct SharedMemoryPoolQueue *rx_queue; // RX queue - struct SharedMemoryPoolQueue *tx_queue; // TX queue + struct FrontendSessionPool *sess_pool; // Session pool + struct SharedMemoryPool *mem_pool; // Shared memory pool + struct SharedMemoryPoolLock *mem_pool_lock; // Shared memory pool lock + struct SharedMemoryPoolQueue *rx_queue; // RX queue + struct SharedMemoryPoolQueue *tx_queue; // TX queue + HyperampShmQueue *hyper_rx_queue; // Hyper RX queue + HyperampShmQueue *hyper_tx_queue; struct FrontendEngOps *ops; } FrontendEngine; diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index 5430bba..6362db7 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -88,81 +88,6 @@ typedef struct { volatile uint32_t contention_count; } __attribute__((packed)) HyperampSpinlock; -static inline void hyperamp_spinlock_init(volatile HyperampSpinlock *lock) -{ - if (!lock) return; - - volatile uint8_t *p = (volatile uint8_t *)lock; - for (size_t i = 0; i < sizeof(HyperampSpinlock); i++) { - p[i] = 0; - } - HYPERAMP_BARRIER(); - - /* Flush the lock state to main memory */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); -} - -static inline void hyperamp_spinlock_lock(volatile HyperampSpinlock *lock, uint32_t zone_id) -{ - if (!lock) return; - - int spin_count = 0; - const int max_spin = 100000; - - while (1) { - HYPERAMP_BARRIER(); - - volatile uint32_t current = lock->lock_value; - - if (current == 0) { - lock->lock_value = 1; - HYPERAMP_BARRIER(); - - volatile uint32_t verify = lock->lock_value; - if (verify == 1) { - lock->owner_zone_id = zone_id; - lock->lock_count++; - HYPERAMP_BARRIER(); - - /* Flush the updated lock state to main memory, - ensuring other cores/VMs observe that the lock is held */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); - return; - } - } - - lock->contention_count++; - spin_count++; - - if (spin_count > max_spin) { - spin_count = 0; -#if defined(__aarch64__) || defined(__arm__) - __asm__ volatile("yield" ::: "memory"); -#else - __asm__ volatile("pause" ::: "memory"); -#endif - } - - for (volatile int i = 0; i < 100; i++) { - HYPERAMP_BARRIER(); - } - } -} - -static inline void hyperamp_spinlock_unlock(volatile HyperampSpinlock *lock) -{ - if (!lock) return; - - HYPERAMP_BARRIER(); - lock->owner_zone_id = 0; - lock->lock_value = 0; - HYPERAMP_BARRIER(); - - /* Critical: flush the unlocked state to main memory, - ensuring other cores/VMs observe that the lock is released */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); -} - /* ==================== Address Mapping Table Entry ==================== */ typedef struct { diff --git a/projects/sel4test/apps/front/include/shared_mem_io.h b/projects/sel4test/apps/front/include/shared_mem_io.h index 256d981..71906ef 100644 --- a/projects/sel4test/apps/front/include/shared_mem_io.h +++ b/projects/sel4test/apps/front/include/shared_mem_io.h @@ -10,6 +10,7 @@ #include #include "session.h" +#include "hyperamp_shm_queue.h" #define ERROR_SHARED_MEM_ADDR UINT64_MAX @@ -21,6 +22,9 @@ #define HSNET_TX_PHY_ADDR_BASE HSNET_RX_PHY_ADDR_BASE + HSNET_MEM_BLOCK_SIZE * MAX_MAP_TABLE_ENTRY_COUNT + + + struct SharedMemoryPoolLock{ int value; // Just for placehoder. We will redefine this struct after receiving the partner's document. }; diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index 892b801..01a0306 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -15,6 +15,82 @@ static const unsigned int TRUSTED_PUBKEY_DER_LEN = 91; +static inline void hyperamp_spinlock_init(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + + volatile uint8_t *p = (volatile uint8_t *)lock; + for (size_t i = 0; i < sizeof(HyperampSpinlock); i++) { + p[i] = 0; + } + HYPERAMP_BARRIER(); + + /* Flush the lock state to main memory */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +} + +static inline void hyperamp_spinlock_lock(volatile HyperampSpinlock *lock, uint32_t zone_id) +{ + if (!lock) return; + + int spin_count = 0; + const int max_spin = 100000; + + while (1) { + HYPERAMP_BARRIER(); + + volatile uint32_t current = lock->lock_value; + + if (current == 0) { + lock->lock_value = 1; + HYPERAMP_BARRIER(); + + volatile uint32_t verify = lock->lock_value; + if (verify == 1) { + lock->owner_zone_id = zone_id; + lock->lock_count++; + HYPERAMP_BARRIER(); + + /* Flush the updated lock state to main memory, + ensuring other cores/VMs observe that the lock is held */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); + return; + } + } + + lock->contention_count++; + spin_count++; + + if (spin_count > max_spin) { + spin_count = 0; +#if defined(__aarch64__) || defined(__arm__) + __asm__ volatile("yield" ::: "memory"); +#else + __asm__ volatile("pause" ::: "memory"); +#endif + } + + for (volatile int i = 0; i < 100; i++) { + HYPERAMP_BARRIER(); + } + } +} + +static inline void hyperamp_spinlock_unlock(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + + HYPERAMP_BARRIER(); + lock->owner_zone_id = 0; + lock->lock_value = 0; + HYPERAMP_BARRIER(); + + /* Critical: flush the unlocked state to main memory, + ensuring other cores/VMs observe that the lock is released */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +} + + /* ==================== Secure Memory Operations ==================== */ static inline void hyperamp_safe_memset(volatile void *dst, uint8_t val, size_t len) -- Gitee From 232b970bfccb8a3ca84ecd93762294fb98c9c28e Mon Sep 17 00:00:00 2001 From: xj009 Date: Tue, 10 Feb 2026 18:23:37 +0800 Subject: [PATCH 038/133] Integrate Hyperamp shared memory queues into frontend proxy --- projects/sel4test/apps/front/include/engine.h | 7 +- .../apps/front/include/hardware_config.h | 146 ++++++++++++++++++ .../apps/front/include/hyperamp_shm_queue.h | 33 ++++ projects/sel4test/apps/front/src/engine.c | 100 ++++++++++++ 4 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 projects/sel4test/apps/front/include/hardware_config.h diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index 2d02579..ff52c1f 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -88,8 +88,8 @@ typedef struct FrontendEngine_{ struct SharedMemoryPoolLock *mem_pool_lock; // Shared memory pool lock struct SharedMemoryPoolQueue *rx_queue; // RX queue struct SharedMemoryPoolQueue *tx_queue; // TX queue - HyperampShmQueue *hyper_rx_queue; // Hyper RX queue - HyperampShmQueue *hyper_tx_queue; + volatile HyperampShmQueue *hyper_rx_queue; // Hyper RX queue + volatile HyperampShmQueue *hyper_tx_queue; struct FrontendEngOps *ops; } FrontendEngine; @@ -141,6 +141,9 @@ void engine_destory_mem_pool_lock(FrontendEngine *eng); int engine_choose_hs_net(FrontendEngine *eng, int *selected_dev_id); +int engine_init_hyperamp_queue(FrontendEngine *eng); + + /** * @brief Get the f2b queue from FrontendEngine's session pool * diff --git a/projects/sel4test/apps/front/include/hardware_config.h b/projects/sel4test/apps/front/include/hardware_config.h new file mode 100644 index 0000000..800bcc0 --- /dev/null +++ b/projects/sel4test/apps/front/include/hardware_config.h @@ -0,0 +1,146 @@ +#ifndef HARDWARE_CONF_H_ +#define HARDWARE_CONF_H_ + +#pragma once + +#define CONFIG_ARM_HIKEY_OUTSTANDING_PREFETCHERS 0 +#define CONFIG_ARM_HIKEY_PREFETCHER_STRIDE 0 +#define CONFIG_ARM_HIKEY_PREFETCHER_NPFSTRM 0 +/* disabled: CONFIG_ARM_HIKEY_PREFETCHER_STBPFDIS */ +/* disabled: CONFIG_ARM_HIKEY_PREFETCHER_STBPFRS */ +/* disabled: CONFIG_PLAT_IMX7 */ +/* disabled: CONFIG_ARCH_AARCH32 */ +#define CONFIG_ARCH_AARCH64 1 +/* disabled: CONFIG_ARCH_ARM_HYP */ +/* disabled: CONFIG_ARCH_RISCV32 */ +/* disabled: CONFIG_ARCH_RISCV64 */ +/* disabled: CONFIG_ARCH_X86_64 */ +/* disabled: CONFIG_ARCH_IA32 */ +#define CONFIG_SEL4_ARCH aarch64 +#define CONFIG_ARCH_ARM 1 +#define CONFIG_ARCH arm +#define CONFIG_WORD_SIZE 64 +#define CONFIG_ARM_PLAT imx8mp-evk +/* disabled: CONFIG_PLAT_BCM2711 */ +/* disabled: CONFIG_PLAT_BCM2837 */ +/* disabled: CONFIG_PLAT_FVP */ +/* disabled: CONFIG_PLAT_HIKEY */ +/* disabled: CONFIG_PLAT_IMX8MQ_EVK */ +/* disabled: CONFIG_PLAT_IMX8MM_EVK */ +#define CONFIG_PLAT_IMX8MP_EVK 1 +/* disabled: CONFIG_PLAT_IMX93 */ +/* disabled: CONFIG_PLAT_MAAXBOARD */ +/* disabled: CONFIG_PLAT_ODROIDC2 */ +/* disabled: CONFIG_PLAT_ODROIDC4 */ +/* disabled: CONFIG_PLAT_PHYTIUM_PI */ +/* disabled: CONFIG_PLAT_QEMU_ARM_VIRT */ +/* disabled: CONFIG_PLAT_QUARTZ64 */ +/* disabled: CONFIG_PLAT_RK3588 */ +/* disabled: CONFIG_PLAT_ROCKPRO64 */ +/* disabled: CONFIG_PLAT_TQMA8XQP1GB */ +/* disabled: CONFIG_PLAT_TX1 */ +/* disabled: CONFIG_PLAT_TX2 */ +/* disabled: CONFIG_PLAT_ZYNQMP */ +#define CONFIG_PLAT imx8mp-evk +/* disabled: CONFIG_ARM_CORTEX_A7 */ +/* disabled: CONFIG_ARM_CORTEX_A8 */ +/* disabled: CONFIG_ARM_CORTEX_A9 */ +/* disabled: CONFIG_ARM_CORTEX_A15 */ +/* disabled: CONFIG_ARM_CORTEX_A35 */ +#define CONFIG_ARM_CORTEX_A53 1 +/* disabled: CONFIG_ARM_CORTEX_A55 */ +/* disabled: CONFIG_ARM_CORTEX_A57 */ +/* disabled: CONFIG_ARM_CORTEX_A72 */ +/* disabled: CONFIG_ARCH_ARM_V7A */ +/* disabled: CONFIG_ARCH_ARM_V7VE */ +#define CONFIG_ARCH_ARM_V8A 1 +/* disabled: CONFIG_AARCH64_SERROR_IGNORE */ +#define CONFIG_ARM_MACH imx +/* disabled: CONFIG_KERNEL_MCS */ +#define CONFIG_ARM_PA_SIZE_BITS_40 1 +/* disabled: CONFIG_ARM_PA_SIZE_BITS_44 */ +#define CONFIG_ARM_ICACHE_VIPT 1 +/* disabled: CONFIG_DEBUG_DISABLE_L2_CACHE */ +/* disabled: CONFIG_DEBUG_DISABLE_L1_ICACHE */ +/* disabled: CONFIG_DEBUG_DISABLE_L1_DCACHE */ +/* disabled: CONFIG_DEBUG_DISABLE_BRANCH_PREDICTION */ +/* disabled: CONFIG_ARM_HYPERVISOR_SUPPORT */ +#define CONFIG_ARM_GIC_V3_SUPPORT 1 +/* disabled: CONFIG_AARCH64_VSPACE_S2_START_L1 */ +/* disabled: CONFIG_ARM_HYP_ENABLE_VCPU_CP14_SAVE_AND_RESTORE */ +/* disabled: CONFIG_ARM_ERRATA_430973 */ +/* disabled: CONFIG_ARM_ERRATA_773022 */ +/* disabled: CONFIG_ARM_SMMU */ +/* disabled: CONFIG_TK1_SMMU */ +/* disabled: CONFIG_ENABLE_A9_PREFETCHER */ +/* disabled: CONFIG_EXPORT_PMU_USER */ +/* disabled: CONFIG_DISABLE_WFI_WFE_TRAPS */ +/* disabled: CONFIG_SMMU_INTERRUPT_ENABLE */ +/* disabled: CONFIG_AARCH32_FPU_ENABLE_CONTEXT_SWITCH */ +#define CONFIG_AARCH64_USER_CACHE_ENABLE 1 +/* disabled: CONFIG_ALLOW_SMC_CALLS */ +#define CONFIG_ARM_TLS_REG_TPIDRU 1 +/* disabled: CONFIG_ARM_TLS_REG_TPIDRURO */ +#define CONFIG_ARM_TLS_REG tpidru +#define CONFIG_L1_CACHE_LINE_SIZE_BITS 6 +/* disabled: CONFIG_ARM_HAS_TLB_LOCK */ +#define CONFIG_HAVE_FPU 1 +#define CONFIG_PADDR_USER_DEVICE_TOP 1099511627776 +#define CONFIG_ROOT_CNODE_SIZE_BITS 13 +#define CONFIG_TIMER_TICK_MS 2 +#define CONFIG_TIME_SLICE 5 +#define CONFIG_RETYPE_FAN_OUT_LIMIT 256 +#define CONFIG_MAX_NUM_WORK_UNITS_PER_PREEMPTION 100 +#define CONFIG_RESET_CHUNK_BITS 8 +#define CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS 230 +#define CONFIG_FASTPATH 1 +/* disabled: CONFIG_EXCEPTION_FASTPATH */ +#define CONFIG_NUM_DOMAINS 1 +/* disabled: CONFIG_SIGNAL_FASTPATH */ +#define CONFIG_NUM_PRIORITIES 256 +#define CONFIG_MAX_NUM_NODES 1 +/* disabled: CONFIG_ENABLE_SMP_SUPPORT */ +#define CONFIG_KERNEL_STACK_BITS 12 +/* disabled: CONFIG_VERIFICATION_BUILD */ +/* disabled: CONFIG_BINARY_VERIFICATION_BUILD */ +#define CONFIG_DEBUG_BUILD 1 +#define CONFIG_HARDWARE_DEBUG_API 1 +#define CONFIG_PRINTING 1 +/* disabled: CONFIG_KERNEL_INVOCATION_REPORT_ERROR_IPC */ +#define CONFIG_NO_BENCHMARKS 1 +/* disabled: CONFIG_BENCHMARK_GENERIC */ +/* disabled: CONFIG_BENCHMARK_TRACK_KERNEL_ENTRIES */ +/* disabled: CONFIG_BENCHMARK_TRACEPOINTS */ +/* disabled: CONFIG_BENCHMARK_TRACK_UTILISATION */ +#define CONFIG_KERNEL_BENCHMARK none +/* disabled: CONFIG_ENABLE_BENCHMARKS */ +/* disabled: CONFIG_KERNEL_LOG_BUFFER */ +#define CONFIG_MAX_NUM_TRACE_POINTS 0 +#define CONFIG_IRQ_REPORTING 1 +#define CONFIG_COLOUR_PRINTING 1 +#define CONFIG_USER_STACK_TRACE_LENGTH 16 +#define CONFIG_KERNEL_OPT_LEVEL_O2 1 +/* disabled: CONFIG_KERNEL_OPT_LEVEL_OS */ +/* disabled: CONFIG_KERNEL_OPT_LEVEL_O0 */ +/* disabled: CONFIG_KERNEL_OPT_LEVEL_O1 */ +/* disabled: CONFIG_KERNEL_OPT_LEVEL_O3 */ +#define CONFIG_KERNEL_OPT_LEVEL -O2 +#define CONFIG_KERNEL_OPTIMISATION_CLONE_FUNCTIONS 1 +/* disabled: CONFIG_KERNEL_FWHOLE_PROGRAM */ +/* disabled: CONFIG_DANGEROUS_CODE_INJECTION */ +/* disabled: CONFIG_DEBUG_DISABLE_PREFETCHERS */ +/* disabled: CONFIG_SET_TLS_BASE_SELF */ +/* disabled: CONFIG_CLZ_32 */ +/* disabled: CONFIG_CLZ_64 */ +/* disabled: CONFIG_CTZ_32 */ +/* disabled: CONFIG_CTZ_64 */ +/* disabled: CONFIG_CLZ_NO_BUILTIN */ +/* disabled: CONFIG_CTZ_NO_BUILTIN */ +/* disabled: CONFIG_EXPORT_PCNT_USER */ +/* disabled: CONFIG_EXPORT_VCNT_USER */ +/* disabled: CONFIG_EXPORT_PTMR_USER */ +/* disabled: CONFIG_EXPORT_VTMR_USER */ +#define CONFIG_VTIMER_UPDATE_VOFFSET 1 + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index 6362db7..d8c2914 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -10,6 +10,7 @@ #include #include +#include "hardware_config.h" /* ==================== Constant Definitions ==================== */ @@ -34,6 +35,7 @@ typedef enum { /* Queue magic number */ #define HYPERAMP_QUEUE_MAGIC 0x48415150 // "HAQP" + /* ==================== Memory Barriers and Cache Operations ==================== */ #if defined(__aarch64__) || defined(__arm__) @@ -77,6 +79,37 @@ typedef enum { } #endif +#if defined(CONFIG_PLAT_IMX8MP_EVK) || defined(CONFIG_PLAT_RK3588) + // Shared memory configuration for i.MX8MP platform + #define SHM_TX_QUEUE_PADDR 0x7E000000UL + #define SHM_RX_QUEUE_PADDR 0x7E001000UL + #define SHM_DATA_PADDR 0x7E002000UL + +#elif defined(CONFIG_PLAT_PHYTIUM_PI) + // Shared memory configuration for Phytium-Pi platform + #define SHM_TX_QUEUE_PADDR 0xDE000000UL + #define SHM_RX_QUEUE_PADDR 0xDE001000UL + #define SHM_DATA_PADDR 0xDE002000UL + +#else + #error "Unknown Platform! Please define addresses for this board." +#endif + + +// Virtual address: Start of the TX queue in Hyperamp shared memory (seL4 → Linux) +#define SHM_TX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54E000UL) + +// Virtual address: Start of the RX queue in Hyperamp shared memory (Linux → seL4) +#define SHM_RX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54F000UL) + +// Virtual address: Start of the general data region in Hyperamp shared memory +#define SHM_DATA_REGION_VA ((volatile void *)0x550000UL) + +//g_tx_queue = (volatile HyperampShmQueue *)0x54e000; +//g_rx_queue = (volatile HyperampShmQueue *)0x54f000; +//g_data_region = (volatile void *)0x550000; + + #define HYPERAMP_BARRIER() do { HYPERAMP_DMB(); HYPERAMP_DSB(); } while(0) /* ==================== Software Spinlock ==================== */ diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 41ac27b..0e69d25 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -3,6 +3,18 @@ extern FrontendEngine *p_g_fr_eng; extern FrontendEngine g_fr_eng; +static volatile HyperampShmQueue *g_hyper_tx_queue = NULL; // seL4 → Linux (seL4 writes requests, Linux reads) +static volatile HyperampShmQueue *g_hyper_rx_queue = NULL; // Linux → seL4 (seL4 reads responses, Linux writes) + +#if 0 +HyperampQueueConfig hyper_tx_config = { + .map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH, + .capacity = 256, + .block_size = 4096, + .phy_addr = SHM_TX_QUEUE_PADDR, + .virt_addr = (uint64_t)g_hyper_tx_queue, +}; +#endif /** * Configuration structure for the high-speed network receive queue. @@ -36,6 +48,37 @@ SharedMemoryPoolQueueConfig high_speed_net_tx_queue_config = { }; +/** + * Configuration structure for the Hyperamp shared transmit queue (seL4 → Linux). + * + * This global instance defines the parameters required to set up the transmit queue's + * shared memory region, including the mapping mode, physical and virtual addresses, + * queue capacity, and block size. + */ +HyperampQueueConfig hyperamp_tx_config = { + .map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH, + .capacity = 256, + .block_size = 4096, + .phy_addr = SHM_TX_QUEUE_PADDR, + .virt_addr = (uint64_t)SHM_TX_QUEUE_VADDR, +}; + + +/** + * Configuration structure for the Hyperamp shared receive queue (Linux → seL4). + * + * This global instance defines the parameters required to set up the receive queue's + * shared memory region, including the mapping mode, physical and virtual addresses, + * queue capacity, and block size. + */ +HyperampQueueConfig hyperamp_rx_config = { + .map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH, + .capacity = 256, + .block_size = 4096, + .phy_addr = SHM_RX_QUEUE_PADDR, + .virt_addr = (uint64_t)SHM_RX_QUEUE_VADDR, +}; + /** * @brief Get data from the specified RX queue (residing in shared memory) * @param queue Pointer to the SharedMemoryPoolQueue (RX queue) to operate on @@ -338,6 +381,63 @@ int engine_init_hs_net_dev(FrontendEngine *eng){ } +/** + * @brief Initializes the Hyperamp shared transmit and receive queues for the frontend engine. + * + * This function initializes both the transmit (TX) and receive (RX) shared memory queues + * used by the Hyperamp inter-OS communication layer. It uses the global configuration + * structures (e.g., tx_config and rx_config) that specify physical/virtual addresses, + * capacity, block size, and mapping mode. + * + * The initialization includes setting up queue metadata, validating memory mappings, + * and preparing the queues for message passing between seL4 and Linux. + * + * @param[in] eng Pointer to the FrontendEngine instance. Must not be NULL. + * + * @return int Status code indicating the result of the initialization: + * - @c FRONTEND_PROXY_PROCESS_OK on success. + * - @c FRONTEND_PROXY_PROCESS_ERROR if any step fails (e.g., invalid config, + * memory mapping error, or queue setup failure). + * + * @note This function assumes that the underlying shared memory regions are already + * mapped into the virtual address space of the current process. + */ +int engine_init_hyperamp_queue(FrontendEngine *eng){ + int ret; + + if(NULL == eng){ + error_print("engine_init_hyperamp_queue failed: input parameter must not be NULL\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + g_hyper_tx_queue = SHM_TX_QUEUE_VADDR; + g_hyper_rx_queue = SHM_RX_QUEUE_VADDR; + + ret = hyperamp_queue_init(g_hyper_tx_queue, &hyperamp_tx_config, 1); + + if(HYPERAMP_OK != ret){ + error_print("engine_init_hyperamp_queue failed: failed to initialize Hyperamp TX queue!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* Critical: Invalidate cache to ensure the printed data is the latest from physical memory */ + hyperamp_cache_invalidate((volatile void *)g_hyper_tx_queue, 64); + + ret = hyperamp_queue_init(g_hyper_rx_queue, &hyperamp_rx_config, 1); + + if(HYPERAMP_OK != ret){ + error_print("engine_init_hyperamp_queue failed: failed to initialize Hyperamp RX queue!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* Critical: Invalidate cache to ensure subsequent reads reflect the latest data from physical memory */ + hyperamp_cache_invalidate((volatile void *)g_hyper_rx_queue, 64); + + eng->hyper_tx_queue = g_hyper_tx_queue; + eng->hyper_rx_queue = g_hyper_rx_queue; + + return FRONTEND_PROXY_PROCESS_OK; +} /** -- Gitee From 0e77fcbb32cb1116147e65ed3f02e005f2a92f81 Mon Sep 17 00:00:00 2001 From: xj009 Date: Wed, 11 Feb 2026 10:11:05 +0800 Subject: [PATCH 039/133] Integrate Hyperamp shared memory queues into frontend proxy --- projects/sel4test/apps/front/include/dev.h | 2 +- projects/sel4test/apps/front/include/message.h | 3 ++- projects/sel4test/apps/front/src/dev.c | 2 +- projects/sel4test/apps/front/src/engine.c | 9 +++++++++ projects/sel4test/apps/front/src/frontend_proto.c | 2 ++ 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/include/dev.h b/projects/sel4test/apps/front/include/dev.h index bd002e1..8505f0e 100644 --- a/projects/sel4test/apps/front/include/dev.h +++ b/projects/sel4test/apps/front/include/dev.h @@ -52,7 +52,7 @@ typedef struct FrontendHighSpeedNetDeviceSet_ { FrontendDevInfo hs_net_dev[MAX_HS_DEV_NUM]; }FrontendHighSpeedNetDeviceSet; -FrontendDevListCfg *p_global_dev_list_cfg; +extern FrontendDevListCfg *p_global_dev_list_cfg; void frontend_init_dev_list(); diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index 29c9d43..3bb0465 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -595,7 +595,8 @@ struct SharedMemoryPoolQueue; typedef enum { MEMORY_ALLOC_SHARED, // Allocate in shared memory - MEMORY_ALLOC_CALLER // Allocated by caller + MEMORY_ALLOC_CALLER, // Memory is allocated by the caller + MEMORY_ALLOC_AMPQUEUE // Message is placed directly into the HyperAMP queue } MemoryAllocMode; int build_proxy_general_message(struct FrontendEngine_ *engine, GeneralProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, diff --git a/projects/sel4test/apps/front/src/dev.c b/projects/sel4test/apps/front/src/dev.c index ad8c11e..3e8868e 100644 --- a/projects/sel4test/apps/front/src/dev.c +++ b/projects/sel4test/apps/front/src/dev.c @@ -15,7 +15,7 @@ FrontendDevInfo dev_array[] = { } }; - +FrontendDevListCfg *p_global_dev_list_cfg = NULL; FrontendDevListCfg global_dev_list_cfg; diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 0e69d25..758e91c 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -253,6 +253,15 @@ void frontend_engine_init(){ free(p_g_fr_eng->dev_set); abort(); } + + ret = engine_init_hyperamp_queue(p_g_fr_eng); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize hyperamp memory queue!\n!"); + free(p_g_fr_eng->sess_pool); + free(p_g_fr_eng->dev_set); + abort(); + } } diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 73bf418..4907372 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -332,6 +332,8 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h } *result_msg = msg_buf; + }else if(MEMORY_ALLOC_AMPQUEUE == msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; }else{ error_print("build_proxy_general_message failed: unsupported allocte mode!"); return FRONTEND_PROXY_PROCESS_ERROR; -- Gitee From 57426e8cb2b55595dd1ef4673e36db7b505bd60d Mon Sep 17 00:00:00 2001 From: xj009 Date: Wed, 11 Feb 2026 10:53:00 +0800 Subject: [PATCH 040/133] fix some bugs --- projects/sel4test/apps/front/src/frontend_proto.c | 1 + projects/sel4test/apps/front/src/session_pool.c | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 4907372..8fbbd54 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -333,6 +333,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h *result_msg = msg_buf; }else if(MEMORY_ALLOC_AMPQUEUE == msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; }else{ error_print("build_proxy_general_message failed: unsupported allocte mode!"); diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c index 5d53be7..98557a2 100644 --- a/projects/sel4test/apps/front/src/session_pool.c +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -154,9 +154,10 @@ int frontend_high_speed_init_pool(struct FrontendSessionPool *pool) TAILQ_INIT(&pool->queue_f2b); TAILQ_INIT(&pool->queue_b2f); - pool->htable = NULL; - pool->ops = &fr_high_speed_pool_ops; - pool->engine = frontend_get_global_engine(); + pool->htable = NULL; + pool->ops = &fr_high_speed_pool_ops; + pool->engine = frontend_get_global_engine(); + front_high_speed_pool = pool; return FRONTEND_PROXY_PROCESS_OK; } -- Gitee From 2229e99cffa38b607700fe5df22af31e6b3b5932 Mon Sep 17 00:00:00 2001 From: xj009 Date: Wed, 11 Feb 2026 18:18:31 +0800 Subject: [PATCH 041/133] Integrate Hyperamp shared memory queues into frontend proxy --- projects/sel4test/apps/front/include/engine.h | 39 +++++++++++++++ .../apps/front/include/hyperamp_shm_queue.h | 5 ++ projects/sel4test/apps/front/src/engine.c | 47 +++++++++++++++++-- .../sel4test/apps/front/src/frontend_proto.c | 27 +++++++++-- 4 files changed, 109 insertions(+), 9 deletions(-) diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index ff52c1f..a7cdb64 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -11,6 +11,10 @@ #define HS_NET_DEV_CFG "hs_net_dev.ini" + +extern volatile HyperampShmQueue *g_hyper_tx_queue; // seL4 → Linux (seL4 writes requests, Linux reads) +extern volatile HyperampShmQueue *g_hyper_rx_queue; // Linux → seL4 (seL4 reads responses, Linux writes) +extern volatile void *g_hyper_data_region; // Shared data buffer referenced by entries in TX/RX queues /* * Check if the string is a valid IPv4 address * Parameter: ip_str - string to check @@ -276,6 +280,41 @@ int frontend_engine_tx_queue_send(struct SharedMemoryPoolQueue *queue, const voi uint8_t *frontend_engine_rx_queue_get_msg(struct SharedMemoryPoolQueue *queue, size_t max_msg_len, int *ret, size_t *out_len); + +/** + * @brief Retrieves a message from the HyperAMP receive queue managed by the FrontendEngine. + * + * @details This function fetches message data from the HyperAMP RX queue associated with the given + * FrontendEngine instance. It returns a pointer to the message buffer if data is available, + * and outputs the operation status and actual message length through output parameters. + * The caller must check the status code stored in @p ret before using the returned buffer + * and message length. + * + * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL + * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow + * @param[out] ret Pointer to an integer variable that stores the operation status code + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * + * @return Pointer to the uint8_t message data buffer on successful data retrieval; + * NULL if the operation fails, the queue is empty, or a system error occurs + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized), + * the returned pointer is NULL and @p out_len is undefined + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message data is temporarily unavailable (e.g., HyperAMP RX queue is empty), + * the returned pointer is NULL and @p out_len is undefined + * + * @note The underlying message buffer resides in shared memory (e.g., @c g_hyper_data_region). + * The ownership and release semantics are determined by the HyperAMP queue protocol; + * typically, the buffer remains valid until the next receive operation or explicit acknowledgment. + * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. + */ +uint8_t *frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, + int *ret, size_t *out_len); + struct FrontendEngOps *get_hs_frontend_engine_ops(); diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index d8c2914..16e26a7 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -19,6 +19,11 @@ #define HYPERAMP_OK 0 #define HYPERAMP_ERROR (-1) +#define HYPERAMP_AGAIN 1 + +#define HYPERAMP_ZONEID_ROOTLINUX 0 +#define HYPERAMP_ZONEID_SEL4 1 + /* Memory mapping modes */ typedef enum { diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 758e91c..af83d88 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -3,8 +3,9 @@ extern FrontendEngine *p_g_fr_eng; extern FrontendEngine g_fr_eng; -static volatile HyperampShmQueue *g_hyper_tx_queue = NULL; // seL4 → Linux (seL4 writes requests, Linux reads) -static volatile HyperampShmQueue *g_hyper_rx_queue = NULL; // Linux → seL4 (seL4 reads responses, Linux writes) +volatile HyperampShmQueue *g_hyper_tx_queue = NULL; // seL4 → Linux (seL4 writes requests, Linux reads) +volatile HyperampShmQueue *g_hyper_rx_queue = NULL; // Linux → seL4 (seL4 reads responses, Linux writes) +volatile void *g_hyper_data_region = NULL; // Shared data buffer referenced by entries in TX/RX queues #if 0 HyperampQueueConfig hyper_tx_config = { @@ -131,6 +132,43 @@ int frontend_engine_rx_queue_get(struct SharedMemoryPoolQueue *queue, void **buf } +/** + * @brief Retrieves a message from the HyperAMP receive queue managed by the FrontendEngine. + * + * @details This function fetches message data from the HyperAMP RX queue associated with the given + * FrontendEngine instance. It returns a pointer to the message buffer if data is available, + * and outputs the operation status and actual message length through output parameters. + * The caller must check the status code stored in @p ret before using the returned buffer + * and message length. + * + * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL + * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow + * @param[out] ret Pointer to an integer variable that stores the operation status code + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * + * @return Pointer to the uint8_t message data buffer on successful data retrieval; + * NULL if the operation fails, the queue is empty, or a system error occurs + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized), + * the returned pointer is NULL and @p out_len is undefined + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message data is temporarily unavailable (e.g., HyperAMP RX queue is empty), + * the returned pointer is NULL and @p out_len is undefined + * + * @note The underlying message buffer resides in shared memory (e.g., @c g_hyper_data_region). + * The ownership and release semantics are determined by the HyperAMP queue protocol; + * typically, the buffer remains valid until the next receive operation or explicit acknowledgment. + * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. + */ +uint8_t *frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, + int *ret, size_t *out_len){ + return NULL; +} + + /** * @brief Retrieves a message from the shared memory pool queue. * @@ -419,8 +457,9 @@ int engine_init_hyperamp_queue(FrontendEngine *eng){ return FRONTEND_PROXY_PROCESS_ERROR; } - g_hyper_tx_queue = SHM_TX_QUEUE_VADDR; - g_hyper_rx_queue = SHM_RX_QUEUE_VADDR; + g_hyper_tx_queue = SHM_TX_QUEUE_VADDR; + g_hyper_rx_queue = SHM_RX_QUEUE_VADDR; + g_hyper_data_region = SHM_DATA_REGION_VA; ret = hyperamp_queue_init(g_hyper_tx_queue, &hyperamp_tx_config, 1); diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 8fbbd54..08496d0 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -2,6 +2,7 @@ #include "frontend_proto.h" +uint8_t global_amp_tx_buf[HYPERAMP_MSG_HDR_PLUS_MAX_SIZE]; /* * Functions for building sub-type proxy messages. @@ -293,7 +294,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h } outer_msg_type = header->outer_header.proxy_msg_type; - header->inner_header.dev_hdr.payload_len; +// header->inner_header.dev_hdr.payload_len; /* @@ -327,16 +328,21 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h msg_buf = malloc(alloc_size); if(NULL == msg_buf){ - error_print("insufficient memory for allocation!\n"); + error_print("build_proxy_general_message failed: insufficient memory for allocation!\n"); return FRONTEND_PROXY_PROCESS_ERROR; } *result_msg = msg_buf; }else if(MEMORY_ALLOC_AMPQUEUE == msg_buf){ - - return FRONTEND_PROXY_PROCESS_OK; + if(NULL == engine->hyper_tx_queue){ + error_print("build_proxy_general_message failed: HyperAMP TX queue is not initialized!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + memset(global_amp_tx_buf, 0, sizeof(global_amp_tx_buf)); + msg_buf = global_amp_tx_buf; + *result_msg = msg_buf; }else{ - error_print("build_proxy_general_message failed: unsupported allocte mode!"); + error_print("build_proxy_general_message failed: unsupported allocte mode!\n"); return FRONTEND_PROXY_PROCESS_ERROR; } @@ -428,6 +434,17 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h debug_cnt++; } #endif + +/* + * In MEMORY_ALLOC_AMPQUEUE mode, the created message should be pushed into the HyperAMP shared queue. + */ + if(MEMORY_ALLOC_AMPQUEUE == alloc_mode){ + g_hyper_data_region; + msg_buf -= sizeof(ProxyMsgHeader); + ret = hyperamp_queue_enqueue(engine->hyper_tx_queue, HYPERAMP_ZONEID_SEL4, msg_buf, payload_len + proxy_msg_payload_len + sizeof(ProxyMsgHeader), g_hyper_data_region); + return ret; + } + return FRONTEND_PROXY_PROCESS_OK; } #endif -- Gitee From 6683e39545b60d52061650923bf9d82b2efe2ec1 Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 12 Feb 2026 14:24:40 +0800 Subject: [PATCH 042/133] Integrate Hyperamp shared memory queues into frontend proxy --- projects/sel4test/apps/front/include/engine.h | 42 +++++++----- projects/sel4test/apps/front/src/engine.c | 68 +++++++++++++------ .../sel4test/apps/front/src/frontend_proto.c | 13 +++- 3 files changed, 83 insertions(+), 40 deletions(-) diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index a7cdb64..38db833 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -285,35 +285,41 @@ uint8_t *frontend_engine_rx_queue_get_msg(struct SharedMemoryPoolQueue *queue, s * @brief Retrieves a message from the HyperAMP receive queue managed by the FrontendEngine. * * @details This function fetches message data from the HyperAMP RX queue associated with the given - * FrontendEngine instance. It returns a pointer to the message buffer if data is available, - * and outputs the operation status and actual message length through output parameters. - * The caller must check the status code stored in @p ret before using the returned buffer - * and message length. + * FrontendEngine instance, and copies the message data to the provided buffer pointed to by @p data. + * It returns an integer status code to indicate the operation result, and outputs the actual + * length of the retrieved message through the @p out_len parameter. + * The caller must check the returned status code before using the data in @p data and the value in @p out_len. * * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL - * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow - * @param[out] ret Pointer to an integer variable that stores the operation status code - * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * @param[in] max_msg_len Maximum allowed length of the message to read (i.e., the size of the @p data buffer), + * used to prevent buffer overflow + * @param[out] data Pointer to a uint8_t buffer that stores the retrieved message data; + * valid only when the return value is FRONTEND_PROXY_PROCESS_OK + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message; + * valid only when the return value is FRONTEND_PROXY_PROCESS_OK * - * @return Pointer to the uint8_t message data buffer on successful data retrieval; - * NULL if the operation fails, the queue is empty, or a system error occurs + * @return Integer status code indicating the result of the operation: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: System-level error occurred + * - FRONTEND_PROXY_PROCESS_AGAIN: Message temporarily unavailable * * @retval FRONTEND_PROXY_PROCESS_OK - * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * Message data is retrieved successfully, the @p data buffer contains valid message content + * and @p out_len holds the actual message length * @retval FRONTEND_PROXY_PROCESS_ERROR - * A system-level error occurred (e.g., engine is NULL, internal queue not initialized), - * the returned pointer is NULL and @p out_len is undefined + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized, @p data/@p out_len is NULL), + * the @p data buffer and @p out_len are undefined * @retval FRONTEND_PROXY_PROCESS_AGAIN * Message data is temporarily unavailable (e.g., HyperAMP RX queue is empty), - * the returned pointer is NULL and @p out_len is undefined + * the @p data buffer and @p out_len are undefined * - * @note The underlying message buffer resides in shared memory (e.g., @c g_hyper_data_region). - * The ownership and release semantics are determined by the HyperAMP queue protocol; - * typically, the buffer remains valid until the next receive operation or explicit acknowledgment. + * @note The message data copied to @p data is sourced from shared memory (e.g., @c g_hyper_data_region). + * The ownership of the @p data buffer is held by the caller (who is responsible for allocating/freeing it), + * while the underlying shared memory lifecycle follows the HyperAMP queue protocol. * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. */ -uint8_t *frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, - int *ret, size_t *out_len); +int frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, + uint8_t *data, size_t *out_len); struct FrontendEngOps *get_hs_frontend_engine_ops(); diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index af83d88..12426e3 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -136,36 +136,66 @@ int frontend_engine_rx_queue_get(struct SharedMemoryPoolQueue *queue, void **buf * @brief Retrieves a message from the HyperAMP receive queue managed by the FrontendEngine. * * @details This function fetches message data from the HyperAMP RX queue associated with the given - * FrontendEngine instance. It returns a pointer to the message buffer if data is available, - * and outputs the operation status and actual message length through output parameters. - * The caller must check the status code stored in @p ret before using the returned buffer - * and message length. + * FrontendEngine instance, and copies the message data to the provided buffer pointed to by @p data. + * It returns an integer status code to indicate the operation result, and outputs the actual + * length of the retrieved message through the @p out_len parameter. + * The caller must check the returned status code before using the data in @p data and the value in @p out_len. * * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL - * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow - * @param[out] ret Pointer to an integer variable that stores the operation status code - * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * @param[in] max_msg_len Maximum allowed length of the message to read (i.e., the size of the @p data buffer), + * used to prevent buffer overflow + * @param[out] data Pointer to a uint8_t buffer that stores the retrieved message data; + * valid only when the return value is FRONTEND_PROXY_PROCESS_OK + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message; + * valid only when the return value is FRONTEND_PROXY_PROCESS_OK * - * @return Pointer to the uint8_t message data buffer on successful data retrieval; - * NULL if the operation fails, the queue is empty, or a system error occurs + * @return Integer status code indicating the result of the operation: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: System-level error occurred + * - FRONTEND_PROXY_PROCESS_AGAIN: Message temporarily unavailable * * @retval FRONTEND_PROXY_PROCESS_OK - * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * Message data is retrieved successfully, the @p data buffer contains valid message content + * and @p out_len holds the actual message length * @retval FRONTEND_PROXY_PROCESS_ERROR - * A system-level error occurred (e.g., engine is NULL, internal queue not initialized), - * the returned pointer is NULL and @p out_len is undefined + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized, @p data/@p out_len is NULL), + * the @p data buffer and @p out_len are undefined * @retval FRONTEND_PROXY_PROCESS_AGAIN * Message data is temporarily unavailable (e.g., HyperAMP RX queue is empty), - * the returned pointer is NULL and @p out_len is undefined + * the @p data buffer and @p out_len are undefined * - * @note The underlying message buffer resides in shared memory (e.g., @c g_hyper_data_region). - * The ownership and release semantics are determined by the HyperAMP queue protocol; - * typically, the buffer remains valid until the next receive operation or explicit acknowledgment. + * @note The message data copied to @p data is sourced from shared memory (e.g., @c g_hyper_data_region). + * The ownership of the @p data buffer is held by the caller (who is responsible for allocating/freeing it), + * while the underlying shared memory lifecycle follows the HyperAMP queue protocol. * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. */ -uint8_t *frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, - int *ret, size_t *out_len){ - return NULL; +int frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, + uint8_t *data, size_t *out_len){ + int ret; + if(NULL == eng || NULL == eng->hyper_rx_queue || NULL == data || NULL == out_len){ + error_print("frontend_engine_hyperamp_rx_queue_get failed: invalid input parameters (NULL pointers)\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(NULL == g_hyper_data_region){ + error_print("frontend_engine_hyperamp_rx_queue_get failed: The HyperAMP shared memory region is not initialized!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + ret = hyperamp_queue_dequeue(eng->hyper_rx_queue, HYPERAMP_ZONEID_SEL4, data, max_msg_len, out_len, g_hyper_data_region); + + if(HYPERAMP_ERROR == ret){ + error_print("frontend_engine_hyperamp_rx_queue_get failed: hyperamp_queue_dequeue execution failed!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + }else if(HYPERAMP_AGAIN == ret){ + return FRONTEND_PROXY_PROCESS_AGAIN; + }else{ +/* + * hyperamp_queue_dequeue execution succeeded, no action required. + */ + } + + return FRONTEND_PROXY_PROCESS_OK; } diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 08496d0..990eac9 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -391,7 +391,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h } if(FRONTEND_PROXY_PROCESS_OK != ret){ - error_print("build_proxy_general_message failed: failed to build proxy message!"); + error_print("build_proxy_general_message failed: failed to build proxy message!\n"); // free_shared_mem(engine->mem_pool, mem_addr); SHM_POOL_QUEUE_HEAD_ROLLBACK(ring_buf); return FRONTEND_PROXY_PROCESS_ERROR; @@ -442,9 +442,16 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h g_hyper_data_region; msg_buf -= sizeof(ProxyMsgHeader); ret = hyperamp_queue_enqueue(engine->hyper_tx_queue, HYPERAMP_ZONEID_SEL4, msg_buf, payload_len + proxy_msg_payload_len + sizeof(ProxyMsgHeader), g_hyper_data_region); - return ret; - } + if(HYPERAMP_OK == ret){ + return FRONTEND_PROXY_PROCESS_OK; + }else if(HYPERAMP_AGAIN == ret){ + return FRONTEND_PROXY_PROCESS_AGAIN; + }else{ + error_print("build_proxy_general_message failed: faild to push message into the HyperAmp queue!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + } return FRONTEND_PROXY_PROCESS_OK; } #endif -- Gitee From 71e18ee45ea84fc854830dde16d81547e1c5608e Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:12:12 +0800 Subject: [PATCH 043/133] upload source codes --- .../apps/front/include/common_utils.h | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 projects/sel4test/apps/front/include/common_utils.h diff --git a/projects/sel4test/apps/front/include/common_utils.h b/projects/sel4test/apps/front/include/common_utils.h new file mode 100644 index 0000000..f833833 --- /dev/null +++ b/projects/sel4test/apps/front/include/common_utils.h @@ -0,0 +1,51 @@ +#ifndef COMMON_UTILS_H_ +#define COMMON_UTILS_H_ + +#include +#include +#include + +#define UTILS_ENABLE_DEBUG 1 + +void error_print(char *error); + +int debug_print(const char *format, ...); + +#if UTILS_ENABLE_DEBUG +#define utils_print(...) printf(__VA_ARGS__) +#else +#define utils_print(...) do {} while(0) +#endif + + +/** + * @brief Print the content of a uint8_t array in the specified format + * @param BUF Pointer to the start of the uint8_t array (must be a valid pointer) + * @param SIZE Number of elements in the array (must be a non-negative integer, preferably of type size_t) + * @param FORMAT printf-style format string (must match the uint8_t type, e.g., "%02hhx ", "%hhu ", "%hho ", "%c") + * + * Notes: + * 1. The format specifier must use the "hh" length modifier for uint8_t (to avoid sign extension issues): + * - Decimal: %hhu (unsigned) + * - Hexadecimal: %hhx (lowercase), %hhX (uppercase) + * - Octal: %hho + * - ASCII: %c (Note: Non-printable characters may display abnormally; handle them as needed) + * 2. Iterates from index 0 to SIZE-1, outputting each element in the specified FORMAT + * 3. Automatically adds a newline after output to separate different buffer contents + */ +#define DUMP_BUFFER_CONTENT(BUF, SIZE, FORMAT) do { \ + /* Boundary check: Return directly if SIZE is 0 to avoid invalid loops */ \ + if ((SIZE) == 0) { \ + printf("Buffer is empty (size = 0)\n"); \ + break; \ + } \ + /* Traverse the array and output each element in the specified format */ \ + for (size_t i = 0; i < (SIZE); ++i) { \ + /* Force cast to uint8_t* to ensure type correctness and avoid pointer type mismatch */ \ + printf(FORMAT, ((const uint8_t*)(BUF))[i]); \ + } \ + /* Add a newline after output to distinguish between different buffer outputs */ \ + printf("\n"); \ +} while (0) + +#endif \ No newline at end of file -- Gitee From 25b74f5f5e9a9eee45e6aff4819ea429cd42c10d Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:24:05 +0800 Subject: [PATCH 044/133] upload source codes --- projects/sel4test/apps/front/include/dev.h | 59 + projects/sel4test/apps/front/include/engine.h | 280 ++++ .../apps/front/include/frontend_api.h | 23 + .../apps/front/include/frontend_proto.h | 80 ++ .../sel4test/apps/front/include/message.h | 609 +++++++++ projects/sel4test/apps/front/include/queue.h | 596 +++++++++ .../apps/front/include/senario_test.h | 34 + .../sel4test/apps/front/include/session.h | 343 +++++ .../apps/front/include/session_pool.h | 63 + .../apps/front/include/shared_mem_io.h | 567 ++++++++ projects/sel4test/apps/front/include/uthash.h | 1137 +++++++++++++++++ 11 files changed, 3791 insertions(+) create mode 100644 projects/sel4test/apps/front/include/dev.h create mode 100644 projects/sel4test/apps/front/include/engine.h create mode 100644 projects/sel4test/apps/front/include/frontend_api.h create mode 100644 projects/sel4test/apps/front/include/frontend_proto.h create mode 100644 projects/sel4test/apps/front/include/message.h create mode 100644 projects/sel4test/apps/front/include/queue.h create mode 100644 projects/sel4test/apps/front/include/senario_test.h create mode 100644 projects/sel4test/apps/front/include/session.h create mode 100644 projects/sel4test/apps/front/include/session_pool.h create mode 100644 projects/sel4test/apps/front/include/shared_mem_io.h create mode 100644 projects/sel4test/apps/front/include/uthash.h diff --git a/projects/sel4test/apps/front/include/dev.h b/projects/sel4test/apps/front/include/dev.h new file mode 100644 index 0000000..bd002e1 --- /dev/null +++ b/projects/sel4test/apps/front/include/dev.h @@ -0,0 +1,59 @@ +#ifndef DEV_H_ +#define DEV_H_ + +#define MAX_DEV_NAME 32 +#define MAX_HS_DEV_NUM 16 +#define MIN_HS_DEV_NUM 1 + + +/* + * High Speed Network device type enumeration + * Contains five common types of network devices + */ +typedef enum { + TRADITIONAL_ETHERNET = 0, // Traditional Ethernet device + TSN, // Time-Sensitive Networking device + WIFI, // WiFi wireless network device + LTE_MODULE, // 4G module (LTE technology standard) + NR_MODULE // 5G module (NR technology standard) +} HSNetDevType; + + +/** + * @brief Frontend device basic information structure + * @details This structure stores the core configuration and status parameters of a single frontend device, + * including unique identifier, type, running status and human-readable device name. + * It is used as the basic data unit in the device list management module. + * @note The name array is a fixed-length character buffer, and the valid string must be terminated with a null character ('\0'). + * @note The values of dev_type and dev_status are usually defined by enumerated types or global macros for unified management. + */ +typedef struct FrontendDevInfo_{ + int dev_id; + int dev_type; + int dev_status; + char name[MAX_DEV_NAME]; +}FrontendDevInfo; + + +/** + * @brief Configuration structure for frontend device list management + * @details This structure aggregates the global configuration data for a set of frontend devices. It defines the total number of devices and references a contiguous array + * that stores the detailed configuration of each individual device. + * @note The memory of the array pointed by @p dev_info can be allocated statically, globally, on the stack, or dynamically. Memory management (allocation and deallocation) + * is the responsibility of the caller. The number of valid array elements must match the value of @p dev_num to avoid out-of-bounds access. + */ +typedef struct FrontendDevListCfg_{ + int dev_num; + FrontendDevInfo *dev_info; +}FrontendDevListCfg; + + +typedef struct FrontendHighSpeedNetDeviceSet_ { + FrontendDevInfo hs_net_dev[MAX_HS_DEV_NUM]; +}FrontendHighSpeedNetDeviceSet; + +FrontendDevListCfg *p_global_dev_list_cfg; + +void frontend_init_dev_list(); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h new file mode 100644 index 0000000..f299d05 --- /dev/null +++ b/projects/sel4test/apps/front/include/engine.h @@ -0,0 +1,280 @@ +#ifndef ENGINE_H +#define ENGINE_H + +// #include +// #include + +#include "dev.h" +#include "session_pool.h" +#include "shared_mem_io.h" +#include "common_utils.h" + + +#define HS_NET_DEV_CFG "hs_net_dev.ini" +/* + * Check if the string is a valid IPv4 address + * Parameter: ip_str - string to check + * Returns: 1 - valid IPv4, 0 - invalid + */ +#define DEV_IS_IPV4(ip_str) ({ \ + struct in_addr addr; \ + inet_pton(AF_INET, (ip_str), &addr) == 1; \ +}) + +/* + * Check if the string is a valid IPv6 address + * Parameter: ip_str - string to check + * Returns: 1 - valid IPv4, 0 - invalid + */ +#define DEV_IS_IPV6(ip_str) ({ \ + struct in6_addr addr; \ + inet_pton(AF_INET6, (ip_str), &addr) == 1; \ +}) + +/* + * Check if the string is a valid IP address (IPv4 or IPv6) + * Parameter: ip_str - string to check + * Returns: 1 - valid IP, 0 - invalid + */ +#define DEV_IS_VALID_IP(ip_str) (DEV_IS_IPV4(ip_str) || DEV_IS_IPV6(ip_str)) + +/* + * Get the IP address type + * Parameter: ip_str - string to check + * Returns: SESS_NON_IP_PROTO - invalid, SESS_IPV4_PROTO - IPv4, SESS_IPV6_PROTO - IPv6 + */ +#define DEV_IP_TYPE(ip_str) ({ \ + int type = SESS_NON_IP_PROTO; \ + if (DEV_IS_IPV4(ip_str)) { \ + type = SESS_IPV4_PROTO; \ + } else if (DEV_IS_IPV6(ip_str)) { \ + type = SESS_IPV6_PROTO; \ + } \ + type; \ +}) + + +#define DEV_IS_VALID_IP(ip_str) (DEV_IS_IPV4(ip_str) || DEV_IS_IPV6(ip_str)) + +struct FrontendEngOps; + +/** + * @brief Frontend engine command state enumeration + * + * Enumerates all possible states of device (dev) and strategy (strgy) message transmission + * between frontend engine and backend. + */ +typedef enum { + FRONTEND_CMD_READY, ///< Ready for transmission (no pending dev/strgy messages) + FRONTEND_CMD_WAITTING, ///< Dev/strgy message is being constructed, waiting for backend processing + FRONTEND_CMD_OK, ///< Dev/strgy message processed successfully by backend + FRONTEND_CMD_ERROR ///< Dev/strgy message processing failed by backend +} FrontendCmdState; + +typedef struct FrontendEngine_{ + uint16_t dev_num; + FrontendHighSpeedNetDeviceSet *dev_set; // High speed network device set + uint16_t sel_id; + uint16_t active_mask; // Show the positions of all the active high-speed network devices as a mask. + uint16_t eng_cmd_id; // Used when constructing device (dev) messages and strategy (strgy) messages, filled into the msg_id member of these messages. +/* + * Maintains the state of dev message and strgy message transmitted by the engine. Supported states: FRONTEND_CMD_READY (transmittable), FRONTEND_CMD_WAITTING (dev/strgy message + * is being constructed, waiting for backend processing), FRONTEND_CMD_OK (dev/strgy message processed successfully by backend), FRONTEND_CMD_ERROR (dev/strgy message processing + * failed by backend). The state will be restored to FRONTEND_CMD_READY after proper handling of FRONTEND_CMD_OK and FRONTEND_CMD_ERROR. + */ + FrontendCmdState eng_cmd_state; + struct FrontendSessionPool *sess_pool; // Session pool + struct SharedMemoryPool *mem_pool; // Shared memory pool + struct SharedMemoryPoolLock *mem_pool_lock; // Shared memory pool lock + struct SharedMemoryPoolQueue *rx_queue; // RX queue + struct SharedMemoryPoolQueue *tx_queue; // TX queue + struct FrontendEngOps *ops; +} FrontendEngine; + + +struct FrontendEngOps { + int (*enable_dev)(FrontendEngine *eng, uint16_t mask); // Enable devices in the high-speed network device set + int (*disable_dev)(FrontendEngine *eng, uint16_t mask); // Disable devices in the high-speed network device set + int (*query_dev)(FrontendEngine *eng, uint16_t *mask); // Query information/status of devices in the high-speed network device set + // int (*choose_dev)(FrontendEngine *eng, uint16_t *dev_id); // Choose the most appropriate high-speed network device over which to establish a session, and record its device ID + int (*conf_dev_selector)(FrontendEngine *eng, uint16_t sel_id); // Configure the device selection function/strategy for the high-speed network device set + int (*query_dev_sel_id)(FrontendEngine *eng, uint16_t *sel_id); // Query the ID of the current device selector in the backend engine +}; + +#define HS_DEV_SELECTOR_NAME_LEN 30 +#define HS_DEV_SELECTOR_NUM 3 + + +typedef struct{ + char sel_name[HS_DEV_SELECTOR_NAME_LEN]; + int (*choose_dev)(FrontendEngine *eng, uint16_t *dev_id); +} HSDevSelector; + + +extern FrontendEngine *p_g_fr_eng; + + +void frontend_engine_init(); +void frontend_engine_run(); +void frontend_engine_destory(); + +FrontendEngine *frontend_get_global_engine(); + +int frontend_engine_init_eng_ops(FrontendEngine *eng); +int engine_init_selector(FrontendEngine *eng); + + +int engine_init_hs_net_dev(FrontendEngine *eng); +int engine_init_sess_pool(FrontendEngine *eng); +int engine_init_shared_mem_pool(FrontendEngine *eng); +int engine_init_shared_mem_pool_lock(FrontendEngine *eng); +int engine_init_shared_mem_queue(FrontendEngine *eng); + + +void engine_destory_hs_net_dev(FrontendEngine *eng); +void engine_destory_sess_pool(FrontendEngine *eng); +void engine_destory_mem_pool(FrontendEngine *eng); +void engine_destory_mem_pool_lock(FrontendEngine *eng); + +int engine_choose_hs_net(FrontendEngine *eng, int *selected_dev_id); + + +/** + * @brief Get the f2b queue from FrontendEngine's session pool + * + * This macro retrieves the f2b queue from the FrontendSessionPool associated with a FrontendEngine. + * It includes null pointer checks for the engine and its session pool. Error messages are printed + * via error_print() when checks fail, with the macro name included for debugging. + * + * @param engine Pointer to a FrontendEngine structure; must not be NULL for valid queue retrieval + * @param result_var Variable to store the result (pointer to FrontendSessionQueue or NULL) + * + * @note The result is stored in the provided result_var, which should be of type + * 'struct FrontendSessionQueue *' + */ +#define FRONTEND_ENGINE_GET_F2B_QUEUE(engine, result_var) \ + do { \ + /* Initialize result to NULL by default */ \ + (result_var) = NULL; \ + \ + /* Check if FrontendEngine pointer is NULL */ \ + utils_print("In FRONTEND_ENGINE_GET_F2B_QUEUE, address of engine is %p \n", engine); \ + if (!(engine)) { \ + error_print("[FRONTEND_ENGINE_GET_F2B_QUEUE] Error:FrontendEngine pointer is NULL when getting f2b queue"); \ + } \ + /* Check if session pool within frontEngine is NULL */ \ + else if (!(engine)->sess_pool) { \ + error_print("[FRONTEND_ENGINE_GET_F2B_QUEUE] Error: FrontendSessionPool in frontEngine is NULL when getting f2b queue"); \ + } \ + /* All checks passed - retrieve the f2b queue */ \ + else { \ + (result_var) = &(engine)->sess_pool->queue_f2b; \ + } \ + } while (0) + +/** + * @brief Get the b2f queue from FrontendEngine's session pool + * + * This macro retrieves the b2f queue from the FrontendSessionPool associated with a FrontendEngine. + * It includes null pointer checks for the engine and its session pool. Error messages are printed + * via error_print() when checks fail, with the macro name included for debugging. + * + * @param engine Pointer to a FrontendEngine structure; must not be NULL for valid queue retrieval + * @param result_var Variable to store the result (pointer to FrontendSessionQueue or NULL) + * + * @note The result is stored in the provided result_var, which should be of type + * 'struct FrontendSessionQueue *' + */ +#define FRONTEND_ENGINE_GET_B2F_QUEUE(engine, result_var) \ + do { \ + /* Initialize result to NULL by default */ \ + (result_var) = NULL; \ + \ + /* Check if FrontendEngine pointer is NULL */ \ + if (!(engine)) { \ + error_print("[FRONTEND_ENGINE_GET_B2F_QUEUE] Error: FrontendEngine pointer is NULL when getting b2f queue"); \ + } \ + /* Check if session pool within FrontendEngine is NULL */ \ + else if (!(engine)->sess_pool) { \ + error_print("[FRONTEND_ENGINE_GET_B2F_QUEUE] Error: FrontendSessionPool in FrontendEngine is NULL when getting b2f queue"); \ + } \ + /* All checks passed - retrieve the b2f queue */ \ + else { \ + (result_var) = &(engine)->sess_pool->queue_b2f; \ + } \ + } while (0) + + +/** + * @brief Get data from the specified RX queue (residing in shared memory) + * @param queue Pointer to the SharedMemoryPoolQueue (RX queue) to operate on + * @param[out] buf_ptr Double pointer to store the address of data in shared memory + * (points to actual data location in shared memory on success) + * @param buf_max_len Maximum allowed length of data that can be retrieved (in bytes) + * @param[out] out_len Pointer to store the actual length of obtained data (in bytes) + * @return FRONTEND_PROXY_PROCESS_OK if data is retrieved successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., invalid queue handle); + * FRONTEND_PROXY_PROCESS_AGAIN if data is temporarily unavailable (e.g., queue is empty) + * @note The caller is responsible for managing the lock of the shared memory pool + * (lock once before multiple calls to reduce overhead) + */ +int frontend_engine_rx_queue_get(struct SharedMemoryPoolQueue *queue, void **buf_ptr, + size_t buf_max_len, size_t *out_len); + +/** + * @brief Send data through the specified TX queue (residing in shared memory) + * @param queue Pointer to the SharedMemoryPoolQueue (TX queue) to operate on + * @param[in] data_ptr Double pointer to the data in shared memory to be sent + * (points to actual data location in shared memory) + * @param data_len Length of the data to be sent (in bytes) + * @param[out] sent_len Pointer to store the actual length of data sent (in bytes) + * @return FRONTEND_PROXY_PROCESS_OK if data is sent successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., queue access violation); + * FRONTEND_PROXY_PROCESS_AGAIN if data cannot be sent temporarily (e.g., queue is full) + * @note The caller is responsible for managing the lock of the shared memory pool + * (lock once before multiple calls to reduce overhead) + */ +int frontend_engine_tx_queue_send(struct SharedMemoryPoolQueue *queue, const void **data_ptr, + size_t data_len, size_t *sent_len); + + + +/** + * @brief Retrieves a message from the shared memory pool queue. + * + * @details This function fetches message data from the specified SharedMemoryPoolQueue instance. + * It returns a pointer to the message buffer if data is available, and outputs the + * operation status and actual message length through output parameters. + * The caller should check the status code stored in @p ret before using the returned buffer + * and message length. + * + * @param[in] queue Pointer to the SharedMemoryPoolQueue instance, cannot be NULL + * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow + * @param[out] ret Pointer to an integer variable that stores the operation status code + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * + * @return Pointer to the uint8_t message data buffer on successful data retrieval; + * NULL if the operation fails, the queue is empty, or a system error occurs + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., invalid queue handle, NULL input pointers), + * the returned pointer is NULL and @p out_len is undefined + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message data is temporarily unavailable (e.g., queue is empty), + * the returned pointer is NULL and @p out_len is undefined + * + * @note The ownership and release mechanism of the returned data buffer depend on the implementation + * of the SharedMemoryPoolQueue module; refer to the module's documentation for memory management rules. + */ +uint8_t *frontend_engine_rx_queue_get_msg(struct SharedMemoryPoolQueue *queue, size_t max_msg_len, + int *ret, size_t *out_len); + +struct FrontendEngOps *get_hs_frontend_engine_ops(); + + +void frontend_engine_init(); + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/frontend_api.h b/projects/sel4test/apps/front/include/frontend_api.h new file mode 100644 index 0000000..1f321ef --- /dev/null +++ b/projects/sel4test/apps/front/include/frontend_api.h @@ -0,0 +1,23 @@ +#ifndef FRONTEND_API_H_ +#define FRONTEND_API_H_ + +#include "engine.h" +#include "session.h" +#include "session_pool.h" +#include "message.h" +#include "frontend_proto.h" +#include "common_utils.h" + + +int frontend_eng_command(struct FrontendEngine_ *eng, GeneralProxyMsgHeader *hdr, uint8_t *data, uint32_t size); + +struct FrontendSession *frontend_sess_new(struct FrontendEngine_ *eng); +int frontend_sess_connect_by_addrstr(struct FrontendSession *sess, int proto, const char *addr_str); +int frontend_sess_connect(struct FrontendSession *sess, struct SessMsgPara *para); +int frontend_sess_close(struct FrontendSession *sess); +int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t size); +int frontend_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t size); +int frontend_sess_bind_callback(struct FrontendSession *sess, SESS_EVENT_CALLBACK event_callback); + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/frontend_proto.h b/projects/sel4test/apps/front/include/frontend_proto.h new file mode 100644 index 0000000..bbaba3a --- /dev/null +++ b/projects/sel4test/apps/front/include/frontend_proto.h @@ -0,0 +1,80 @@ +#ifndef FRONTEND_PROTO_H_ +#define FRONTEND_PROTO_H_ + +#include "message.h" +#include "session.h" +#include "session_pool.h" +#include "common_utils.h" + + +#define PROXY_MSG_TYPE_VALID(x) (((x) == PROXY_MSG_TYPE_DEV) || \ + ((x) == PROXY_MSG_TYPE_STRGY) || \ + ((x) == PROXY_MSG_TYPE_SESS) || \ + ((x) == PROXY_MSG_TYPE_DATA)) + +#define PROXY_MSG_LEN_VALID(x) (((x) >= PROXY_MSG_MIN_SIZE) || \ + ((x) <= PROXY_MSG_MAX_SIZE)) + +#define PROXY_ADMIN_SESSION_ID 0 +#define FRONTEND_ADMIN_SESSION_ID PROXY_ADMIN_SESSION_ID +#define BACKEND_ADMIN_SESSION_ID PROXY_ADMIN_SESSION_ID + +#define PROXY_HANDOVER_SESSION_ID 0xFFFF +#define FRONTEND_HANDOVER_SESSION_ID PROXY_HANDOVER_SESSION_ID +#define BACKEND_HANDOVER_SESSION_ID PROXY_HANDOVER_SESSION_ID + +#define APP_SESSION_ID_VALID(x) (((x) != PROXY_ADMIN_SESSION_ID) || \ + ((x) != PROXY_HANDOVER_SESSION_ID)) + +#define DEV_ID_AUTO_HANDOVER 0xFF + + +int frontend_proxy_msg_process(uint8_t *msg); + +int frontend_proxy_dev_msg_command(uint8_t *msg); +int frontend_proxy_dev_msg_process(uint8_t *msg); +int frontend_proxy_dev_msg_process_ver1(uint16_t msg_type, uint16_t msg_id, uint16_t action_type, uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_dev_msg_process_disable_ver1(uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_dev_msg_process_enable_ver1(uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_dev_msg_process_query_ver1(uint16_t payload_len, uint8_t *msg_payload); + +int frontend_proxy_strgy_msg_process(uint8_t *msg); +int frontend_proxy_strgy_msg_response(uint8_t *msg); +int frontend_proxy_strgy_msg_process_ver1(uint16_t msg_type, uint16_t msg_id, uint16_t action_type, uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_strgy_msg_process_set_ver1(uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_strgy_msg_process_query_ver1(uint16_t payload_len, uint8_t *msg_payload); + + + +int frontend_proxy_sess_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint8_t *msg); +int frontend_proxy_sess_msg_response(uint8_t *msg); +int frontend_proxy_sess_msg_process_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t msg_type, + uint16_t action_type, uint16_t ip_version, uint16_t payload_len, + uint8_t *msg_payload); + + +int frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload); +int frontend_proxy_sess_msg_process_close_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t payload_len, uint8_t *msg_payload); + + +int __frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload); +int __frontend_proxy_sess_msg_process_close_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t payload_len, uint8_t *msg_payload); + + +int frontend_proxy_sess_msg_process_passive_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload); +int __frontend_proxy_sess_msg_process_passive_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload); + + + +int frontend_proxy_data_msg_prosess(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t data_len, uint8_t *msg); +int frontend_proxy_data_msg_recv(struct FrontendSession *sess, uint8_t *msg); +int frontend_proxy_data_msg_send(struct FrontendSession *sess, uint8_t *msg); + + + +int frontend_proxy_shmem_data_msg_recv(struct FrontendSession *sess, uint8_t **msg); +int frontend_proxy_shmem_data_msg_send(struct FrontendSession *sess, const uint8_t **msg); +int frontend_proxy_sock_data_msg_recv(struct FrontendSession *sess, uint8_t *msg); +int frontend_proxy_sock_data_msg_send(struct FrontendSession *sess, uint8_t *msg); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h new file mode 100644 index 0000000..29c9d43 --- /dev/null +++ b/projects/sel4test/apps/front/include/message.h @@ -0,0 +1,609 @@ +#ifndef MESSAGE_H +#define MESSAGE_H + +#include +#include +#include +#include "common_utils.h" + +#define PROXY_PROTO_VERSION_1 1 +// #define PROXY_MSG_TYPE_DEV 0 +// #define PROXY_MSG_TYPE_STRGY 1 +// #define PROXY_MSG_TYPE_SESS 2 +// #define PROXY_MSG_TYPE_DATA 3 + + +typedef enum { + PROXY_MSG_TYPE_DEV = 0, // Device message + PROXY_MSG_TYPE_STRGY, // Strategy message + PROXY_MSG_TYPE_SESS, // Session message + PROXY_MSG_TYPE_DATA // Data message +} ProxyMsgType; + + +#define PROXY_PROTO_DEV_VERSION_1 1 +#define PROXY_PROTO_STRGY_VERSION_1 1 +#define PROXY_PROTO_SESS_VERSION_1 1 + + + +#define PROXY_MSG_HDR_SIZE 8 +#define PROXY_MSG_MIN_SIZE 1 +#define PROXY_MSG_MAX_SIZE 4088 +// Sum of header size and maximum message size (total maximum size including header, in bytes) +#define PROXY_MSG_HDR_PLUS_MAX_SIZE (PROXY_MSG_HDR_SIZE + PROXY_MSG_MAX_SIZE) +#define PROXY_MSG_INVALID_LEN -1 + +struct IPv4Address { + uint8_t data[4]; +}__attribute__((packed)); + +struct IPv6Address { + uint8_t data[16]; +}; + +union IPAddress { + struct IPv4Address ipv4_addr; + struct IPv6Address ipv6_addr; +}__attribute__((packed)); + + +/* + * Macro: Copy IPv4 address from struct in_addr to custom struct IPv4Address + * Parameters: + * dest - Destination structure pointer (struct IPv4Address*) + * src - Source structure pointer (const struct in_addr*) + * Notes: + * 1. Converts 32-bit network byte order address to 4-byte array in host order + * 2. Includes null pointer check to prevent invalid memory access + */ +#define COPY_IN_TO_IPV4(dest, src) do { \ + if ((dest) != NULL && (src) != NULL) { \ + uint32_t addr = ntohl((src)->s_addr); \ + (dest)->data[0] = (addr >> 24) & 0xFF; \ + (dest)->data[1] = (addr >> 16) & 0xFF; \ + (dest)->data[2] = (addr >> 8) & 0xFF; \ + (dest)->data[3] = addr & 0xFF; \ + } \ +} while (0) + +/* + * Macro: Copy data from custom struct IPv4Address to struct in_addr + * Parameters: + * dest - Destination structure pointer (struct in_addr*) + * src - Source structure pointer (const struct IPv4Address*) + * Notes: + * 1. Combines 4-byte array into 32-bit value in network byte order + * 2. Reverse operation of COPY_IN_TO_IPV4 macro + */ +#define COPY_IPV4_TO_IN(dest, src) do { \ + if ((dest) != NULL && (src) != NULL) { \ + uint32_t addr = ((uint32_t)(src)->data[0] << 24) | \ + ((uint32_t)(src)->data[1] << 16) | \ + ((uint32_t)(src)->data[2] << 8) | \ + (uint32_t)(src)->data[3]; \ + (dest)->s_addr = htonl(addr); \ + } \ +} while (0) + + +/* + * Macro: Copy IPv6 address from struct in6_addr to custom struct IPv6Address + * Parameters: + * dest - Destination structure pointer (struct IPv6Address*) + * src - Source structure pointer (const struct in6_addr*) + * Notes: + * 1. Internally uses memcpy to copy 16 bytes of data (their memory layouts are compatible) + * 2. Includes null pointer check to avoid accessing null pointers + */ +#define COPY_IN6_TO_IPV6(dest, src) do { \ + if ((dest) != NULL && (src) != NULL) { \ + memcpy((dest)->data, (src)->s6_addr, 16); \ + } \ +} while (0) + +/* + * Macro: Copy data from custom struct IPv6Address to struct in6_addr + * Parameters: + * dest - Destination structure pointer (struct in6_addr*) + * src - Source structure pointer (const struct IPv6Address*) + * Notes: + * Reverse copy, functionally symmetric to the above macro + */ +#define COPY_IPV6_TO_IN6(dest, src) do { \ + if ((dest) != NULL && (src) != NULL) { \ + memcpy((dest)->s6_addr, (src)->data, 16); \ + } \ +} while (0) + + +/** + * @brief Proxy message header structure + * + * This structure defines the header format for proxy messages, containing metadata + * such as protocol version, message type, session identifiers, and payload length. + */ +typedef struct { + uint8_t version; // Protocol version. Currently unused, set to 1. + uint8_t proxy_msg_type; // Proxy message type. Possible values: device message (0), strategy message (1), session message (2), data message (3). + uint16_t frontend_sess_id; // Frontend session ID. Used for matching frontend and backend sessions in the frontend proxy. + uint16_t backend_sess_id; // Backend session ID. Used for matching frontend and backend sessions in the backend proxy. + uint16_t payload_len; // Payload length in bytes. Valid range: 1 to 4088. Must not exceed one physical page. +} __attribute__((packed)) ProxyMsgHeader; + + + + +/** + * Calculate the total memory space occupied by the complete message described by ProxyMsgHeader + * Including: size of the header structure itself + length of the payload data + */ +#define PROXY_MSG_TOTAL_SIZE(p_msg_header) \ + (sizeof(ProxyMsgHeader) + (p_msg_header)->payload_len) + + +/** + * @brief Macro to calculate total shared queue memory size with fixed fragment size + * + * For cross-system shared memory usage with strict size regulations, each fragment + * occupies a fixed size regardless of actual payload: + * - Each fragment = sizeof(ProxyMsgHeader) + PROXY_MSG_MAX_SIZE + * - Total memory = number of fragments × fixed fragment size + * + * Number of fragments is calculated using ceiling division to ensure all data is covered. + * + * @param data_size Size of the data payload to be sent (in bytes) + * @return size_t Total shared queue memory required (in bytes) + */ +#define PROXY_MSG_TOTAL_MEM_SIZE(data_size) \ + ( \ + /* Calculate number of fragments (ceiling division) */ \ + ( ((data_size) + PROXY_MSG_MAX_SIZE - 1) / PROXY_MSG_MAX_SIZE ) \ + * (sizeof(ProxyMsgHeader) + PROXY_MSG_MAX_SIZE) /* Fixed size per fragment */ \ + ) + + +typedef enum { + ACTION_TYPE_COMMAND = 0, // Command + ACTION_TYPE_RESPONSE // Response +} ActionType; + + +/** + * @brief Device message header structure + * + * This structure defines the header format for device-related messages, containing metadata such as + * protocol version, message type, message identifier, signaling type, and payload length. + */ +typedef struct { + uint16_t version; // Protocol version. Currently unused; set to 1. + uint16_t msg_type; // Message type. Possible values: Disable (0), Enable (1), Query (2) + uint16_t msg_id; // Message ID. Used to match commands with their corresponding responses. + uint16_t action_type; // Signaling type. Possible values: Command (0), Response (1) + uint16_t payload_len; // Payload length in bytes. +} __attribute__((packed)) DevMsgHeader; + + +typedef enum { + DEV_MSG_DISABLE = 0, // Disable + DEV_MSG_ENABLE, // Enable + DEV_MSG_QUERY // Query +} DevMsgType; + +// Check if the device message type is valid +#define IS_VALID_DEV_MSG_TYPE(dev_msg_type) \ + ((dev_msg_type) == DEV_MSG_DISABLE || \ + (dev_msg_type) == DEV_MSG_ENABLE || \ + (dev_msg_type) == DEV_MSG_QUERY) + + +/** + * @brief Device message mask structure + * + * Used to store the content of the "Enable"/"Disable" commands, specifically representing the mask + * that indicates which devices are selected for enabling or disabling. + */ +typedef struct { + uint16_t data; // Content of the "Enable"/"Disable" commands, representing the mask that indicates which devices are selected to enable or disable. +} __attribute__((packed)) DevMsgMask; + + + +/** + * @brief Device message report structure + * + * This structure contains the response data for the "Query" command, including + * status information, error details, and the active device mask. + */ +typedef struct { + uint8_t status; // Status code indicating the overall result of the operation + uint8_t error; // Error code providing specific details about any errors encountered + uint16_t data; // Response data from the "Query" command, returning the mask indicating which devices are active +} __attribute__((packed)) DevMsgReport; + + +/** + * Calculate the payload length of a device message based on its type and action type. + * + * @param dev_msg_type Device message type (DEV_MSG_ENABLE, DEV_MSG_DISABLE or DEV_MSG_QUERY) + * @param action_type Action type (ACTION_TYPE_COMMAND or ACTION_TYPE_RESPONSE) + * @return Payload length in bytes, or PROXY_MSG_INVALID_LEN if type is invalid + */ +#define DEV_MSG_PAYLOAD_LEN(dev_msg_type, action_type) \ +( \ + /* Check if device message type is valid */ \ + (dev_msg_type == DEV_MSG_ENABLE) ? \ + ( \ + /* Check if action type is valid */ \ + (action_type == ACTION_TYPE_COMMAND) ? 2 : \ + (action_type == ACTION_TYPE_RESPONSE) ? 4 : \ + PROXY_MSG_INVALID_LEN /* Invalid action type */ \ + ) : \ + (dev_msg_type == DEV_MSG_DISABLE) ? \ + ( \ + (action_type == ACTION_TYPE_COMMAND) ? 2 : \ + (action_type == ACTION_TYPE_RESPONSE) ? 2 : \ + PROXY_MSG_INVALID_LEN /* Invalid action type */ \ + ) : \ + (dev_msg_type == DEV_MSG_QUERY) ? \ + ( \ + (action_type == ACTION_TYPE_COMMAND) ? 0 : \ + (action_type == ACTION_TYPE_RESPONSE) ? 4 : \ + PROXY_MSG_INVALID_LEN /* Invalid action type */ \ + ) : \ + PROXY_MSG_INVALID_LEN /* Invalid device message type */ \ +) + + +/* + * Get payload length directly from DevMsgHeader struct + * euses DEV_MSG_PAYLOAD_LEN to avoid duplicate logic + */ +#define DEV_MSG_HEADER_PAYLOAD_LEN(header) \ + DEV_MSG_PAYLOAD_LEN((header)->msg_type, (header)->action_type) + + +/** + * @brief Strategy message header structure + * + * This structure defines the header format for strategy-related messages, containing metadata such as + * protocol version, message type, message identifier, signaling type, and payload length. + */ +typedef struct { + uint16_t version; // Protocol version. Currently unused; set to 1. + uint16_t msg_type; // Message type. Possible values: Set (0), Query (1) + uint16_t msg_id; // Message ID. Used to match commands with their corresponding responses. + uint16_t action_type; // Signaling type. Possible values: Command (0), Response (1) + uint16_t payload_len; // Payload length in bytes. +} __attribute__((packed)) StrgyMsgHeader; + + +typedef enum { + STRGY_MSG_SET = 0, // Set + STRGY_MSG_QUERY // Query +} StrgyMsgType; + + +typedef enum { + STRGY_OP_STATUS_SUCCESS = 0, // Session operation succeeded + STRGY_OP_STATUS_FAIL = 1, // Session operation failed + STRGY_OP_STATUS_NUM = 2 // Total number of enumeration members +} StrgyOpStatus; + + +// Corresponds to the code field in the structure, indicating specific reason codes for strategy operation results +typedef enum { + STRGY_OP_CODE_SUCCESS = 0, // Operation succeeded + STRGY_OP_CODE_PARAMETER_INVALID = 1, // Invalid Parameter + STRGY_OP_CODE_MAX // Total number of enumeration members +} StrgyOpCode; + + +// Check if the strategy message type is valid +#define IS_VALID_STRGY_MSG_TYPE(strgy_msg_type) \ + ((strgy_msg_type) == STRGY_MSG_SET || \ + (strgy_msg_type) == STRGY_MSG_QUERY) + + +/** + * Calculate the payload length of a strategy message based on its type and action type. + * + * @param strgy_msg_type Strategy message type (STRGY_MSG_SET or STRGY_MSG_QUERY) + * @param action_type Action type (ACTION_TYPE_COMMAND or ACTION_TYPE_RESPONSE) + * @return Payload length in bytes, or PROXY_MSG_INVALID_LEN if type is invalid + */ +#define STRGY_MSG_PAYLOAD_LEN(strgy_msg_type, action_type) \ +( \ + /* Check strategy message type first */ \ + (strgy_msg_type == STRGY_MSG_SET) ? \ + ( \ + /* Determine length for SET message based on action type */ \ + (action_type == ACTION_TYPE_COMMAND) ? 2 : /* SET command → 2 bytes */ \ + (action_type == ACTION_TYPE_RESPONSE) ? 2 : /* SET response → 2 bytes */ \ + PROXY_MSG_INVALID_LEN /* Invalid action type for SET message */ \ + ) : \ + (strgy_msg_type == STRGY_MSG_QUERY) ? \ + ( \ + /* Determine length for QUERY message based on action type */ \ + (action_type == ACTION_TYPE_COMMAND) ? 0 : /* QUERY command → 0 bytes */ \ + (action_type == ACTION_TYPE_RESPONSE) ? 4 : /* QUERY response → 4 bytes */ \ + PROXY_MSG_INVALID_LEN /* Invalid action type for QUERY message */ \ + ) : \ + PROXY_MSG_INVALID_LEN /* Invalid strategy message type */ \ +) + + +/* + * Get payload length directly from StrgyMsgHeader struct + * euses STRGY_MSG_PAYLOAD_LEN to avoid duplicate logic + */ +#define STRGY_MSG_HEADER_PAYLOAD_LEN(header) \ + DEV_MSG_PAYLOAD_LEN((header)->msg_type, (header)->action_type) + + +/** + * @brief Strategy command enable message structure + * + * This structure defines the format for strategy command enable messages, specifically containing + * parameters required when enabling a specified strategy. It is used to传递 configuration details + * for strategy activation. + */ +typedef struct { + uint16_t strgy_para; // Strategy parameter. Possible values: 0 (Round Robin), 1 (Select device with highest current available bandwidth), 2 (Select device with lowest current latency) +} __attribute__((packed)) StrgyCMDEnableMessage; + + +/** + * @brief Strategy message report structure + * + * This structure contains the response data for strategy-related "Query" commands, including + * a status code, error code, and the active strategy information returned by the query. + */ +typedef struct { + uint8_t status; // Status code indicating the overall result of the strategy operation (e.g., success or failure) + uint8_t error; // Error code providing specific details if the strategy operation encountered an error (0 for no error) + uint16_t data[]; // Flexible array member acting as a placeholder. Stores the response data from the "Query" command, specifically the active strategy code. +} __attribute__((packed)) StrgyMsgReport; + + +/** + * @brief Session message header structure + * + * This structure defines the header format for session-related messages, containing metadata such as + * protocol version, message type, signaling type, IP version, and payload length. It is used for + * managing session operations like creation and closure. + */ +typedef struct { + uint16_t version; // Protocol version. Currently unused; set to 1. + uint16_t msg_type; // Message type. Possible values: Create (0), Close (1) + uint16_t action_type; // Signaling type. Possible values: Command (0), Response (1) + uint16_t ip_version; // IP version. Possible values: SESS_IPV4_PROTO (4), SESS_IPV6_PROTO (6) + uint16_t payload_len; // Payload length in bytes. +} __attribute__((packed)) SessMsgHeader; + + + +/** + * @brief IPv4 session parameter structure + * + * This structure contains parameters required for establishing or managing an IPv4-based session, + * including device identification, transport protocol, IPv4 address, and corresponding port information. + */ +typedef struct{ + uint16_t dev_id; // Device identifier, uniquely identifies the target device in the session + uint16_t trans_proto; // Transport protocol used for the session (e.g., TCP, UDP) + struct IPv4Address ipv4_addr; // IPv4 address structure containing the device's IPv4 address information + uint16_t port; // Port number associated with the IPv4 address for the session +} __attribute__((packed)) SessParaIPv4; + + + +/** + * @brief IPv6 session parameter structure + * + * This structure contains parameters required for establishing or managing an IPv6-based session, + * including device identification, transport protocol, IPv6 address, and corresponding port information. + */ +typedef struct{ + uint16_t dev_id; // Device identifier, uniquely identifies the target device in the session + uint16_t trans_proto; // Transport protocol used for the session (e.g., TCP, UDP) + struct IPv6Address ipv6_addr; // IPv6 address structure containing the device's IPv6 address information + uint16_t port; // Port number associated with the IPv6 address for the session +} __attribute__((packed)) SessParaIPv6; + + +typedef enum { + SESS_MSG_CLOSE = 0, // Close + SESS_MSG_CREATE // Create +} SessMsgType; + + +typedef enum { + SESS_NON_IP_PROTO = 0, // None-IP protocol + SESS_IPV4_PROTO = 4, // IPv4 + SESS_IPV6_PROTO = 6 // IPv6 +} SessIpProtoVersion; + + +typedef enum { + SESS_UDP_PROTO = 0, // UDP + SESS_TCP_PROTO = 1, // TCP + SESS_FASTPATH_PROTO = 2 // XDP or eBPF +} SessTranProto; + +// Check if the session message type is valid +#define IS_VALID_SESS_MSG_TYPE(sess_msg_type) \ + ((sess_msg_type) == SESS_MSG_CREATE || \ + (sess_msg_type) == SESS_MSG_CLOSE) + + +// Check if the IP protocol verion is valid +#define IS_VALID_SESS_IP_VERSION(ip_version) \ + ((ip_version) == SESS_IPV4_PROTO || \ + (ip_version) == SESS_IPV6_PROTO) + + + +/** + * Calculate the payload length of a session message based on its type, action type, and IP protocol version. + * + * @param sess_msg_type Session message type (SESS_MSG_CREATE or SESS_MSG_CLOSE) + * @param action_type Action type (ACTION_TYPE_COMMAND or ACTION_TYPE_RESPONSE) + * @param ip_version IP protocol version (SESS_IPV4_PROTO or SESS_IPV6_PROTO) + * @return Payload length in bytes, or PROXY_MSG_INVALID_LEN if type/version is invalid + */ +#define SESS_MSG_PAYLOAD_LEN(sess_msg_type, action_type, ip_version) \ +( \ + /* Check session message type first */ \ + (sess_msg_type == SESS_MSG_CLOSE) ? \ + ( \ + utils_print("type is SESS_MSG_CLOSE\n"),\ + /* Determine length for CLOSE message based on action type */ \ + (action_type == ACTION_TYPE_COMMAND) ? 0 : /* CLOSE command → 0 bytes */ \ + (action_type == ACTION_TYPE_RESPONSE) ? 2 : /* CLOSE response → 2 bytes (status + error) */ \ + PROXY_MSG_INVALID_LEN /* Invalid action type for CLOSE message */ \ + ) : \ + (sess_msg_type == SESS_MSG_CREATE) ? \ + ( \ + utils_print("type is SESS_MSG_CREATE, action type is %d, ip_version is %d\n",action_type, ip_version),\ + /* Determine length for CREATE message based on action type */ \ + (action_type == ACTION_TYPE_RESPONSE) ? 2 : /* CREATE response → 2 bytes (status + error) */ \ + (action_type == ACTION_TYPE_COMMAND) ? \ + ( \ + /* Determine length for CREATE command based on IP version */ \ + (ip_version == SESS_IPV4_PROTO) ? 10 : /* IPv4 → 10 bytes (session params) */ \ + (ip_version == SESS_IPV6_PROTO) ? 22 : /* IPv6 → 22 bytes (session params) */ \ + PROXY_MSG_INVALID_LEN /* Invalid IP version for CREATE command */ \ + ) : \ + PROXY_MSG_INVALID_LEN /* Invalid action type for CREATE message */ \ + ) : \ + PROXY_MSG_INVALID_LEN /* Invalid session message type */ \ +) + +/* + * Get payload length directly from StrgySessHeader struct + * euses STRGY_MSG_PAYLOAD_LEN to avoid duplicate logic + */ +#define SESS_MSG_HEADER_PAYLOAD_LEN(header) \ + SESS_MSG_PAYLOAD_LEN((header)->msg_type, (header)->action_type, (header)->ip_version) + +typedef struct { + struct IPv4Address ipv4_addr; // IPv4 address + uint16_t port; // Transport layer port; +} __attribute__((packed)) IPv4PortTuple; + +typedef struct { + struct IPv6Address ipv6_addr; // IPv6 address + uint16_t port; // Transport layer port +} __attribute__((packed)) IPv6PortTuple; + +typedef union { + IPv4PortTuple ipv4_port_tuple; + IPv6PortTuple ipv6_port_tuple; +}IPPortTuple; + +/* + * The structure of the session create-response message's payload. + */ +// Corresponds to the status field in the structure, indicating the overall status of the session operation (success/failure) +typedef enum { + SESS_OP_STATUS_SUCCESS = 0, // Session operation succeeded + SESS_OP_STATUS_FAIL = 1, // Session operation failed + SESS_OP_STATUS_NUM = 2 // Total number of enumeration members +} SessOpStatus; + + +// Corresponds to the code field in the structure, indicating specific reason codes for operation results +typedef enum { + SESS_OP_CODE_SUCCESS = 0, // Operation succeeded + SESS_OP_CODE_NO_PERMISSION = 1, // No permission to perform the operation + SESS_OP_CODE_DEVICE_ERROR = 2, // Device error occurred + SESS_OP_CODE_RESOURCE_INSUFFICIENT = 3, // Insufficient resources + SESS_OP_CODE_NETWORK_UNREACHABLE = 4, // Network is unreachable + SESS_OP_CODE_PARAMETER_INVALID = 5, // Invalid Parameter + SESS_OP_CODE_MAX // Total number of enumeration members +} SessOpCode; + + +// Structure for session operation response data, containing status and specific reason code +typedef struct SessOpRespData_ { + uint8_t status; // Corresponding to SessOpStatus enumeration (overall operation status) + uint8_t code; // Corresponding to SessOpCode enumeration (specific reason code for operation result) +} __attribute__((packed)) SessOpRespData; + +/* + * Session message parameter structure, used to describe core parameters related to session establishment, + * including device identification, transport protocol type, and IP-port combination and other key information. + */ +struct SessMsgPara{ +// Frontend session ID, used to deliver information for establishing a new session. + uint16_t frontend_sess_id; +// Backend session ID, used to deliver information for establishing a new session. + uint16_t backend_sess_id; +// Device ID, of type uint16_t, with value range 0x0000-0xFFFF; when set to 0xFFFF, it indicates entering vertical handover mode. + uint16_t dev_id; +// IP version, of type uint16_t, of type uint16_t, supported values include: 4 (IPv4), 6 (IPv6). + uint16_t ip_version; +// Transport layer protocol type, of type uint16_t, supported values include: SESS_UDP_PROTO (0, UDP protocol), SESS_TCP_PROTO(1, TCP protocol), SESS_FASTPATH_PROTO(2, FastPath protocol). + uint16_t trans_proto; +// Tuple of IP address and port number, used to describe the combination of IP address and corresponding port number of the communication endpoint. + IPPortTuple ip_port_tuple; +}; + + +/** + * @brief Session parameters structure for IPv4-based sessions + * @details Contains parameters required to establish and manage an IPv4 session, + * including transport protocol, device selection, and destination endpoint. + * Uses packed alignment to ensure contiguous memory layout. + */ +typedef struct { +// uint16_t transport_layer_proto; /**< Transport layer protocol (2 bytes, e.g., SESS_TCP_PROTO for TCP, SESS_UDP_PROTO for UDP) */ + uint16_t device_selection; /**< Device selection identifier (2 bytes) */ + uint16_t transport_layer_proto; /**< Transport layer protocol (2 bytes, e.g., SESS_TCP_PROTO for TCP, SESS_UDP_PROTO for UDP) */ + IPv4PortTuple dest_endpoint; /**< Destination endpoint containing IPv4 address and port */ +} __attribute__((packed)) SessIPv4Params; + + + +/** + * @brief Session parameters structure for IPv6-based sessions + * @details Contains parameters required to establish and manage an IPv6 session, + * including transport protocol, device selection, and destination endpoint. + * Uses packed alignment to ensure contiguous memory layout. + */ +typedef struct { +// uint16_t transport_layer_proto; /**< Transport layer protocol (2 bytes, e.g., 6 for TCP, 17 for UDP) */ + uint16_t device_selection; /**< Device selection identifier (2 bytes) */ + uint16_t transport_layer_proto; /**< Transport layer protocol (2 bytes, e.g., 6 for TCP, 17 for UDP) */ + IPv6PortTuple dest_endpoint; /**< Destination endpoint containing IPv6 address and port */ +} __attribute__((packed)) SessIPv6Params; + + +typedef struct{ + ProxyMsgHeader outer_header; +// ProxyMsgType msg_type; + union { // Nested union to reduce memory usage (avoids redundant space) + DevMsgHeader dev_hdr; // Device message header member + StrgyMsgHeader strgy_hdr; // Strategy message header member + SessMsgHeader sess_hdr; // Session message header member + } inner_header; // Nested union alias for easy access to specific headers +} GeneralProxyMsgHeader; + +struct FrontendEngine_; +struct SharedMemoryPoolQueue; + +typedef enum { + MEMORY_ALLOC_SHARED, // Allocate in shared memory + MEMORY_ALLOC_CALLER // Allocated by caller +} MemoryAllocMode; + +int build_proxy_general_message(struct FrontendEngine_ *engine, GeneralProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, + uint8_t **result_msg, MemoryAllocMode alloc_mode, struct SharedMemoryPoolQueue *ring_buf); +int build_proxy_dev_message(DevMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); +int build_proxy_strgy_message(StrgyMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); +int build_proxy_sess_message(SessMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); +int build_proxy_data_message(ProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/queue.h b/projects/sel4test/apps/front/include/queue.h new file mode 100644 index 0000000..58e667e --- /dev/null +++ b/projects/sel4test/apps/front/include/queue.h @@ -0,0 +1,596 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +/* + * Singly-linked List access methods. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == (void *)(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == (void *)(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) + +#ifndef TAILQ_END +#define TAILQ_END(head) NULL // Standard definition: end of queue is NULL +#endif + +// Manually define TAILQ_FOREACH_SAFE macro (only use if the system header doesn't support it) +#ifndef TAILQ_FOREACH_SAFE + +#if 0 +#define TAILQ_FOREACH_SAFE(var, head, field, next_var) \ + for ((var) = TAILQ_FIRST((head)), \ + (next_var) = TAILQ_NEXT((var), field); \ + (var) != TAILQ_END((head)); \ + (var) = (next_var), (next_var) = TAILQ_NEXT((var), field)) +#endif + +#define TAILQ_FOREACH_SAFE(var, head, field, next_var) \ + for (var = TAILQ_FIRST(head); \ + var != TAILQ_END(head) && (next_var = TAILQ_NEXT(var, field), 1); \ + var = next_var) + +#endif + +#endif /* sys/queue.h */ diff --git a/projects/sel4test/apps/front/include/senario_test.h b/projects/sel4test/apps/front/include/senario_test.h new file mode 100644 index 0000000..a64418d --- /dev/null +++ b/projects/sel4test/apps/front/include/senario_test.h @@ -0,0 +1,34 @@ +#ifndef SENARIO_TEST_H_ +#define SENARIO_TEST_H_ + +#include "engine.h" +#include "frontend_proto.h" +#include "message.h" +#include "shared_mem_io.h" + + +int scenario_msg_inject_frontend(FrontendEngine *engine, + GeneralProxyMsgHeader *msg_header, + const uint8_t *msg_payload, + size_t msg_payload_len, + MemoryAllocMode alloc_mode, + uint8_t **result_msg, + char *result_desc, + size_t desc_len); + + +int test_proxy_scenario_multi_type_msg_build_frontend(FrontendEngine *engine); + + + +int device_msg_inject_frontend(FrontendEngine *engine); + +int strategy_msg_inject_frontend(FrontendEngine *engine); + +int session_msg_inject_frontend(FrontendEngine *engine); + +int data_msg_inject_frontend(FrontendEngine *engine); + +int test_proxy_scenario_msg_read_from_rx_queue_frontend(FrontendEngine *engine); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h new file mode 100644 index 0000000..47f9394 --- /dev/null +++ b/projects/sel4test/apps/front/include/session.h @@ -0,0 +1,343 @@ +#ifndef SESSION_H +#define SESSION_H + +#include +#include +#include "uthash.h" +// #include "shared_mem_io.h" + +// Frontend proxy processing result: Success (data read/written normally, logic executed completely) +#define FRONTEND_PROXY_PROCESS_OK 0 + +// Frontend proxy processing result: Failure (system-level error, such as memory allocation failure, invalid handle, protocol parsing error, etc. Requires error investigation) +#define FRONTEND_PROXY_PROCESS_ERROR -1 + +// Frontend proxy processing result: Process temporarily unavailable (non-error state, only data read/write cannot be completed, such as empty queue, data not ready, resource temporarily occupied, etc. Retry is allowed) +#define FRONTEND_PROXY_PROCESS_AGAIN 1 + + +/** +/** + * @brief Frontend session related event types + * + * This enumeration defines all event types involved in the lifecycle, data interaction, + * and exception handling of the frontend session (FrontendSession). Each event corresponds + * to a specific stage or state change of the session. + */ +typedef enum { + FRONTEND_SESS_EVENT_CONN, /**< Frontend session connection established event (triggered when the session successfully connects to the peer/backend) */ + FRONTEND_SESS_EVENT_RECVDATA, /**< Frontend session data received event (triggered when the session receives data from the peer/backend, and the data is ready for processing) */ + FRONTEND_SESS_EVENT_CLOSE, /**< Frontend session close event (triggered when the session needs to be closed actively or passively, e.g., after receiving close command or peer disconnect) */ + FRONTEND_SESS_EVENT_TIMEOUT, /**< Frontend session timeout event (triggered when the session has no data interaction or response within the predefined timeout period) */ + FRONTEND_SESS_EVENT_ABNOMAL, /**< Frontend session abnormal event (triggered when unexpected exceptions occur during session operation, such as resource allocation failure, protocol parsing error, or unexpected connection interruption) */ + FRONTEND_SESS_EVENT_MAX /**< Total number of frontend session event types (for boundary checking and iteration, not a valid event type) */ +} FrontendSessionEvent; + + + +struct ControlMsg{ + uint16_t dev_id; +}; + +/** + * @brief Memory source type of the data field in message segment (SessMsgSeg) + * + * Used to identify whether the memory pointed to by the data pointer in the SessMsgSeg structure + * is dynamically allocated or comes from shared memory (to determine subsequent memory management + * approaches, such as whether manual release is required) + */ +typedef enum { + SESS_MSG_SEG_DYNAMIC_ALLOC, ///< data points to dynamically allocated memory (needs to be freed with free()) + SESS_MSG_SEG_SHARED_MEM ///< data points to shared memory (no manual release needed; managed by the shared memory manager) +} SessMsgSegType; + + +/** + * @brief Structure representing a segment of session message data, supporting both dynamic and shared memory + * This structure encapsulates a segment of message data for session communication, including + * metadata about the data buffer and the buffer itself. It can manage data in two modes: + * dynamically allocated memory or shared memory (via a shared memory pool). + */ +struct SessMsgSeg { +/*< Length of the data buffer (in bytes). Indicates the valid data size in the buffer pointed to by @ref data. */ + uint16_t len; +/* + * < Memory source type of the data buffer, corresponding to SessMsgSegType. + * Valid values: SESS_MSG_SEG_DYNAMIC_ALLOC (dynamic memory) or SESS_MSG_SEG_SHARED_MEM (shared memory). + */ + uint16_t type; +/* + * < Pointer to the associated shared memory pool. + * Valid and non-NULL only when @ref type is SESS_MSG_SEG_SHARED_MEM, used for managing shared memory ownership and validation. + * NULL when @ref type is SESS_MSG_SEG_DYNAMIC_ALLOC (no shared memory pool associated). + */ + struct SharedMemoryPool *mem_pool; +/*< Pointer to the actual data buffer. + * For SESS_MSG_SEG_DYNAMIC_ALLOC: Points to memory allocated via malloc(). + * For SESS_MSG_SEG_SHARED_MEM: Points to a valid data segment within the shared memory managed by @ref mem_pool. + */ + uint8_t *data; +/* + * < TAILQ queue entry. Used to link multiple SessMsgSeg structures into a doubly linked list for sequential access. + */ + TAILQ_ENTRY(SessMsgSeg) entry; +}; + + +TAILQ_HEAD(SessMsgQueue, SessMsgSeg); + + + +struct SessMsgSeg *sess_msg_seg_alloc(size_t len, SessMsgSegType type, uint8_t *shared_data, struct SharedMemoryPool *mem_pool); +struct SessMsgSeg* sess_msg_seg_alloc_lite(SessMsgSegType type); +void sess_msg_seg_free(struct SessMsgSeg **seg_ptr); + +void sess_msg_queue_free_all(struct SessMsgQueue *queue); + +struct FrontendProtocolProcess; +struct FrontendEngine_; +struct FrontendSession; + +#define FRONTEND_SESS_LINKED_TO_QUEUE 1 +typedef void (*SESS_CALLBACK)(struct FrontendSession *sess, uint8_t *data, int len); +typedef void (*SESS_EVENT_CALLBACK)(struct FrontendSession *sess, FrontendSessionEvent event); + + + +/** + * @brief Frontend session lifecycle state enumeration + * + * Defines all possible lifecycle states of a frontend session, describing the connection status + * between the frontend and backend during the session's lifetime. + */ +typedef enum { + FRONTEND_SESS_INITIALIZE, ///< Initial state: Newly created frontend session (no connection attempt yet) + FRONTEND_SESS_CONNECTING, ///< Connecting state: Attempting to establish a connection with the backend + FRONTEND_SESS_CONNECTED, ///< Connected state: Successfully established connection with the backend session + FRONTEND_SESS_CLOSED ///< Closed state: Session has been terminated (final state) +} FrontendSessState; + + +struct FrontendSession { + int sess_type; +/* + * Session overall state machine, identifying the lifecycle phase of the frontend session. + * Supported states: + * 1. FRONTEND_SESS_INITIALIZE: Newly created frontend session (initial state) + * 2. FRONTEND_SESS_CONNECTING: Attempting to establish a connection with the backend + * 3. FRONTEND_SESS_CONNECTED: Connection with the backend session has been successfully established + * 4. FRONTEND_SESS_CLOSED: Session has been closed (terminated state) + */ + FrontendSessState sess_state; + int ip_version; + uint16_t frontend_sess_id; + uint16_t backend_sess_id; // hash key +/* + * State machine states, indicating the linked status of the session in different directions + * state_f2b: When its value is FRONTEND_SESS_LINKED_TO_QUEUE, it means the entries_f2b node of the current session + * is linked to the queue_f2b (belonging to FrontendSessionPool in FrontendEngine_) + */ + uint8_t state_f2b; + uint8_t state_b2f; + +// Message queues + struct SessMsgQueue msg_f2b; // front-end to back-end message queue + struct SessMsgQueue msg_b2f; // back-end to front-end message queue +// Queue link nodes + TAILQ_ENTRY(FrontendSession) entries_f2b; // front-end to back-end active queue node + TAILQ_ENTRY(FrontendSession) entries_b2f; // back-end to front-end active queue node +// Protocol processing + struct FrontendProtocolProcess *protocol_process; // protocol processing module pointer +// Pointer to the Frontend engine associated with this session + struct FrontendEngine_ *eng; +// Private data pointer (used to store session-specific data) + void *pri_data; + SESS_CALLBACK data_process_callback; + SESS_EVENT_CALLBACK event_callback; + UT_hash_handle hh; +}; + + +/** + * @brief Get the block size of the front-end session's queue in the specified direction (with null-pointer safe check, branchless concatenation implementation) + * + * Dynamically select the queue member using identifier concatenation without conditional judgment; + * perform step-by-step null-pointer checks along the pointer chain to avoid illegal access. + * Pointer chain: sess -> eng -> [tx_queue/rx_queue] -> block_size (target queue member generated by concatenating dir and _queue via ##) + * + * @param sess Pointer to the front-end session instance (type: struct FrontendSession *), which can be NULL + * @param dir Queue direction identifier, only two valid values are supported: tx (corresponds to tx_queue) and rx (corresponds to rx_queue) + * + * @return uint16_t Block size of the queue (block_size): + * - Non-0: All dependent pointers are valid, returns the actual configured block size (unit: bytes) + * - 0: Any dependent pointer is NULL (sess/eng/target direction queue) or no corresponding member after dir concatenation (may report an error in advance during compilation) + * + * @note 1. Branchless design: Directly generate tx_queue/rx_queue member names by concatenating dir and _queue via ##, + * completely replacing the original conditional judgment with higher execution efficiency and simpler syntax; + * 2. Compile-time validity check: If an invalid value other than tx/rx is passed to dir (e.g., txx), + * concatenation will generate a non-existent member like txx_queue, which directly reports an error during compilation, + * avoiding issues earlier than runtime judgment; + * 3. Null-pointer safety retained: Maintains the original three-level pointer check (sess→eng→queue), + * and directly returns 0 when any link is NULL through the && short-circuit feature, without sacrificing null-pointer safety; + * 4. Type compatibility: The return type is consistent with block_size (uint16_t), and 0U is an unsigned integer 0, + * compatible with numerical comparison scenarios; + * 5. Concatenation safety: All parameters and member accesses in the macro are enclosed in parentheses to avoid + * syntax errors caused by operator precedence conflicts. + */ +#define SESS_GET_QUEUE_BLOCK_SIZE(sess, dir) \ +( \ + (sess) != NULL && \ + (sess)->eng != NULL && \ + (sess)->eng->dir##_queue != NULL \ + ? (sess)->eng->dir##_queue->block_size \ + : 0U \ +) + + +/** + * @brief Link Frontend session to the specified queue (only if not linked yet) and set state bit + * + * @param[in] sess Pointer to struct FrontendSession (target session) + * @param[in] dir Direction identifier ("f2b" for front2back, "b2f" for back2front) + * + * @details 1. Validate critical pointers (sess/eng/sess_pool) + * 2. Check if FRONTEND_SESS_LINKED_TO_QUEUE bit is NOT set in state_ + * 3. If not set: set the bit (bitwise OR) + insert entries_ into queue_ + * + * @note - Avoids duplicate linking (prevents inserting the same node into TAILQ multiple times) + * - FRONTEND_SESS_LINKED_TO_QUEUE must be a single-bit mask (e.g., 1U<<0) + */ +#define FRONTEND_SESS_LINK_TO_QUEUE(sess, dir) do { \ + /* Step 1: Validate pointers to avoid null dereference */ \ + if ((sess) != NULL && (sess)->eng != NULL && (sess)->eng->sess_pool != NULL) { \ + /* Step 2: Check if NOT linked yet (target bit is 0) */ \ + if (((sess)->state_##dir & FRONTEND_SESS_LINKED_TO_QUEUE) == 0) { \ + /* Step 3: Set linked bit + insert into queue */ \ + (sess)->state_##dir |= FRONTEND_SESS_LINKED_TO_QUEUE; \ + TAILQ_INSERT_TAIL(&(sess)->eng->sess_pool->queue_##dir, (sess), entries_##dir); \ + } \ + } \ +} while (0) + + + +/** + * @brief Unlink Frontend session from the specified queue (only if linked) and clear state bit + * + * @param[in] sess Pointer to struct FrontendSession (target session) + * @param[in] dir Direction identifier ("f2b" for front2back, "b2f" for back2front) + * + * @details 1. Validate critical pointers (sess/eng/session_pool) + * 2. Check if FRONTEND_SESS_LINKED_TO_QUEUE bit is set in state_ + * 3. If set: remove entries_ from queue_ + clear the bit (bitwise AND NOT) + * + * @note - Avoids invalid unlinking (prevents removing a node not in TAILQ) + * - Ensure queue_ is initialized before calling + */ +#define FRONTEND_SESS_UNLINK_FROM_QUEUE(sess, dir) do { \ + /* Step 1: Validate pointers to avoid null dereference */ \ + if ((sess) != NULL && (sess)->eng != NULL && (sess)->eng->sess_pool != NULL) { \ + /* Step 2: Check if already linked (target bit is 1) */ \ + if (((sess)->state_##dir & FRONTEND_SESS_LINKED_TO_QUEUE) != 0) { \ + /* Step 3: Remove from queue + clear linked bit */ \ + TAILQ_REMOVE(&(sess)->eng->sess_pool->queue_##dir, (sess), entries_##dir); \ + (sess)->state_##dir &= ~FRONTEND_SESS_LINKED_TO_QUEUE; \ + } \ + } \ +} while (0) + + +/** + * @brief Get the associated shared memory pool (mem_pool) from a FrontendSession pointer + * This macro retrieves the shared_memory_pool pointer contained in FrontendEngine_ + * through the eng member (pointing to FrontendEngine_) of the FrontendSession structure. + * @param sess Pointer to a struct FrontendSession + * @return Pointer to struct SharedMemoryPool, i.e., sess->eng->mem_pool + */ +#define FRONTEND_SESSION_MEM_POOL(sess) ((sess)->eng->mem_pool) + + +/** + * @brief Insert a SessMsgSeg pointer into the specified direction queue of FrontendSession + * + * @param[in] sess Pointer to struct FrontendSession (the session containing the target queue) + * @param[in] seg Pointer to struct SessMsgSeg (the message segment to be inserted) + * @param[in] dir Direction identifier, must be "f2b" (front2back) or "b2f" (back2front) + * + * @details 1. Validate that sess and seg are non-NULL to avoid null dereference + * 2. Insert seg into sess->msg_ queue using TAILQ_INSERT_TAIL (FIFO order) + * 3. The queue is identified by concatenating "msg_" with dir (msg_f2b or msg_b2f) + * + * @note - must be "f2b" or "b2f"; invalid values will cause compilation errors + * - Ensure sess->msg_ has been initialized with TAILQ_INIT() before insertion + * - seg must point to a valid SessMsgSeg instance (allocated and initialized) + * - This macro performs a tail insertion to maintain FIFO order of messages + */ +#define SESS_MSG_SEG_INSERT_QUEUE(sess, seg, dir) do { \ + /* Validate critical pointers */ \ + if ((sess) != NULL && (seg) != NULL) { \ + /* Insert the segment into the target queue (msg_f2b or msg_b2f) */ \ + TAILQ_INSERT_TAIL(&(sess)->msg_##dir, (seg), entry); \ + } \ +} while (0) + + +/** + * @brief Remove and return the first SessMsgSeg pointer from the specified direction queue of FrontendSession + * + * @param[in] sess Pointer to struct FrontendSession (the session containing the target queue) + * @param[out] seg_ptr Double pointer to struct SessMsgSeg (output: receives the removed segment; set to NULL if queue is empty) + * @param[in] dir Direction identifier, must be "f2b" (front2back) or "b2f" (back2front) + * + * @details 1. Validate that sess and seg_ptr are non-NULL to avoid null dereference + * 2. Check if the target queue (msg_) is non-empty using TAILQ_FIRST + * 3. If non-empty: remove the first element with TAILQ_REMOVE and assign to *seg_ptr + * 4. If empty: set *seg_ptr to NULL + * + * @note - must be "f2b" or "b2f"; invalid values will cause compilation errors + * - Ensure sess->msg_ has been initialized with TAILQ_INIT() before removal + * - seg_ptr must be a valid double pointer (points to a struct SessMsgSeg* variable) + * - The removed segment's memory is not freed by this macro (caller must handle via sess_msg_seg_free) + */ +#define SESS_MSG_SEG_REMOVE_HEAD(sess, seg_ptr, dir) do { \ + /* Validate critical pointers */ \ + if ((sess) != NULL && (seg_ptr) != NULL) { \ + /* Initialize output to NULL (handles empty queue case) */ \ + *(seg_ptr) = NULL; \ + /* Check if queue is non-empty */ \ + if (TAILQ_FIRST(&(sess)->msg_##dir) != NULL) { \ + /* Get the first element and remove it from the queue */ \ + *(seg_ptr) = TAILQ_FIRST(&(sess)->msg_##dir); \ + TAILQ_REMOVE(&(sess)->msg_##dir, *(seg_ptr), entry); \ + } \ + } \ +} while (0) + +struct FrontendProtocolProcess { + + int (*connect)(struct FrontendSession* sess); + + int (*accept)(struct FrontendSession* sess); + + int (*read)(struct FrontendSession* sess, uint8_t* data, uint32_t size); + + int (*write)(struct FrontendSession* sess, const uint8_t* data, uint32_t size); + + int (*close)(struct FrontendSession* sess); + +}; + +TAILQ_HEAD(FrontendSessionQueue, FrontendSession); + +struct FrontendSessionID { + uint16_t id; + TAILQ_ENTRY(FrontendSessionID) entry; +}; +TAILQ_HEAD(FrontendSessionIDQueue, FrontendSessionID); + + +void default_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event); +int session_send(struct FrontendSession* sess, const uint8_t* data, uint32_t size); +int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/session_pool.h b/projects/sel4test/apps/front/include/session_pool.h new file mode 100644 index 0000000..7cc53c4 --- /dev/null +++ b/projects/sel4test/apps/front/include/session_pool.h @@ -0,0 +1,63 @@ +#ifndef SESSION_POOL_H +#define SESSION_POOL_H + +#include +#include + +#include "message.h" +#include "session.h" + +struct FrontendEngine_; + +struct FrontendSessionPoolOps; + +struct FrontendSessionPool { + char *pool_name; // Name/identifier of the session pool + int sess_num; // Current number of sessions in the pool + int capacity; // Maximum number of sessions the pool can hold (total capacity) + struct FrontendEngine_ *engine; // Pointer to the engine this pool belongs to + struct FrontendSessionPoolOps *ops; // Set of operation functions for the pool (e.g., create, delete) + struct FrontendSessionQueue queue_f2b; // Queue containing active sessions + struct FrontendSessionQueue queue_b2f; // Queue for backend-to-frontend session communication/mapping + struct FrontendSessionIDQueue id_queue; // Queue holding available/reusable session IDs + struct FrontendSession *htable; // Hash table storing sessions (for efficient lookup by ID) +}; + + + + +struct FrontendSessionPoolOps { + int (*create_sess_step1)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para); + int (*create_sess_step2)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, uint16_t sess_id, SessOpRespData *resp); + int (*create_sess_passive)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para); + int (*insert_sess)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); + struct FrontendSession* (*search_sess)(struct FrontendSessionPool *s_pool, uint16_t id); + int (*delete_sess)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); + int (*data_process)(struct FrontendSession *sess); + int (*data_process_b2f)(struct FrontendSession *sess); + int (*data_process_f2b)(struct FrontendSession *sess); + int (*close_sess_step1)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); + int (*close_sess_step2)(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, SessOpRespData *resp); + void (*destroy_pool)(struct FrontendSessionPool *s_pool); +}; + +extern struct FrontendSessionPool *front_high_speed_pool; +int frontend_high_speed_init_pool(struct FrontendSessionPool *pool); + +struct FrontendSessionPool *frontend_get_high_speed_pool(); + +//helper func +uint16_t allocate_id(struct FrontendSessionIDQueue *id_q); +void release_id(struct FrontendSessionIDQueue *id_q, uint16_t id); +void print_pool(struct FrontendSessionPool *s_pool); +void high_speed_delete_all_sess(struct FrontendSessionPool *s_pool); +void fill_id_queue(struct FrontendSessionIDQueue *id_q); +void inc_sess_num(struct FrontendSessionPool *pool); +void dec_sess_num(struct FrontendSessionPool *pool); + + +int frontend_high_speed_data_process_b2f(struct FrontendSession *sess); +int frontend_high_speed_data_process_f2b(struct FrontendSession *sess); + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/shared_mem_io.h b/projects/sel4test/apps/front/include/shared_mem_io.h new file mode 100644 index 0000000..256d981 --- /dev/null +++ b/projects/sel4test/apps/front/include/shared_mem_io.h @@ -0,0 +1,567 @@ +#ifndef SHARED_MEM_IO_H +#define SHARED_MEM_IO_H + + +#include +#include +#include +#include +#include +#include + +#include "session.h" + +#define ERROR_SHARED_MEM_ADDR UINT64_MAX + +#define MAX_MAP_TABLE_ENTRY_COUNT 64 + +#define HSNET_RX_PHY_ADDR_BASE 0xA000 +#define HSNET_MEM_BLOCK_SIZE 4096 +#define HSNET_RX_MEM_BLOCK_COUNT MAX_MAP_TABLE_ENTRY_COUNT + +#define HSNET_TX_PHY_ADDR_BASE HSNET_RX_PHY_ADDR_BASE + HSNET_MEM_BLOCK_SIZE * MAX_MAP_TABLE_ENTRY_COUNT + +struct SharedMemoryPoolLock{ + int value; // Just for placehoder. We will redefine this struct after receiving the partner's document. +}; + +struct SharedMemoryPool{ + int value; // Just for placehoder. We will redefine this struct after receiving the partner's document. + struct SharedMemoryPoolLock lock; +}; + +struct DequeNode{ + int value; // // Just for placehoder. We will redefine this struct after receiving the partner's document. + struct DequeNode *prev; + struct DequeNode *next; +}; + +/** + * Enumeration of shared memory mapping modes, describing the continuity relationship between physical and logical addresses + */ +typedef enum { + SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH, // Physical addresses are contiguous, logical addresses are contiguous + SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL // Physical addresses are contiguous, logical addresses are discrete +} ShareMemMapMode; + + + +/** + * When the map mode is SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL, the shared memory queue maintains a mapping table. + * This table contains a set of MapTableEntries to establish the mapping relationship between physical addresses and virtual addresses. + * + * Mapping table entry: Describes a single mapping relationship between a virtual address and a physical address. + */ +typedef struct { + uint64_t virt_addr; /* Virtual address */ + uint64_t phy_addr; /* Physical address */ +} MapTableEntry; + + + + + +/** + * @brief FIFO queue (ring buffer implementation) based on SharedMemoryPool + * A high-efficiency first-in-first-out (FIFO) queue implemented with a ring buffer structure, + * which allocates memory from an associated shared memory pool. It is designed for inter-process + * or inter-thread communication, managing element enqueue/dequeue via ring buffer indexes + * and reflecting memory allocation details through core parameters. + */ +struct SharedMemoryPoolQueue { + /* + * the memory-maping mode in Linux side. + */ + uint8_t map_mode1; + /* + * the memory-maping mode in microkernel-OS side. + */ + uint8_t map_mode2; + + /* header index of the ring buffer, pointing to the next free slot for enqueuing. + Works with `tail` to maintain FIFO order. */ + uint16_t header; + + /* tail index of the ring buffer, pointing to the next element to be dequeued. + Works with `header` to maintain FIFO order. */ + uint16_t tail; + + /* Current number of elements in the queue. Dynamically updated with + enqueue (increment) and dequeue (decrement) operations. */ + // size_t length; + + /* Total memory size (in bytes) allocated to this queue from the shared memory pool. + Determines the maximum storage space available for elements. */ + /* + * Maximum number of elements the queue can hold, representing the upper limit of elements + * based on allocated memory and element size. + */ + uint16_t capacity; + + /* Maximum number of elements the queue can hold. Calculated as (capacity / block_size), + representing the upper limit of elements based on allocated memory and element size. */ +// size_t max_num_items; + + /* Size (in bytes) of each element's memory block. All elements in the queue + occupy a fixed size to simplify memory management and access. */ + uint16_t block_size; + /* + * Physical address of the queue control block in shared memory. + * Used for direct access across processes or between kernel and user spaces. + * Can also assist in spinlock/mutex synchronization during sharing processes. + */ + uint64_t phy_addr; + /* + * Virtual address of the queue control block in the Linux size. + * Valid when map_mode1 is SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH. + */ + uint64_t virt_addr1; + /* + * Virtual address of the queue control block in the microkernel-OS size. + * Valid when map_mode2 is SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH + */ + uint64_t virt_addr2; + /* + * Address mapping table for the Linux side, storing up to MAX_MAP_TABLE_ENTRY_COUNT entries + * that map virtual addresses to physical addresses. + * + * * Valid when map_mode1 is SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL. + */ + MapTableEntry table1[MAX_MAP_TABLE_ENTRY_COUNT]; + /* + * Address mapping table for the microkernel-OS side, storing up to MAX_MAP_TABLE_ENTRY_COUNT entries + * that map virtual addresses to physical addresses. + * + * Valid when map_mode2 is SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL. + */ + MapTableEntry table2[MAX_MAP_TABLE_ENTRY_COUNT]; + /* + * Pointer to the associated shared memory pool. All memory for the queue + * (including control block and data blocks) is allocated from this pool. + */ + struct SharedMemoryPool *pool; +}__attribute__((packed)); + + +/** + * @brief Configuration parameters structure for initializing SharedMemoryPoolQueue + * + * A structure containing all necessary parameters required to initialize a SharedMemoryPoolQueue + * instance. It aggregates external input parameters that define the queue's memory properties + * and association with a shared memory pool, serving as a clean interface for queue initialization. + * + * Key parameters include: + * - `pool`: Reference to the associated shared memory pool (mandatory) + * - `map_mode`: the memory mapping mode + * - `phy_addr`/`virt_addr`: Addresses of the control block in shared memory and current process + * - `capacity`: Total number of elememts allocated to the queue from the pool + * - `block_size`: Fixed size of each element's memory block (determines max element count) + */ +typedef struct SharedMemoryPoolQueueConfig_{ + struct SharedMemoryPool *pool; // Associated shared memory pool +/* + * < Memory mapping mode (enumerated type). Specifies how the shared + * memory is mapped into the current process address space (e.g., + * direct physical mapping, virtual address translation). Refer to + * SHARE_MEM_MAP_MODE_* macros for valid values. + */ + uint16_t map_mode; + uint64_t phy_addr; // Physical address of control block in shared memory + uint64_t virt_addr; // Virtual address of control block in current process + size_t capacity; // Total memory size (bytes) allocated to the queue + size_t block_size; // Size (bytes) of each element's memory block +} SharedMemoryPoolQueueConfig; + + +/** + * Macro: Check if map_mode of SharedMemoryPoolQueueConfig is a valid enumerated value + * Function: Verifies whether the map_mode of the SharedMemoryPoolQueueConfig is correctly assigned to one of the valid values + * defined in the ShareMemMapMode enumeration, while ensuring the input config pointer is non-null + * (to avoid null pointer dereference). + * @param conf Pointer to the SharedMemoryPoolQueueConfig structure whose map_mode member needs to be checked + * @return Returns 1 (true) if conf is non-null and map_mode is a valid enumerated value; otherwise returns 0 (false) + */ +#define IS_VALID_SHM_CONF_MAP_MODE(conf) \ + ((conf) != NULL && ((conf)->map_mode == SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH \ + || (conf)->map_mode == SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL)) + + + +/** + * @brief FIFO queue ENQUEUE operation macro (standard C compatible version) + * + * This macro implements the PUSH operation for SharedMemoryPoolQueue. It uses a do...while structure + * to ensure syntax compatibility and returns the operation result through the second parameter. + * The specific logic is as follows: + * 1. Increment the header index; if it exceeds max_num_items(capacity), wrap around (circular nature) + * 2. Check if the operation triggers a queue exception (header equals tail or out of bounds) + * 3. If abnormal, roll back the header and set an error status; otherwise, set a success status + * + * @param queue Pointer to the SharedMemoryPoolQueue structure (input) + * @param result Variable to receive the operation result (output), which can be: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: Operation failed (queue is full or abnormal) + */ +#define SHMP_QUEUE_ENQUEUE(queue, result) do { \ + /* Initialize result to success status */ \ + (result) = FRONTEND_PROXY_PROCESS_OK; \ + /* Save original header for rollback in case of exception */ \ + uint16_t original_header = (queue)->header; \ + \ + /* Update header: increment by 1, wrap around if exceeding capacity */ \ + (queue)->header++; \ + if ((queue)->header >= (queue)->capacity) { \ + (queue)->header -= (queue)->capacity; \ + } \ + \ + /* Check for abnormal status: header conflicts with tail or out of bounds */ \ + if ((queue)->header == (queue)->tail || (queue)->header >= (queue)->capacity) { \ + /* Roll back header to pre-operation state */ \ + (queue)->header = original_header; \ + /* Set error result */ \ + (result) = FRONTEND_PROXY_PROCESS_ERROR; \ + } \ +} while(0) + + +/** + * @brief FIFO-queue DEQUEUE operation macro (standard C compatible version) + * + * This macro implements the DEQUEUE operation for SharedMemoryPoolQueue with priority on empty queue check. + * It first verifies if the queue is empty before modifying any indices. The logic is: + * 1. Check if queue is empty (tail equals header) - return error immediately if true + * 2. Save original tail for rollback in case of subsequent errors + * 3. Increment the tail index; if exceeding capacity, wrap around (circular nature) + * 4. Check for out-of-bounds error; rollback and return error if detected + * 5. Return success status if all operations complete normally + * + * @param queue Pointer to the SharedMemoryPoolQueue structure (input) + * @param result Variable to receive the operation result (output), which can be: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: Operation failed (queue is empty or abnormal) + */ +#define SHMP_QUEUE_DEQUEUE(queue, result) do { \ + /* First check if queue is empty (tail equals header) */ \ + if ((queue)->tail == (queue)->header) { \ + (result) = FRONTEND_PROXY_PROCESS_ERROR; \ + } else { \ + /* Initialize result to success status */ \ + (result) = FRONTEND_PROXY_PROCESS_OK; \ + /* Save original tail for rollback in case of exception */ \ + uint16_t original_tail = (queue)->tail; \ + \ + /* Update tail: increment by 1 */ \ + (queue)->tail++; \ + \ + /* Handle wrap-around if exceeding capacity */ \ + if ((queue)->tail >= (queue)->capacity) { \ + (queue)->tail -= (queue)->capacity; \ + } \ + } \ +} while(0) + + +/** + * @brief Macro to get the virtual address of the element pointed by header in the shared memory queue + * + * Calculates the virtual address of the queue element that the header index points to. + * The address is derived from the queue's base virtual address plus the offset calculated by + * header index multiplied by block size. + * + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @return uint64_t Virtual address of the element at header position + */ +#define SHMP_QUEUE_HEADER_VIRT_ADDR(queue) \ + ((uint64_t)(queue)->virt_addr + (uint64_t)((queue)->header + 1) * (uint64_t)(queue)->block_size) + + +/** + * @brief Macro to get the virtual address of the position pointed by tail in the shared memory queue + * + * Calculates the virtual address of the queue position that the tail index points to (next available slot for enqueuing). + * The address is derived from the queue's base virtual address plus the offset calculated by + * tail index multiplied by block size. + * + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @return uint64_t Virtual address of the position at tail index + */ +#define SHMP_QUEUE_TAIL_VIRT_ADDR(queue) \ + ((uint64_t)(queue)->virt_addr + (uint64_t)((queue)->tail + 1) * (uint64_t)(queue)->block_size) + + +/** + * @brief Macro to calculate used memory size using header and tail indices + * + * Computes the total memory occupied by elements in the queue using header and tail indices, + * without relying on the length field. Follows circular queue logic: + * - When tail >= header: used elements = tail - header + * - When tail < header: used elements = (capacity - header) + tail + * Total used memory is then elements count multiplied by block size. + * + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @return size_t Total used memory size in bytes + */ +#define SHMP_QUEUE_USED_MEMORY(queue) \ + ((size_t)( \ + ((queue)->tail >= (queue)->header) ? \ + ((queue)->tail - (queue)->header) : \ + ((queue)->capacity - (queue)->header + (queue)->tail) \ + ) * (size_t)(queue)->block_size) + + +/** + * @brief Macro to calculate surplus (available) memory in the shared memory queue + * + * Implements a two-step internal calculation: + * 1. First compute the number of available slots using header, tail and capacity: + * - When tail >= header: surplus slots = capacity - (tail - header) + * - When tail < header: surplus slots = header - tail + * 2. Then multiply available slots by block size to get surplus memory in bytes + * + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @return size_t Surplus memory size in bytes + */ +#define SHMP_QUEUE_SURPLUS_MEMORY(queue) \ + ((size_t)( \ + /* Step 1: Calculate number of surplus slots */ \ + ((queue)->tail >= (queue)->header) ? \ + ((queue)->capacity - ((queue)->tail - (queue)->header)) : \ + ((queue)->header - (queue)->tail) \ + /* Step 2: Convert slots to memory size */ \ + ) * (size_t)(queue)->block_size) + + +/** + * @brief Allocate a memory slot from the queue head + * @param queue Pointer to the SharedMemoryPoolQueue structure + * @param addr_ptr Pointer to store the virtual address of allocated slot (output) + * @return Returns FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + * @note Allocation logic: + * 1. Check if queue is full (next header == tail) + * 2. If not full, store current header's slot address in addr_ptr + * 3. Update header with wrap-around handling (mod capacity) + */ +#define SHM_POOL_QUEUE_HEAD_ALLOC(queue, addr_ptr) ({ \ + int _status = FRONTEND_PROXY_PROCESS_ERROR; \ + if ((queue != NULL) && (addr_ptr != NULL)) { \ + uint16_t _next_header = (queue->header + 1) % (uint16_t)queue->capacity; \ + if (_next_header != queue->tail) { \ + *addr_ptr = (uintptr_t)(queue->virt_addr1 + (queue->header + 1)* queue->block_size); \ + queue->header = _next_header; \ + _status = FRONTEND_PROXY_PROCESS_OK; \ + } else { \ + *addr_ptr = ERROR_SHARED_MEM_ADDR; \ + } \ + } \ + _status; \ +}) + + + +/** + * @brief Looks up the virtual address from table1 or table2 with boundary checks + * + * This macro retrieves the virtual address from either table1 or table2 of the + * SharedMemoryPoolQueue structure based on table_id, with validation of input parameters. + * + * @param queue Pointer to SharedMemoryPoolQueue structure + * @param table_id Table identifier (must be 1 for table1 or 2 for table2) + * @param idx Index of the entry to look up (must be in range 0 to capacity-1) + * @param addr_ptr Pointer to store the retrieved virtual address. + * Set to ERROR_SHARED_MEM_ADDR if parameters are invalid. + * + * @note Performs validation on both table_id and index range. Ensures access + * only to valid entries within the bounds defined by queue->capacity. + */ +#define SHM_POOL_QUEUE_LOOKUP_VIRTADDR(queue, table_id, idx, addr_ptr) \ + do { \ + /* Initialize to error value by default */ \ + *(addr_ptr) = ERROR_SHARED_MEM_ADDR; \ + \ + /* Validate table_id is either 1 or 2 */ \ + if (table_id != 1 && table_id != 2) { \ + break; \ + } \ + \ + /* Validate index is within [0, capacity-1] range */ \ + if (idx >= (queue)->capacity || idx < 0) { \ + break; \ + } \ + \ + /* Look up virtual address from the specified valid table */ \ + if (table_id == 1) { \ + *(addr_ptr) = (queue)->table1[idx].virt_addr; \ + } else { /* table_id == 2 */ \ + *(addr_ptr) = (queue)->table2[idx].virt_addr; \ + } \ + } while (0) + + +/** + * @def SHM_POOL_QUEUE_ALLOC_FROM_HEADER(queue, addr_ptr) + * @brief Allocates a memory block from the header of a shared memory pool queue + * + * This macro allocates a memory block from the header position of a shared memory pool queue. + * It calculates the next header position, checks for available space, and assigns the memory + * address based on the queue's memory mapping mode. If allocation fails (e.g., null pointers + * or no available space), it sets the address to ERROR_SHARED_MEM_ADDR. + * + * @param[in] queue Pointer to the shared memory pool queue structure + * @param[out] addr_ptr Pointer to store the allocated memory block's address (as uintptr_t) + * + * @details The allocation process works as follows: + * 1. Checks if both @p queue and @p addr_ptr are non-null + * 2. Calculates the next header position using modulo arithmetic with queue capacity + * 3. Checks if space is available (next header != queue tail) + * 4. Assigns address based on mapping mode: + * - SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH: Directly calculates address from virtual base + * - SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL: Uses lookup macro for virtual address + * - Other modes: Sets error address + * 5. Updates queue header to next position on successful allocation + * 6. Sets @p addr_ptr to ERROR_SHARED_MEM_ADDR on errors (null pointers or no space) + */ +#define SHM_POOL_QUEUE_ALLOC_FROM_HEADER(queue, addr_ptr) \ + do { \ + if ((queue != NULL) && (addr_ptr != NULL)) { \ + uint16_t _next_header = (queue->header + 1) % (uint16_t)queue->capacity; \ + if (_next_header != queue->tail) { \ + if(SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH == queue->map_mode1) {\ + *addr_ptr = (uintptr_t)(queue->virt_addr1 + (queue->header + 1) * queue->block_size); \ + queue->header = _next_header; \ + } else if(SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL == queue->map_mode1){\ + SHM_POOL_QUEUE_LOOKUP_VIRTADDR(queue, 1, queue->header, addr_ptr);\ + queue->header = _next_header; \ + } else { \ + *addr_ptr = ERROR_SHARED_MEM_ADDR;\ + } \ + } else { \ + *addr_ptr = ERROR_SHARED_MEM_ADDR; \ + } \ + }\ + }while(0) + + +/** + * @brief Roll back the head position of a shared memory pool queue after failed post-allocation processing + * This macro reverts the head pointer of a shared memory pool queue to its previous position, + * specifically intended for scenarios where an element was successfully allocated (head pointer incremented), + * but subsequent processing of the allocated element (e.g., data initialization, validation, or business logic) failed. + * It restores the queue to a consistent state, preventing invalid allocation markers. + * @param queue Pointer to the SharedMemoryPoolQueue structure. If NULL, the macro performs no operation. + * @note Rollback conditions and logic: + * No action is taken if the input queue pointer is NULL (safety check) + * Rollback is skipped if header == tail (queue is empty), as this would create an invalid underflow state + * When rollback is performed: + * If header is 0, it wraps around to (capacity - 1) to maintain circular queue semantics + * For non-zero header values, it is simply decremented by 1 + * @details Critical usage context: + * This macro should be invoked only after a successful element allocation (where the head pointer was already advanced) + * but before completing the element's processing. Common failure scenarios include: + * Failed data writing to the allocated element + * Validation errors in the data to be stored + * Resource shortages during element initialization + * Aborted business logic operations after allocation + * Without this rollback, the queue would retain an advanced head pointer, marking a "used" slot that contains no valid data, + * leading to lost queue capacity and potential data corruption in subsequent operations. + * The do-while(0) structure ensures the macro behaves like a single statement, safe for use in if/else blocks without extra braces. + */ +#define SHM_POOL_QUEUE_HEAD_ROLLBACK(queue) do { \ + if (queue != NULL) { \ + if(queue->header == queue->tail) \ + break;\ + queue->header = (queue->header == 0) ? \ + (uint16_t)(queue->capacity - 1) : \ + (queue->header - 1); \ + } \ +} while (0) + + +int init_shared_mem_pool(struct SharedMemoryPool *mem_pool); +void free_shared_mem_pool(struct SharedMemoryPool *mem_pool); +uint64_t alloc_shared_mem(struct SharedMemoryPool *mem_pool); +void free_shared_mem(struct SharedMemoryPool *mem_pool, uint64_t addr); + +int init_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool); +void free_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool); +int fetch_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool); +int release_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool); + + + +struct SharedMemoryPoolQueue *shared_mem_pool_queue_create_frontend(const SharedMemoryPoolQueueConfig *config); + +int shared_mem_pool_queue_frontend_setup_pages(struct SharedMemoryPoolQueue *queue, uint32_t page_num, uint64_t page_phy[]); + +int shared_mem_pool_queue_initialize(struct SharedMemoryPoolQueue *queue, const SharedMemoryPoolQueueConfig *config); + +int shared_mem_pool_queue_destroy(struct SharedMemoryPoolQueue* queue); + + + +int shared_mem_pool_queue_send(struct SharedMemoryPoolQueue *queue, + const void *data, + size_t data_size); + +/** + * @brief Sends a variable-size block (up to block_size) to the SharedMemoryPoolQueue: One Copy + * + * Stores a variable-length data block (with size ≤ block_size) into the queue using one-copy semantics. + * The data is transferred to a shared memory slot through a single copy operation. Regardless of the + * actual data size, the entire block_size space in shared memory is occupied to maintain fixed-size + * slot management. Explicitly handles full queue cases. + * + * @param queue Pointer to the SharedMemoryPoolQueue instance + * @param data Input pointer to the variable-length data block to be sent. Must point to valid memory + * containing the data (not a pointer to a pointer). + * @param data_size Size of the input data block (must be > 0 and ≤ queue->block_size for valid operation) + * + * @return int Returns: + * - FRONTEND_PROXY_PROCESS_OK: Success, data was copied to shared memory and queue state updated + * - FRONTEND_PROXY_PROCESS_AGAIN: Queue is full (next tail position equals header) + * - FRONTEND_PROXY_PROCESS_ERROR: Invalid input parameters (NULL pointers for queue or data), + * invalid block_size (0 bytes), data_size = 0, or data_size > queue->block_size + * + * @note The input data block (pointed to by data) must remain valid until the copy operation completes. + * One-copy semantics involve a single data transfer (e.g., via memcpy) from the input buffer to the + * target shared memory slot. + * Critical behavior: Even if data_size < block_size, the entire block_size space in shared memory is + * reserved and counted as occupied (surplus decreases by block_size, not data_size). + * Queue state (tail, length, surplus) is updated via the SHMP_QUEUE_ENQUEUE macro after the copy completes. + * Address calculation: Target slot address is derived from queue->virt_addr + (tail * block_size). + */ +int shared_mem_pool_queue_send_oc(struct SharedMemoryPoolQueue *queue, + const void *data, + size_t data_size); + + +int shared_mem_pool_queue_recv_zc(struct SharedMemoryPoolQueue *queue, + void **buffer, + size_t *out_data_size); + + +/** + * @brief Acquire access right to the shared memory queue by obtaining the shared memory pool lock + * @param queue Pointer to the SharedMemoryPoolQueue instance to be accessed + * @return FRONTEND_PROXY_PROCESS_OK if lock is acquired successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., invalid pool handle); + * FRONTEND_PROXY_PROCESS_AGAIN if lock acquisition times out (retry may succeed) + * @note Retrieves the lock associated with the shared memory pool (queue->pool) to control queue access; + * Must be paired with SHARED_MEM_QUEUE_UNLOCK using the same queue to prevent deadlocks; + * FRONTEND_PROXY_PROCESS_AGAIN indicates temporary unavailability - callers should retry later + */ +#define SHARED_MEM_QUEUE_LOCK(queue) (fetch_shared_mem_pool_lock((queue)->pool)) + +/** + * @brief Release access right to the shared memory queue by releasing the shared memory pool lock + * @param queue Pointer to the SharedMemoryPoolQueue instance that was accessed + * @return FRONTEND_PROXY_PROCESS_OK if lock is released successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., releasing an unheld lock) + * @note Releases the lock associated with the shared memory pool (queue->pool) to end controlled access; + * Must be paired with SHARED_MEM_QUEUE_LOCK using the same queue to prevent deadlocks; + * Does not return FRONTEND_PROXY_PROCESS_AGAIN - release operation either succeeds or fails + */ +#define SHARED_MEM_QUEUE_UNLOCK(queue) (release_shared_mem_pool_lock((queue)->pool)) + +struct SharedMemoryPoolQueue *shared_mem_pool_queue_create_frontendend(const SharedMemoryPoolQueueConfig *config); + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/uthash.h b/projects/sel4test/apps/front/include/uthash.h new file mode 100644 index 0000000..06c2eeb --- /dev/null +++ b/projects/sel4test/apps/front/include/uthash.h @@ -0,0 +1,1137 @@ +/* +Copyright (c) 2003-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.3.0 + +#include /* memcmp, memset, strlen */ +#include /* ptrdiff_t */ +#include /* exit */ + +#if defined(HASH_NO_STDINT) && HASH_NO_STDINT +/* The user doesn't have , and must figure out their own way + to provide definitions for uint8_t and uint32_t. */ +#else +#include /* uint8_t, uint32_t */ +#endif + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define DECLTYPE(x) (__typeof(x)) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifndef HASH_FUNCTION +#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FUNCTION(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) ((bv[(idx)/8U] & (1U << ((idx)%8U))) != 0) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) 1 +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + const struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#include /* fprintf, stderr */ +#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx + * (archive link: https://archive.is/Ivcan ) + */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ + default: ; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + break; \ + default: ; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ -- Gitee From 6e68e1fe7b23bdf27cb9222674c35c4621b6fc89 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:34:39 +0800 Subject: [PATCH 045/133] upload source files --- .../sel4test/apps/front/src/common_utils.c | 17 + projects/sel4test/apps/front/src/dev.c | 29 + projects/sel4test/apps/front/src/engine.c | 545 ++++++++ .../sel4test/apps/front/src/frontend_api.c | 510 +++++++ .../sel4test/apps/front/src/frontend_proto.c | 1243 +++++++++++++++++ projects/sel4test/apps/front/src/main.c | 39 + .../sel4test/apps/front/src/senario_test.c | 445 ++++++ projects/sel4test/apps/front/src/session.c | 246 ++++ .../sel4test/apps/front/src/session_pool.c | 801 +++++++++++ .../sel4test/apps/front/src/shared_mem_io.c | 397 ++++++ 10 files changed, 4272 insertions(+) create mode 100644 projects/sel4test/apps/front/src/common_utils.c create mode 100644 projects/sel4test/apps/front/src/dev.c create mode 100644 projects/sel4test/apps/front/src/engine.c create mode 100644 projects/sel4test/apps/front/src/frontend_api.c create mode 100644 projects/sel4test/apps/front/src/frontend_proto.c create mode 100644 projects/sel4test/apps/front/src/main.c create mode 100644 projects/sel4test/apps/front/src/senario_test.c create mode 100644 projects/sel4test/apps/front/src/session.c create mode 100644 projects/sel4test/apps/front/src/session_pool.c create mode 100644 projects/sel4test/apps/front/src/shared_mem_io.c diff --git a/projects/sel4test/apps/front/src/common_utils.c b/projects/sel4test/apps/front/src/common_utils.c new file mode 100644 index 0000000..f9898d6 --- /dev/null +++ b/projects/sel4test/apps/front/src/common_utils.c @@ -0,0 +1,17 @@ +#include "common_utils.h" + +void error_print(char *error){ +#if UTILS_ENABLE_DEBUG + printf("%s\n", error); +#endif +} + + +int debug_print(const char *format, ...){ +#if UTILS_ENABLE_DEBUG + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +#endif +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/dev.c b/projects/sel4test/apps/front/src/dev.c new file mode 100644 index 0000000..ad8c11e --- /dev/null +++ b/projects/sel4test/apps/front/src/dev.c @@ -0,0 +1,29 @@ +#include "common_utils.h" +#include "dev.h" +#include "engine.h" +#include "session.h" +#include "session_pool.h" + + + +FrontendDevInfo dev_array[] = { + [0] = { + .dev_status = 0, + .dev_id = 0, + .dev_type = 1, + .name = "Frontend_Dev0" + } +}; + + +FrontendDevListCfg global_dev_list_cfg; + + +void frontend_init_dev_list(){ + p_global_dev_list_cfg = &global_dev_list_cfg; + memset(p_global_dev_list_cfg, 0, sizeof(p_global_dev_list_cfg)); + p_global_dev_list_cfg->dev_info = dev_array; + p_global_dev_list_cfg->dev_num = sizeof(dev_array)/sizeof(FrontendDevInfo); + + utils_print("In %s: number of device is %d\n", __func__, sizeof(dev_array)/sizeof(FrontendDevInfo)); +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c new file mode 100644 index 0000000..41ac27b --- /dev/null +++ b/projects/sel4test/apps/front/src/engine.c @@ -0,0 +1,545 @@ +#include "engine.h" + +extern FrontendEngine *p_g_fr_eng; +extern FrontendEngine g_fr_eng; + + +/** + * Configuration structure for the high-speed network receive queue. + * + * This global instance initializes parameters for managing the receive queue's shared memory, + * including memory mapping mode, addresses, capacity, and block size. + */ +SharedMemoryPoolQueueConfig high_speed_net_rx_queue_config = { + .pool = NULL, + .map_mode = SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH, // Assumes virtual address mapping mode + .phy_addr = HSNET_RX_PHY_ADDR_BASE, // 64-bit unsigned integer zero value + .virt_addr = 0ULL, // 64-bit unsigned integer zero value + .capacity = MAX_MAP_TABLE_ENTRY_COUNT, // Total number of element + .block_size = 4096 // 4096 bytes per element block +}; + + +/** + * Configuration structure for the high-speed network transmit queue. + * + * This global instance initializes parameters for managing the transmit queue's shared memory, + * including memory mapping mode, addresses, capacity, and block size. + */ +SharedMemoryPoolQueueConfig high_speed_net_tx_queue_config = { + .pool = NULL, + .map_mode = SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH, // Assumes virtual address mapping mode + .phy_addr = HSNET_TX_PHY_ADDR_BASE, // 64-bit unsigned integer zero value + .virt_addr = 0ULL, // 64-bit unsigned integer zero value + .capacity = MAX_MAP_TABLE_ENTRY_COUNT, // Total number of element + .block_size = 4096 // 4096 bytes per element block +}; + + +/** + * @brief Get data from the specified RX queue (residing in shared memory) + * @param queue Pointer to the SharedMemoryPoolQueue (RX queue) to operate on + * @param[out] buf_ptr Double pointer to store the address of data in shared memory + * (points to actual data location in shared memory on success) + * @param buf_max_len Maximum allowed length of data that can be retrieved (in bytes) + * @param[out] out_len Pointer to store the actual length of obtained data (in bytes) + * @return FRONTEND_PROXY_PROCESS_OK if data is retrieved successfully; + * FRONTEND_PROXY_PROCESS_ERROR if a system-level error occurs (e.g., invalid queue handle); + * FRONTEND_PROXY_PROCESS_AGAIN if data is temporarily unavailable (e.g., queue is empty) + * @note The caller is responsible for managing the lock of the shared memory pool + * (lock once before multiple calls to reduce overhead) + */ +int frontend_engine_rx_queue_get(struct SharedMemoryPoolQueue *queue, void **buf_ptr, + size_t buf_max_len, size_t *out_len){ + int ret; + size_t msg_size, buf_size; + ProxyMsgHeader *msg_hdr; + + if(NULL == queue || NULL == buf_ptr || NULL == out_len){ + error_print("frontend_engine_rx_queue_get failed: invalid input parameters (NULL pointers)"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + utils_print("In %s, before call shared_mem_pool_queue_recv_zc, buf_ptr = %p, *buf_ptr = %p\n", __func__, buf_ptr, *buf_ptr); + ret = shared_mem_pool_queue_recv_zc(queue, buf_ptr, &buf_size); + utils_print("In %s, after call shared_mem_pool_queue_recv_zc, buf_ptr = %p, *buf_ptr = %p\n", __func__, buf_ptr, *buf_ptr); + + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_engine_rx_queue_get failed: failed to retrieve data from RX queue"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + error_print("frontend_engine_rx_queue_get returns: the RX queue is empty"); + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + msg_hdr = (ProxyMsgHeader *)(*buf_ptr); + msg_size = msg_hdr->payload_len; + + if(msg_size + sizeof(ProxyMsgHeader) > buf_max_len){ + error_print("frontend_engine_rx_queue_get failed: message total size (header + payload) exceeds buffer maximum length"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + *out_len = msg_size; + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Retrieves a message from the shared memory pool queue. + * + * @details This function fetches message data from the specified SharedMemoryPoolQueue instance. + * It returns a pointer to the message buffer if data is available, and outputs the + * operation status and actual message length through output parameters. + * The caller should check the status code stored in @p ret before using the returned buffer + * and message length. + * + * @param[in] queue Pointer to the SharedMemoryPoolQueue instance, cannot be NULL + * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow + * @param[out] ret Pointer to an integer variable that stores the operation status code + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * + * @return Pointer to the uint8_t message data buffer on successful data retrieval; + * NULL if the operation fails, the queue is empty, or a system error occurs + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., invalid queue handle, NULL input pointers), + * the returned pointer is NULL and @p out_len is undefined + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message data is temporarily unavailable (e.g., queue is empty), + * the returned pointer is NULL and @p out_len is undefined + * + * @note The ownership and release mechanism of the returned data buffer depend on the implementation + * of the SharedMemoryPoolQueue module; refer to the module's documentation for memory management rules. + */ +uint8_t *frontend_engine_rx_queue_get_msg(struct SharedMemoryPoolQueue *queue, size_t max_msg_len, + int *ret, size_t *out_len){ + size_t msg_size, buf_size; + ProxyMsgHeader *msg_hdr; + uint8_t *buff_addr; + int ret_val; + + if(NULL == queue || NULL == ret || NULL == out_len){ + error_print("frontend_engine_rx_queue_get_msg failed: invalid input parameters (NULL pointers)\n"); + *ret = FRONTEND_PROXY_PROCESS_ERROR; + *out_len = 0; + return NULL; + } + + utils_print("In %s, before call shared_mem_pool_queue_recv_zc, buf_ptr = %p\n", __func__, buff_addr); + ret_val = shared_mem_pool_queue_recv_zc(queue, &buff_addr, &buf_size); + utils_print("In %s, after call shared_mem_pool_queue_recv_zc, buf_ptr = %p\n", __func__, buff_addr); + + if(FRONTEND_PROXY_PROCESS_ERROR == ret_val){ + error_print("frontend_engine_rx_queue_get_msg failed: failed to retrieve data from RX queue\n"); + *ret = FRONTEND_PROXY_PROCESS_ERROR; + *out_len = 0; + return NULL; + } + + if(FRONTEND_PROXY_PROCESS_AGAIN == ret_val){ + error_print("frontend_engine_rx_queue_get_msg failed: the RX queue is empty\n"); + *ret = FRONTEND_PROXY_PROCESS_AGAIN; + *out_len = 0; + return NULL; + } + + msg_hdr = (ProxyMsgHeader *)(buff_addr); + msg_size = msg_hdr->payload_len; + *ret = FRONTEND_PROXY_PROCESS_OK; + *out_len = msg_size + sizeof(ProxyMsgHeader); + + utils_print("*out_len = %d\n", *out_len); + + return buff_addr; +} + + +/** + * @brief Initialize the global FrontendEngine instance + * + * This function initializes the global FrontendEngine singleton (pointed to by `p_g_fr_eng`), + * including resource allocation, dependency initialization, and configuration loading. + * It must be called before any other operations related to the FrontendEngine (e.g., + * calling `get_global_frontend_engine()` to use the instance). + * + * @note 1. Recommended to call this function once during program startup (e.g., in main() before business logic); + * 2. This function is not thread-safe. Ensure no concurrent calls during initialization; + * 3. The function is non-reentrant. Repeated calls may cause resource leaks or initialization conflicts; + * 4. If initialization fails, subsequent use of the FrontendEngine instance (via `get_global_frontend_engine()`) + * may return a null pointer or lead to undefined behavior. + * + * @warning Do not skip this initialization or call it multiple times. Uninitialized or repeatedly initialized + * FrontendEngine may result in program crashes, resource conflicts, or functional abnormalities. + */ +void frontend_engine_init(){ + int ret; + memset(&g_fr_eng, 0, sizeof(g_fr_eng)); + p_g_fr_eng = &g_fr_eng; + + + p_g_fr_eng->sess_pool = malloc(sizeof(struct FrontendSessionPool)); + + ret = frontend_high_speed_init_pool(p_g_fr_eng->sess_pool); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize frontend high-speed session pool (frontend_high_speed_init_pool returned error)!"); + free(p_g_fr_eng->sess_pool); + abort(); + } + + ret = engine_init_hs_net_dev(p_g_fr_eng); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize frontend high-speed device list!\n!"); + free(p_g_fr_eng->sess_pool); + free(p_g_fr_eng->dev_set); + abort(); + } + + ret = engine_init_shared_mem_queue(p_g_fr_eng); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize shared memory queue!\n!"); + free(p_g_fr_eng->sess_pool); + free(p_g_fr_eng->dev_set); + abort(); + } +} + + +int frontend_engine_init_eng_ops(FrontendEngine *eng){ + if(NULL == eng){ + error_print("frontend_engine_init_eng_ops failed: the eng point is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int engine_init_shared_mem_queue(FrontendEngine *eng){ + struct SharedMemoryPoolQueue *rx_queue, *tx_queue; + SharedMemoryPoolQueueConfig *rx_queue_conf, *tx_queue_conf; + + if(NULL == eng){ + error_print("engine_init_shared_mem_queue() failed: the engine instance is NULL (uninitialized or invalid)!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + rx_queue_conf = &high_speed_net_rx_queue_config; + rx_queue = shared_mem_pool_queue_create_frontend(rx_queue_conf); + + + if(NULL == rx_queue){ + error_print("engine_init_shared_mem_queue() failed: out of memory for the shared memory RX queue allocation!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + tx_queue_conf = &high_speed_net_tx_queue_config; + tx_queue = shared_mem_pool_queue_create_frontend(tx_queue_conf); + + if(NULL == tx_queue){ + error_print("engine_init_shared_mem_queue() failed: out of memory for the shared memory TX queue allocation!"); + free(rx_queue); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + rx_queue_conf->pool = eng->mem_pool; + tx_queue_conf->pool = eng->mem_pool; + + eng->rx_queue = rx_queue; + eng->tx_queue = tx_queue; + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Initialize high-speed network devices for the frontend engine + * + * @details This function executes the full initialization process for high-speed network devices. + * It validates the input engine pointer, dynamically allocates memory for the high-speed + * device set structure, and initializes the global device configuration list. + * The function verifies the validity of the device count from the global configuration, + * initializes the device set memory to zero, and traverses the global configuration to + * copy core device parameters (ID, type, name) to the local high-speed network device array. + * All allocated memory will be freed properly in error scenarios to avoid memory leaks. + * + * @param eng Pointer to the FrontendEngine instance, must not be NULL + * + * @return int Status code indicating the execution result of device initialization + * @retval FRONTEND_PROXY_PROCESS_OK Initialization completed successfully + * @retval FRONTEND_PROXY_PROCESS_ERROR Initialization failed (null pointer, memory allocation failure, invalid device count) + * + * @note Depends on external function: frontend_init_dev_list() + * @note Depends on global configuration structure: p_global_dev_list_cfg + * @note Depends on macro definitions: MAX_HS_DEV_NUM, MIN_HS_DEV_NUM + * @note Uses memcpy for device name copying and memset for memory zero-initialization + * + * @warning The function performs dynamic memory allocation with malloc(), remember to manage + * the lifecycle of dev_set in subsequent business logic + */ +int engine_init_hs_net_dev(FrontendEngine *eng){ + FrontendHighSpeedNetDeviceSet *dev_set; + FrontendDevInfo *dev_info; + int dev_cnt, dev_num; + + if(NULL == eng){ + error_print("engine_init_hs_net_dev failed: the engine should not be NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + dev_set = malloc(sizeof(FrontendHighSpeedNetDeviceSet)); + + if(NULL == dev_set){ + error_print("engine_init_hs_net_dev failed: insurficient memory resource!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* + * Initialize global device configuration list + */ + frontend_init_dev_list(); + + dev_num = p_global_dev_list_cfg->dev_num; + + if(dev_num > MAX_HS_DEV_NUM || dev_num < MIN_HS_DEV_NUM){ + error_print("engine_init_hs_net_dev failed: invalied device number!\n"); + free(dev_set); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + memset(dev_set, 0, sizeof(FrontendHighSpeedNetDeviceSet)); + dev_cnt = 0; + +/* + * Traverse and obtain device pointers sequentially based on configurations in p_global_dev_list_cfg. + */ + while(dev_cnt < dev_num){ + dev_info = &dev_set->hs_net_dev[dev_cnt]; + dev_info->dev_id = p_global_dev_list_cfg->dev_info[dev_cnt].dev_id; + dev_info->dev_type = p_global_dev_list_cfg->dev_info[dev_cnt].dev_status; + memcpy(dev_info->name, p_global_dev_list_cfg->dev_info[dev_cnt].name, strlen(p_global_dev_list_cfg->dev_info[dev_cnt].name)); +// utils_print("The device name is %s\n", p_global_dev_list_cfg->dev_info[dev_cnt].name); +// utils_print("The device name is %s\n", dev_info->name); + dev_cnt++; + } + + eng->dev_set = dev_set; + + return FRONTEND_PROXY_PROCESS_OK; +} + + + + +/** + * @brief Main loop function of the engine, handling message processing and data transmission cyclically + * This function executes a continuous loop consisting of four main steps. After completing all steps, + * it returns to the first step to implement the core operation of the frontend engine. + * @details The loop process is as follows: + * + * 1. Read data from the RX queue of the shared memory queue owned by the FrontendEngine instance, and process + * them sequentially through the frontend proxy protocol stack. + * If any data is read, there are two cases: + * (a) For device messages, strategy messages, and session messages (these are responses from the backend): + * The frontend proxy protocol stack performs corresponding processing for each type, and updates the frontend's + * relevant state based on the message content (the frontend does not generate response packets). + * (b) For sessions that receive data messages: + * These sessions are placed into the backend-to-frontend active queue for processing in step (2). + * If no data message is found, the procedure jumps to step (3). + * + * 2. Access and process session instances in the backend-to-frontend active queue sequentially: + * Sequentially call the application callback function registered by each session in the queue (serving as a data-ready notification). + * After receiving the callback notification, the application will call the dedicated read API to read data from the corresponding session, + * and complete business processing independently based on the read data (the frontend does not involve active data extraction or socket transmission here). + * If the application needs to send data to the backend proxy through the shared memory queue, when the data to be sent is added to the send buffer, + * the session will be added to the frontend-to-backend active queue, and these sessions will be processed in STEP (3). + * + * 3. Access and process session instances in the frontend-to-backend active queue sequentially: + * For each session in the queue, read the pending data stored in the session's send buffer (data to be sent to the backend proxy by the application). + * Construct data messages according to the frontend proxy protocol specification, and send the messages to the backend proxy through the shared memory's + * TX queue. + * After the data is successfully sent, clear the corresponding pending data in the session's send buffer; if the send buffer becomes empty, remove the session + * from the frontend-to-backend active queue. (This step specifically handles data transmission requests initiated by the application, realizing the frontend- + * -to-backend data proxy through the shared memory queue) + * + * After completing all operations in step (3), the loop returns to step (1) to continue the cyclic processing. + */ +void frontend_engine_run(){ + FrontendEngine *eng; + struct SharedMemoryPoolQueue *rx_queue, *tx_queue; + struct FrontendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *cur_sess, *next_sess; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + uint8_t *proxy_msg; + uint32_t msg_size; + int ret; + + eng = frontend_get_global_engine(); +/* + * rx_queue: Local receive queue, which actually maps to the back-end's transmit queue (tx_queue). + * Data sent by the front-end through its tx_queue will be received by the local side via this rx_queue. + * + * tx_queue: Local transmit queue, which is used as the back-end's receive queue (rx_queue). + * Data sent by the local side through this tx_queue will be received by the back-end via its rx_queue. + */ + if(NULL == eng->rx_queue || NULL == eng->tx_queue){ + error_print("frontend_engine_run failed: The global backend engine's RX queue or TX queue has not been initialized!"); + return ; + } + + FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); + FRONTEND_ENGINE_GET_B2F_QUEUE(eng, active_queue_b2f); + + if(NULL == active_queue_f2b || NULL == active_queue_b2f){ + error_print("frontend_engine_run failed: The global frontend engine's f2b session queue or b2f session queue has not been initialized!"); + return ; + } + + if(NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("frontend_engine_run failed: Global frontend engine's session pool (sess_pool) or its operation set (ops) is not initialized!"); + return ; + } + + sess_pool = eng->sess_pool; + sess_pool_ops = sess_pool->ops; + + do{ +/* + * STEP (1). + */ +eng_run_step1: +/* + * Acquire access lock for the RX queue. + */ + ret = SHARED_MEM_QUEUE_LOCK(rx_queue); + +/* + * If returning BACKEND_PROXY_PROCESS_ERROR, it indicates a system-level error (e.g., invalid lock handle, shared memory pool corruption) + * Failed to acquire the lock; print error message and exit the current flow. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_engine_run failed: failed to get the lock of the RX queue!"); + return; + } + +/* + * If returning BACKEND_PROXY_PROCESS_AGAIN, it indicates lock acquisition timed out (temporary unavailability, e.g., lock held by another process) + * No error occurred; jump to eng_run_step3 to retry or proceed with alternative logic. + */ + if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + goto eng_run_step3; + } + + + do{ + + /* + * Retrieve data from the RX queue. + */ + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + + /* + * If returning FRONTEND_PROXY_PROCESS_ERROR, it indicates a system-level error (e.g., invalid queue handle, shared memory access exception, etc.) + * Processing cannot continue; print error message and return directly. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_engine_run failed: failed to get data from RX queue!"); + return; + } + + /* + * If returning FRONTEND_PROXY_PROCESS_AGAIN, it indicates temporary inability to retrieve data (e.g., empty queue, resource temporarily occupied, etc., non-error state) + * No error reporting needed; jump to eng_run_step2 to execute the next process. + */ + if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + goto eng_run_step2; + } + /* + * Process the proxy message. + */ + frontend_proxy_msg_process(proxy_msg); + + }while(FRONTEND_PROXY_PROCESS_OK == ret); + +eng_run_step2: + SHARED_MEM_QUEUE_UNLOCK(rx_queue); +/* + * Recall the FRONTEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (1) procedure may renew the back-to-front queue (queue_b2f) of the session pool. + */ + FRONTEND_ENGINE_GET_B2F_QUEUE(eng, active_queue_b2f); + + TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess){ + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_RECVDATA); + +/* + * queue_b2f: Session queue for sessions with pending data to be received by the frontend from the backend. + * (Pending data refers to data sent from the backend to the frontend, which the frontend needs to read.) + * If the receive queue (msg_b2f) is empty, the session instance should be removed from the queue_b2f list. + */ + if(TAILQ_EMPTY(&cur_sess->msg_b2f)){ + TAILQ_REMOVE(active_queue_b2f, cur_sess, entries_b2f); + cur_sess->state_b2f &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } + } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess) + + +eng_run_step3: +/* + * Recall the BACKEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (2) procedure may renew the front-to-back queue (queue_f2b) of the session pool. + */ + FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); + + SHARED_MEM_QUEUE_LOCK(tx_queue); + + TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess){ + +/* + * Call the corresponding data_process_f2b function pointer in the session pool's operation set (sess_pool_ops), which attempts to send the frontend-to-backend data maintained by the + * current session (cur_sess) via the shared memory's TX queue. + * The return value corresponds to three scenarios: + * Returns FRONTEND_PROXY_PROCESS_OK: All data has been sent successfully. + * Returns FRONTEND_PROXY_PROCESS_AGAIN: Not all data has been sent, and no errors occurred. + * Returns FRONTEND_PROXY_PROCESS_ERROR: An error occurred during the sending process. + */ + ret = sess_pool_ops->data_process_f2b(cur_sess); +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_OK, this indicates all message segments in the front-to-back (F2B) message queue have been sent via the shared memory's TX queue. + * Such sessions should be detached from the F2B active queue, and their "linked to queue" state flag should be cleared. + */ + if(FRONTEND_PROXY_PROCESS_OK == ret){ + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->state_f2b &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_ERROR, it indicates an error occurred when attempting to send data via the shared memory's TX queue. + * For such sessions, they should be removed from the frontend-to-backend active queue (active_queue_f2b), and the session's abnormal event callback should be triggered. + * + * Note that the recovery of resources occupied by the session needs to be completed during the processing of the FRONTEND_SESS_EVENT_ABNOMAL event, and this responsibility + * lies with the application. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNOMAL); + } +/* + * When not all data has been sent and no errors have occurred, no action is required. + */ + } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess) + + SHARED_MEM_QUEUE_UNLOCK(tx_queue); + + }while(1); +} + + +void frontend_engine_destory(){} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/frontend_api.c b/projects/sel4test/apps/front/src/frontend_api.c new file mode 100644 index 0000000..b2d94d5 --- /dev/null +++ b/projects/sel4test/apps/front/src/frontend_api.c @@ -0,0 +1,510 @@ +#include "frontend_api.h" + +/** + * @brief Macro to convert a dotted-decimal IPv4 string to an IPv4Address structure + * @details Parses a string in "xxx.xxx.xxx.xxx" format, validates each octet range (0-255), + * and populates the IPv4Address structure with the binary representation. + * Provides error messages to stderr for invalid formats or out-of-range values. + * + * @param ip_str Input string in dotted-decimal IPv4 format (e.g., "192.168.1.1") + * @param addr_struct Output IPv4Address structure to be populated with parsed values + */ +#define IPV4_STR_TO_ADDR(ip_str, addr_struct) do { \ + unsigned int a, b, c, d; /**< Temporary storage for parsed octet values */ \ + \ + /* Attempt to parse 4 octets from the input string */ \ + if (sscanf(ip_str, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) { \ + \ + /* Validate all octets are within the 0-255 range */ \ + if (a <= 255 && b <= 255 && c <= 255 && d <= 255) { \ + /* Populate the structure with validated octets */ \ + (addr_struct).data[0] = (uint8_t)a; \ + (addr_struct).data[1] = (uint8_t)b; \ + (addr_struct).data[2] = (uint8_t)c; \ + (addr_struct).data[3] = (uint8_t)d; \ + } else { \ + /* Handle octet values outside valid range */ \ + error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 address!"); \ + } \ + } else { \ + /* Handle invalid string format (not matching xxx.xxx.xxx.xxx) */ \ + error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 format!"); \ + } \ +} while(0) + + + +/** + * @brief Macro to convert an IPv4:port string to an IPv4PortTuple structure + * @details Parses a string in "xxx.xxx.xxx.xxx:port" format, splits it into IP address + * and port components, validates both parts, and populates the IPv4PortTuple. + * Port numbers must be in the range 0-65535. + * + * @param ip_port_str Input string in "xxx.xxx.xxx.xxx:port" format (e.g., "192.168.1.100:8080") + * @param tuple_struct Output IPv4PortTuple structure to be populated with parsed values + */ +#define IPV4_PORT_STR_TO_TUPLE(ip_port_str, tuple_struct) do { \ + char ip_str[16]; /* Buffer to store the IP address part (max IPv4 string length is 15) */ \ + unsigned int port; \ + \ + /* Parse the IP address and port from the input string */ \ + if (sscanf(ip_port_str, "%15[^:]:%u", ip_str, &port) == 2) { \ + \ + /* Convert and validate the IP address part */ \ + IPV4_STR_TO_ADDR(ip_str, (tuple_struct).ipv4_addr); \ + \ + /* Validate the port number (0-65535 range) */ \ + if (port > 65535) { \ + error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid port number: must be 0-65535)!"); \ + } else { \ + (tuple_struct).port = (uint16_t)port; \ + } \ + } else { \ + error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid IP:port format!"); \ + } \ +} while(0) + + +/** + * @brief Create and initialize a new FrontendSession instance + * + * This function is responsible for creating a FrontendSession object by first validating the input + * FrontendEngine_ context (including its session pool and associated operations), then allocating + * memory for the new session, and initializing the core association between the session and the engine. + * The new session will hold a reference to the input engine, while other session-specific fields + * remain uninitialized and require further setup by the caller. + * + * @param[in] eng Pointer to a FrontendEngine_ instance that provides the necessary context for session creation, + * including a valid session pool (`sess_pool`) and corresponding operation set (`sess_pool->ops`). + * Must not be NULL, and its nested `sess_pool` and `sess_pool->ops` must also be non-NULL. + * + * @return struct FrontendSession* + * - Pointer to the newly created and partially initialized FrontendSession instance on success. + * - NULL on failure (potential causes: invalid engine context (eng/sess_pool/ops is NULL), + * or memory allocation for FrontendSession fails). + * + * @note 1. This function only initializes the `eng` field of the FrontendSession; other member fields + * (e.g., session ID, connection status, protocol parameters) remain uninitialized and need + * to be set explicitly by the caller before using the session. + * 2. The memory of the returned FrontendSession is allocated via `malloc`. The caller is responsible + * for managing its lifecycle (e.g., releasing memory via `free` when the session is no longer needed) + * to avoid memory leaks. + * 3. The validity of `eng->sess_pool` and `eng->sess_pool->ops` is a prerequisite for subsequent + * session management operations (e.g., connection, release), hence the strict validation here. + * + * @warning 1. If `eng` is NULL, or `eng->sess_pool` is NULL, or `eng->sess_pool->ops` is NULL, the function + * will immediately return NULL and print an error message ("invalid engine context"), without + * attempting memory allocation. + * 2. Memory allocation failure (returned by `malloc`) will also result in a NULL return and an + * error message. The caller must check the return value before using the session pointer to + * prevent null pointer dereference crashes. + * 3. Uninitialized fields of the returned FrontendSession may contain garbage values; do not use + * the session for connection or other operations until all required fields are properly initialized. + */ +struct FrontendSession *frontend_sess_new(struct FrontendEngine_ *eng){ + struct FrontendSession *new_sess; + + if(NULL == eng || NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("frontend_sess_new failed: invalid engine context!\n"); + return NULL; + } + + new_sess = malloc(sizeof(struct FrontendSession)); + + if(NULL == new_sess){ + error_print("frontend_sess_new failed: failed to allocate memory for FrontendSession!\n"); + return NULL; + } + + new_sess->eng = eng; + new_sess->event_callback = default_session_event_callback; + + return new_sess; +} + +/** + * @brief Build a frontend session connection request based on the IP:PORT string and specified protocol, + * then send it to the shared memory queue + * + * This function accepts a frontend session object, protocol type, and an IP:PORT formatted address string. + * It will attempt to convert the proto and IP:PORT into an instance of struct SessMsgPara. If the conversion + * is successful, it will call the frontend_sess_connect function. Additionally, it parses the address information, + * constructs a connection request, and finally sends the request to the shared memory queue to complete the + * initialization and delivery of the connection request. + * + * @param[in,out] sess Pointer to the frontend session object, used to store session-related configurations + * and status information. Valid memory must be pre-allocated (non-NULL). + * @param[in] proto Connection protocol type (valid values: predefined protocol macros corresponding + * to TCP/UDP, which must be semantically consistent with the protocol field in SessMsgPara). + * @param[in] addr_str IP:PORT formatted address string (supports IPv4: "x.x.x.x:port" or IPv6: "[ipv6_addr]:port". + * Cannot be NULL or an empty string). + * + * @return int Function execution result: + * - FRONTEND_PROXY_PROCESS_OK: Successfully converted to struct SessMsgPara, called frontend_sess_connect, + * and sent the connection request to the shared memory queue. + * - FRONTEND_PROXY_PROCESS_ERROR: Execution failed (potential causes: sess is NULL, invalid format of addr_str, + * invalid protocol type, failed conversion to struct SessMsgPara, + * failed call to frontend_sess_connect, failed operation on the shared memory queue, etc.). + * + * @note 1. addr_str must strictly comply with the "IP:PORT" format. + * 2. The proto parameter must be a predefined valid protocol macro (e.g., SESS_TCP_PROTO/SESS_UDP_PROTO). Passing an invalid + * value will result in conversion failure and return FRONTEND_PROXY_PROCESS_ERROR. + * 3. The sess pointer must point to effectively allocated memory. The function will not actively allocate or free + * the sess memory; the caller is responsible for its lifetime management. + * 4. The initialization status of the shared memory queue is guaranteed by the underlying module. If the queue is + * not ready, the function will return FRONTEND_PROXY_PROCESS_ERROR. + * + * @warning If sess is NULL, addr_str is NULL, or addr_str has an invalid format, the function will immediately return + * FRONTEND_PROXY_PROCESS_ERROR without performing the conversion to struct SessMsgPara, calling frontend_sess_connect, + * or executing the connection request construction and sending operations. + */ +int frontend_sess_connect_by_addrstr(struct FrontendSession *sess, int proto, const char *addr_str){ + struct SessMsgPara para; +/* + * When the frontend session ID is set to FRONTEND_HANDOVER_SESSION_ID, the frontend protocol will allocate + * a frontend session ID before sending the SESSION-CREATION command to the backend proxy. + */ + para.frontend_sess_id = FRONTEND_HANDOVER_SESSION_ID; + para.backend_sess_id = BACKEND_HANDOVER_SESSION_ID; + para.ip_version = SESS_IPV4_PROTO; + para.dev_id = DEV_ID_AUTO_HANDOVER; + para.trans_proto = proto; + IPV4_PORT_STR_TO_TUPLE(addr_str, para.ip_port_tuple.ipv4_port_tuple); + + return frontend_sess_connect(sess, ¶); +} + + +/** + * @brief Build and send a frontend session connection request based on the provided SessMsgPara configuration + * + * This function uses the protocol, IP address, and port information encapsulated in struct SessMsgPara, + * combines it with the frontend session object (struct FrontendSession), to construct a valid connection request. + * It verifies the validity of input parameters first, then delivers the constructed request to the shared memory queue, + * completing the core process of frontend session connection initialization. + * + * @param[in,out] sess Pointer to the frontend session object, used to store session-related configurations, + * runtime status, and connection context. Must point to pre-allocated valid memory (non-NULL). + * @param[in] para Pointer to the SessMsgPara structure that contains connection core parameters: + * - Protocol type (TCP/UDP, consistent with predefined protocol macros) + * - Target IP address (valid IPv4/IPv6 string) + * - Target port number (valid range: 1~65535) + * Must be non-NULL and contain legally valid configuration data. + * + * @return int Function execution result: + * - FRONTEND_PROXY_PROCESS_OK: Successfully verified parameters, constructed the connection request, + * and sent it to the shared memory queue. + * - FRONTEND_PROXY_PROCESS_ERROR: Execution failed (potential causes: sess/para is NULL, invalid protocol + * type in para, illegal IP address/port in para, failed shared memory queue + * operation, or invalid session state in sess, etc.). + * + * @note 1. This function is the core implementation of frontend session connection; frontend_sess_connect_by_addrstr() + * calls this function after converting "IP:PORT" string and protocol to a valid struct SessMsgPara instance. + * 2. The function does not parse or correct the parameters in para (e.g., IP format, port range); the caller + * must ensure para contains legally valid configuration before calling. + * 3. The sess object must be properly initialized (e.g., default state set) before calling; uninitialized + * sess may lead to unexpected behavior or failure. + * 4. The shared memory queue's availability is guaranteed by the underlying module; if the queue is uninitialized + * or full, the function will return FRONTEND_PROXY_PROCESS_ERROR directly. + * + * @warning 1. If sess or para is NULL, the function will immediately return FRONTEND_PROXY_PROCESS_ERROR without + * performing any connection request construction or delivery operations. + * 2. Passing para with illegal parameters (e.g., invalid protocol, out-of-range port, malformed IP) will + * result in request construction failure and return FRONTEND_PROXY_PROCESS_ERROR. + */ +int frontend_sess_connect(struct FrontendSession *sess, struct SessMsgPara *para){ + struct FrontendEngine_ *eng; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *ops; + int ret; + + eng = sess->eng; + sess_pool = eng->sess_pool; + ops = sess_pool->ops; + + if(NULL == ops->create_sess_step1){ + error_print("frontend_sess_connect failed: sess_pool->ops->create_sess_step1 is NULL (required session creation step 1 function not configured)!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + utils_print("In %s, addr is %d.%d.%d.%d port = %d\n", __func__, + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[0], + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[1], + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[2], + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[3], + para->ip_port_tuple.ipv4_port_tuple.port); + + ret = ops->create_sess_step1(sess_pool, sess, para); + + return ret; +} + + +/** + * @brief Calculate the number of message segments required for sending a message (includes header + data) + * + * Each segment can hold up to `bsize` bytes (fixed header + message data). Uses ceil division to ensure full data coverage. + * + * @param dsize Total size of message data to send (bytes, non-negative) + * @param bsize Max capacity of a single message segment (bytes, must be > hsize) + * @param hsize Fixed size of the segment header (bytes, non-negative) + * + * @return size_t Required segment count: + * - 0: Invalid param (bsize <= hsize, no space for data) + * - >=1: Valid count (all data + headers fit in segments) + * + * @note 1. Effective data per segment: bsize - hsize (header occupies fixed space in each segment); + * 2. Ceil division: (dsize + effective_per_seg - 1U) / effective_per_seg to avoid truncation; + * 3. Edge cases: dsize=0 returns 1 (header-only segment if bsize>hsize); dsize <= effective_per_seg returns 1. + */ +#define CALC_MSG_SEG_NUM(dsize, bsize, hsize) \ +( \ + (bsize) > (hsize) ? \ + ((((dsize) + (bsize) - (hsize) - 1U) / ((bsize) - (hsize)))) : 0U \ +) + + +/** + * @brief Write data to the send buffer of a frontend session (for frontend session data transmission) + * + * This function is used to write specified data into the send buffer of a frontend session (FrontendSession). + * The underlying communication mechanism will subsequently be responsible for sending the buffered data to the frontend. + * Data is not sent directly to the network immediately, but first written to the buffer, which is limited by the remaining buffer space. + * + * @param[in] sess Pointer to the frontend session instance, pointing to the frontend session object to operate on; must not be NULL + * @param[in] data Pointer to the buffer of data to be sent, pointing to the start address of the data to be written into the send buffer; must not be NULL + * @param[in] size Length of the data to be sent (unit: bytes); must be greater than 0, otherwise no valid data can be written + * + * @return int Function execution result with the following specific meanings: + * - >0: Number of bytes successfully written to the send buffer (may be less than the requested size, depending on the remaining buffer space) + * - =0: Insufficient send buffer space, no data could be written + * - -1: Send error (e.g., invalid session, invalid parameters, failed underlying buffer operation, etc.) + * + * @note 1. Both parameters sess and data must be valid non-NULL pointers; otherwise, -1 may be returned and undefined behavior may be triggered; + * 2. If the return value is greater than 0 but less than size, it indicates that only part of the data was written to the buffer. The remaining data needs to be retried after the buffer has free space; + * 3. If 0 is returned, it is recommended to retry the call after a short delay; if -1 is returned, the session status and parameter validity should be checked first. + */ +int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t size){ + int block_size, seg_num, data_size, header_size, eff_data_per_seg, snd_size, cur_size, ret; + struct SessMsgSeg *msg_seg; + GeneralProxyMsgHeader data_msg_hdr; + uint8_t *payload, **res_pointer; + uint8_t *res_buf[100] = {NULL}; + + block_size = SESS_GET_QUEUE_BLOCK_SIZE(sess, tx); + + if(0 == block_size){ + error_print("frontend_sess_send failed: the TX queue is not initialized correctly!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + data_size = size; + header_size = PROXY_MSG_HDR_SIZE; + seg_num = CALC_MSG_SEG_NUM(data_size, block_size, header_size); + eff_data_per_seg = block_size - header_size; + + memset(&data_msg_hdr, 0, sizeof(data_msg_hdr)); + + data_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + data_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_DATA; + data_msg_hdr.outer_header.backend_sess_id = sess->backend_sess_id; + data_msg_hdr.outer_header.frontend_sess_id = sess->frontend_sess_id; +/* + * Copy data into message segments. Each message segment carries a PROXY-DATA message whose payload does not exceed block_size - header_size. + */ + snd_size = 0; + payload = data; + res_pointer = res_buf; + + while(seg_num > 0){ + if(data_size > eff_data_per_seg){ + cur_size = eff_data_per_seg; + }else{ + cur_size = data_size; + } + +// msg_seg = sess_msg_seg_alloc_lite(SESS_MSG_SEG_DYNAMIC_ALLOC); + msg_seg = sess_msg_seg_alloc(cur_size, SESS_MSG_SEG_DYNAMIC_ALLOC, NULL, NULL); + + if(NULL == msg_seg){ + error_print("frontend_sess_send failed: insufficient memory for allocating message segment instance!\n"); + goto seg_frag_imcomplete; + } +#if 0 + data_msg_hdr.outer_header.payload_len = cur_size; + ret = build_proxy_general_message(sess->eng, &data_msg_hdr, payload, cur_size, res_pointer, MEMORY_ALLOC_CALLER, NULL); + + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_sess_send failed: build data message error!\n"); + goto seg_frag_error; + } + + msg_seg->data = res_pointer; + msg_seg->len = cur_size + sizeof(ProxyMsgHeader); +#endif + memcpy(msg_seg->data, payload, cur_size); + + SESS_MSG_SEG_INSERT_QUEUE(sess, msg_seg, f2b); + + snd_size += cur_size; + payload += cur_size; + data_size -= cur_size; + + seg_num--; + }// while +/* + * All data is orgnized in frontend-to-backend message queue (msg_f2b). + */ + FRONTEND_SESS_LINK_TO_QUEUE(sess, f2b); + return snd_size; + +seg_frag_imcomplete: + +/* + * snd_size > 0 means at least one message segment has been inserted into the session's frontend-to-backend message queue (msg_f2b). This session should be marked as an active session (link to + * queue_f2b), so the frontend protocol module will process it and send the data in msg_f2b via the shared memory TX queue. + */ + if(snd_size > 0){ + FRONTEND_SESS_LINK_TO_QUEUE(sess, f2b); + } + + return snd_size; + +seg_frag_error: + FRONTEND_SESS_UNLINK_FROM_QUEUE(sess, f2b); + return -1; +} + + +/** + * @brief Read data from the receive buffer of a frontend session (for frontend session data reception) + * This function is used to read specified data from the receive buffer of a frontend session (FrontendSession). + * The underlying communication mechanism has already received data from the frontend and stored it in the receive buffer. + * Data is not read directly from the network immediately, but from the pre-received buffer, which is limited by the amount of available data in the buffer. + * The function is non-blocking: it will not block the calling thread waiting for data arrival or buffer operations, and will return immediately regardless of whether valid data is read. + * If the available space in the receive buffer is smaller than the size of a complete message block, the message block will be truncated, and only the part that fits in the buffer will + * be read. + * + * @param[in] sess Pointer to the frontend session instance, pointing to the frontend session object to operate on; must not be NULL + * @param[inout] data Pointer to the buffer for storing received data, pointing to the start address where the read data will be stored; must not be NULL + * @param[in] size Maximum length of data that can be stored in the receive buffer (unit: bytes); must be greater than 0, otherwise no valid data can be read + * @return int Function execution result with the following specific meanings: + * 0: Number of bytes successfully read from the receive buffer (may be less than the requested size, depending on the available data in the buffer) + * =0: No available data in the receive queue, no data could be read + * -1: Receive error (e.g., invalid session, invalid parameters, failed underlying buffer operation, etc.) + * @note 1. Both parameters sess and data must be valid non-NULL pointers; otherwise, -1 may be returned and undefined behavior may be triggered; + * If the return value is greater than 0 but less than size, it indicates that only part of the available data was read from the buffer. The remaining data can be retried after new data is + * received into the buffer;If 0 is returned, it is recommended to retry the call after a short delay; if -1 is returned, the session status and parameter validity should be checked first. + */ +int frontend_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t size){ + struct SessMsgSeg *cur_seg; + int read_size, ret; + + if(NULL == sess || NULL == data || 0 == size){ + error_print("frontend_sess_recv failed: invalid parameters!\n"); + return -1; + } + + cur_seg = TAILQ_FIRST(&sess->msg_b2f); +/* + * The receive queue is empty. + */ + if(NULL == cur_seg){ + return 0; + } + + read_size = (size > cur_seg->len) ? cur_seg->len : size; + + memcpy(data, cur_seg->data, read_size); + TAILQ_REMOVE(&sess->msg_b2f, cur_seg, entry); + free(cur_seg); + +#if 0 +/* + * queue_b2f: Session queue for sessions with pending data to be received by the frontend from the backend. + * (Pending data refers to data sent from the backend to the frontend, which the frontend needs to read.) + * If the receive queue (msg_b2f) is empty, the session instance should be removed from the queue_b2f list. + */ + if(TAILQ_EMPTY(&sess->msg_b2f)){ + FRONTEND_SESS_UNLINK_FROM_QUEUE(sess, b2f); + } +#endif + return read_size; +} + + +/** + * @brief Send SESSION-CLOSURE command to backend and release the frontend session + * + * This function is responsible for sending a SESSION-CLOSURE command message to the backend, + * notifying it to close the session associated with the given FrontendSession instance. + * After the backend completes the session closure and sends a SESSION-CLOSURE response message, + * the frontend will release all resources occupied by this session. + * + * @param[in] sess Pointer to the FrontendSession instance to be closed; must be a valid non-NULL pointer + * + * @return Operation status code: + * - FRONTEND_PROXY_PROCESS_OK: SESSION-CLOSURE command sent successfully, + * and the session has been released after receiving backend response + * - FRONTEND_PROXY_PROCESS_ERROR: Operation failed, which may be caused by input parameters + * that do not meet the requirements or temporary failure to + * construct the SESSION-CLOSURE command message + * + * @note 1. If a NULL pointer or invalid FrontendSession instance is passed, the function will immediately + * return FRONTEND_PROXY_PROCESS_ERROR; + * 2. After the function returns successfully, the memory space pointed to by 'sess' may be freed + * internally (depending on specific implementation), and subsequent access to this pointer is prohibited; + * 3. Ensure that any necessary pre-processing (e.g., data synchronization) is completed before calling + * this function to avoid data loss during session closure + */ +int frontend_sess_close(struct FrontendSession *sess){ + struct FrontendEngine_ *eng; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *ops; + int ret; + + eng = sess->eng; + sess_pool = eng->sess_pool; + ops = sess_pool->ops; + + if(NULL == ops->close_sess_step1){ + error_print("frontend_sess_close failed: sess_pool->ops->close_sess_step1 is NULL (required session creation step 1 function not configured)!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + ret = ops->close_sess_step1(sess_pool, sess); + + return ret; +} + + +/** + * @brief Binds an event callback function to a specified frontend session instance. + * + * @details This function performs NULL validation on the input parameters, + * and assigns the user-provided event callback handler to the callback + * member of the FrontendSession structure. If any input pointer is NULL, + * an error log will be printed and an error status code will be returned. + * + * @param[in] sess Pointer to the target FrontendSession structure instance, + * cannot be a NULL pointer. + * @param[in] event_callback Function pointer of the session event callback type, + * used to handle session-related events, cannot be a NULL pointer. + * + * @return int Execution status code: + * - @ref FRONTEND_PROXY_PROCESS_OK Operation succeeded, callback bound successfully + * - @ref FRONTEND_PROXY_PROCESS_ERROR Operation failed, invalid NULL input parameters + * + * @note This function only performs parameter validation and direct member assignment, + * without thread synchronization or additional state checking for the session. + */ +int frontend_sess_bind_callback(struct FrontendSession *sess, SESS_EVENT_CALLBACK event_callback){ + if(NULL == sess || NULL == event_callback){ + error_print("frontend_sess_bind_callback failed: input pointer parameters must not be NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess->event_callback = event_callback; + + return FRONTEND_PROXY_PROCESS_OK; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c new file mode 100644 index 0000000..73bf418 --- /dev/null +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -0,0 +1,1243 @@ +#include "engine.h" +#include "frontend_proto.h" + + + +/* + * Functions for building sub-type proxy messages. + * + * - build_proxy_dev_message : Builds a device-specific proxy message + * - build_proxy_strgy_message : Builds a strategy-specific proxy message + * - build_proxy_sess_message : Builds a session-specific proxy message + * - build_proxy_data_message : Builds a data-specific proxy message + */ + + +/** + * @brief Builds a complete proxy device message by combining the device header and payload. + * Builds a complete proxy device message by combining the device message header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * @param[in] dev_hdr Pointer to a DevMsgHeader structure specifying the device message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the device message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * dev_hdr->payload_len (if header contains payload length field) for consistency. + * @param[out] result_msg Double pointer to receive the address of the constructed proxy device message. + * On success, points to a newly allocated buffer containing the complete device message. + * Caller must free this memory with appropriate function when done. + * Must not be NULL. + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful message construction; + * Returns FRONTEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) or memory allocation fails. + */ + int build_proxy_dev_message(DevMsgHeader *dev_hdr, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ + DevMsgHeader *header; + size_t corr_len; + uint8_t *dev_msg; + + + corr_len = DEV_MSG_HEADER_PAYLOAD_LEN(dev_hdr); + utils_print("corr_len = %d, payload_len = %d\n", corr_len, payload_len); + + if(payload_len != corr_len){ + error_print("build_proxy_dev_message failed: payload length does not match expected value based on message type and action type!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(payload_len != dev_hdr->payload_len){ + error_print("build_proxy_dev_message failed: payload length does not match the payload_len field in DevMsgHeader!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + dev_msg = *result_msg; + header = (DevMsgHeader *)dev_msg; + header->version = dev_hdr->version; + header->msg_type = dev_hdr->msg_type; + header->msg_id = dev_hdr->msg_id; + header->action_type = dev_hdr->action_type; + header->payload_len = payload_len; + + dev_msg += sizeof(DevMsgHeader); + + utils_print("In %s, before memcpy\n", __func__); + memcpy(dev_msg, payload, payload_len); + + return FRONTEND_PROXY_PROCESS_OK; + } + + +/** + * @brief Builds a complete proxy strategy message by combining the strategy header and payload. + * Builds a complete proxy strategy message by combining the strategy message header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * @param[in] strgy_hdr Pointer to a StrgyMsgHeader structure specifying the strategy message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the strategy message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * strgy_hdr->payload_len (if header contains payload length field) for consistency. + * @param[out] result_msg Double pointer to receive the address of the constructed proxy strategy message. + * On success, points to a newly allocated buffer containing the complete strategy message. + * Caller must free this memory with appropriate function when done. + * Must not be NULL. + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful message construction; + * Returns FRONTEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) or memory allocation fails. + */ +int build_proxy_strgy_message(StrgyMsgHeader *strgy_hdr, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ + StrgyMsgHeader *header; + size_t corr_len; + uint8_t *strgy_msg; + + corr_len = STRGY_MSG_HEADER_PAYLOAD_LEN(strgy_hdr); + + if(payload_len != corr_len){ + error_print("build_proxy_strgy_message failed: payload length does not match expected value based on message type and action type!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(payload_len != strgy_hdr->payload_len){ + error_print("build_proxy_strgy_message failed: payload length does not match the payload_len field in StrgyMsgHeader!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + strgy_msg = *result_msg; + header = (StrgyMsgHeader *)strgy_msg; + header->version = strgy_hdr->version; + header->msg_type = strgy_hdr->msg_type; + header->msg_id = strgy_hdr->msg_id; + header->action_type = strgy_hdr->action_type; + header->payload_len = payload_len; + + strgy_msg += sizeof(StrgyMsgHeader); + memcpy(strgy_msg, payload, payload_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Builds a complete proxy session message by combining the session header and payload. + * Builds a complete proxy session message by combining the session message header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * @param[in] sess_hdr Pointer to a SessMsgHeader structure specifying the session message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the session message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * sess_hdr->payload_len (if header contains payload length field) for consistency. + * @param[out] result_msg Double pointer to receive the address of the constructed proxy session message. + * On success, points to a newly allocated buffer containing the complete session message. + * Caller must free this memory with appropriate function when done. + * Must not be NULL. + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful message construction; + * Returns FRONTEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) or memory allocation fails. + */ +int build_proxy_sess_message(SessMsgHeader *sess_hdr, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ + utils_print("In %s\n", __func__); + SessMsgHeader *header; + size_t corr_len; + uint8_t *sess_msg; + + corr_len = SESS_MSG_HEADER_PAYLOAD_LEN(sess_hdr); + + if(payload_len != corr_len){ + error_print("build_proxy_sess_message failed: payload length does not match expected value based on message type, action type and IP version!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(payload_len != sess_hdr->payload_len){ + error_print("build_proxy_sess_message failed: payload length does not match the payload_len field in SessMsgHeader!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess_msg = *result_msg; + header = (SessMsgHeader *)sess_msg; + header->version = sess_hdr->version; + header->msg_type = sess_hdr->msg_type; + header->action_type = sess_hdr->action_type; + header->ip_version = sess_hdr->ip_version; + header->payload_len = sess_hdr->payload_len; + + utils_print("In %s, version = %d, msg_type = %d, action_type = %d, ip_version = %d, payload_len = %d, address = %p\n", + __func__, header->version, header->msg_type, header->action_type, header->ip_version, header->payload_len, &header); + + sess_msg += sizeof(SessMsgHeader); + + if(0 == payload_len){ + if(NULL == payload){ + return FRONTEND_PROXY_PROCESS_OK; + }else{ + error_print("build_proxy_sess_message failed: payload_len is 0, but payload is not NULL!\n\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + } + + memcpy(sess_msg, payload, payload_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Builds a complete proxy data message by combining the proxy message header and payload. + * Builds a complete proxy data message by combining the proxy message header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * @param[in] proxy_msg_hdr Pointer to a ProxyMsgHeader structure specifying the proxy data message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the data message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * proxy_msg_hdr->payload_len (if header contains payload length field) for consistency. + * @param[out] result_msg Double pointer to receive the address of the constructed proxy data message. + * On success, points to a newly allocated buffer containing the complete data message. + * Caller must free this memory with appropriate function (e.g., free()) when done. + * Must not be NULL. + * @return int Returns BACKEND_PROXY_PROCESS_OK on successful message construction; + * Returns BACKEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) or memory allocation fails. +*/ +int build_proxy_data_message(ProxyMsgHeader *proxy_msg_hdr, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ + utils_print("In %s\n", __func__); + uint8_t *data_msg; + utils_print("In %s, payload_len = %d, proxy_msg_hdr->payload_len = %d\n", __func__, payload_len, proxy_msg_hdr->payload_len); + utils_print("frontend_sess_id = %d, frontend_sess_id =%d\n", proxy_msg_hdr->frontend_sess_id, proxy_msg_hdr->backend_sess_id); + +#if 0 + if(payload_len != proxy_msg_hdr->payload_len){ + error_print("build_proxy_data_message failed: payload length does not match expected value based on message type, action type and IP version!"); + return BACKEND_PROXY_PROCESS_ERROR; + } +#endif + + utils_print("Address of proxy data header = %p, content =%p, size of ProxyMsgHeader = %d\n", proxy_msg_hdr, *result_msg, sizeof(ProxyMsgHeader)); + + data_msg = *result_msg; + memcpy(data_msg, payload, payload_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + + + +/** + * @brief Builds a complete message by combining the general header and payload. + * + * Builds a complete proxy general message by combining the general header and payload. + * The function will allocate memory for the output message (caller is responsible for freeing it). + * + * Constructs a complete proxy general message by integrating the provided header and payload. + * The memory for the output message is managed based on the specified allocation mode: + * For MEMORY_ALLOC_SHARED: Memory is allocated within shared memory, which is organized in a FIFO RING buffer. + * The caller does not need to handle memory deallocation, as the shared memory is managed by the FIFO RING buffer mechanism. + * For MEMORY_ALLOC_CALLER: Memory must be pre-allocated by the caller. The function will directly populate the + * provided buffer without checking its size; the caller is solely responsible for ensuring the buffer is large + * enough to hold the complete message (header + payload). + * + * + * @param[in] engine Pointer to a BackendEngine object containing backend proxy's global context, + * such as runtime configuration, memory allocator handles, or system resources. + * Used for accessing backend-specific settings or memory management during message construction. + * Must not be NULL. + * @param[in] header Pointer to a GeneralProxyMsgHeader structure specifying the message header. + * Must not be NULL. + * @param[in] payload Pointer to the const uint8_t buffer containing the message payload. + * Can be NULL only if payload_len is 0. + * @param[in] payload_len Length of the payload in bytes. Must be non-negative and match + * header->payload_len (if header contains payload length field) for consistency. + + * @param[out] result_msg Double pointer to receive the address of the constructed proxy message. + For MEMORY_ALLOC_SHARED: On success, points to the message location within the shared FIFO RING buffer. + No caller action is needed for deallocation. + For MEMORY_ALLOC_CALLER: Must point to a pre-allocated buffer. On success, the buffer is populated with + the complete message. Caller must ensure sufficient size.Must not be NULL. + * @param[in] ring_buf Pointer to a struct SharedMemoryPoolQueue. Required and must not be NULL when alloc_mode is MEMORY_ALLOC_SHARED + (used for FIFO RING buffer operations). + Ignored when alloc_mode is MEMORY_ALLOC_CALLER (can be NULL). + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful message construction; + * Returns FRONTEND_PROXY_PROCESS_ERROR if any parameter is invalid (e.g., NULL pointers, + * mismatched lengths) + * Returns FRONTEND_PROXY_PROCESS_AGAIN if shared-memory queue is full (for MEMORY_ALLOC_SHARED) + */ + +#if 1 +int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *header, + const uint8_t *payload, size_t payload_len, uint8_t **result_msg, + MemoryAllocMode alloc_mode, struct SharedMemoryPoolQueue *ring_buf){ + uint8_t *msg_buf; + uint64_t mem_addr; + uint16_t proxy_msg_payload_len; + ProxyMsgType outer_msg_type; + ProxyMsgHeader *proxy_msg_hdr; + DevMsgHeader *dev_hdr; + StrgyMsgHeader *strgy_hdr; + SessMsgHeader *sess_hdr; + int ret, alloc_size; + +/* + * Check the validity of the input parameters. + */ + if(NULL == header || NULL == result_msg){ + error_print("build_proxy_general_message failed: input(s) for generating proxy message is/are NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + if(NULL == engine){ + error_print("build_proxy_general_message failed: backend engine is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + outer_msg_type = header->outer_header.proxy_msg_type; + header->inner_header.dev_hdr.payload_len; + + +/* + * Allocate shared-memory for storing the proxy message. + */ + if(MEMORY_ALLOC_SHARED == alloc_mode){ + if(NULL == ring_buf){ + error_print("build_proxy_general_message failed: MEMORY_ALLOC_SHARED mode requires a non-NULL ring buffer (FIFO queue)!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + utils_print("before SHM_POOL_QUEUE_ALLOC_FROM_HEADER, header = %d, tail = %d, virt addr = %lld\n", ring_buf->header, ring_buf->tail, ring_buf->virt_addr1); + SHM_POOL_QUEUE_ALLOC_FROM_HEADER(ring_buf, &mem_addr); + utils_print("after SHM_POOL_QUEUE_ALLOC_FROM_HEADER, header = %d, tail = %d, memaddr = %lld\n", ring_buf->header, ring_buf->tail, mem_addr); + + if(ERROR_SHARED_MEM_ADDR == mem_addr){ + error_print("build_proxy_general_message failed: shared memory FIFO queue is full, cannot allocate new block!\n"); + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + msg_buf = (uint8_t *)mem_addr; + *result_msg = msg_buf; + +// SHM_POOL_QUEUE_LOOKUP_VIRTADDR(ring_buf, 1, 1, &mem_addr); +// SHM_POOL_QUEUE_ALLOC_FROM_HEADER(ring_buf, &mem_addr); + }else if(MEMORY_ALLOC_CALLER == alloc_mode){ +/* + * Frontend protocol should allocate memory dynamically. + */ + alloc_size = sizeof(ProxyMsgHeader) + header->outer_header.payload_len; + msg_buf = malloc(alloc_size); + + if(NULL == msg_buf){ + error_print("insufficient memory for allocation!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + *result_msg = msg_buf; + }else{ + error_print("build_proxy_general_message failed: unsupported allocte mode!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* + * Fill the proxy message header. + */ + proxy_msg_hdr = (ProxyMsgHeader *)msg_buf; + proxy_msg_hdr->version = header->outer_header.version; + proxy_msg_hdr->proxy_msg_type = outer_msg_type; + proxy_msg_hdr->frontend_sess_id = header->outer_header.frontend_sess_id; + proxy_msg_hdr->backend_sess_id = header->outer_header.backend_sess_id; + + utils_print("In %s, version = %d, proxy_msg_type = %d, frontend_sess_id = %d, backend_sess_id = %d, payload_len = %d\n", __func__, + proxy_msg_hdr->version, proxy_msg_hdr->proxy_msg_type, proxy_msg_hdr->frontend_sess_id, proxy_msg_hdr->backend_sess_id, proxy_msg_hdr->payload_len); + msg_buf += sizeof(ProxyMsgHeader); + switch(outer_msg_type) { + case PROXY_MSG_TYPE_DEV: + dev_hdr = &header->inner_header.dev_hdr; + proxy_msg_payload_len = sizeof(DevMsgHeader); + utils_print("In %s, before enter build_proxy_dev_message\n", __func__); + ret = build_proxy_dev_message(dev_hdr, payload, payload_len, &msg_buf); + break; + case PROXY_MSG_TYPE_STRGY: + strgy_hdr = &header->inner_header.strgy_hdr; + proxy_msg_payload_len = sizeof(StrgyMsgHeader); + ret = build_proxy_strgy_message(strgy_hdr, payload, payload_len, &msg_buf); + break; + case PROXY_MSG_TYPE_SESS: + sess_hdr = &header->inner_header.sess_hdr; + proxy_msg_payload_len = sizeof(SessMsgHeader); + utils_print("In %s, before enter build_proxy_sess_message\n", __func__); + ret = build_proxy_sess_message(sess_hdr, payload, payload_len, &msg_buf); + break; + case PROXY_MSG_TYPE_DATA: + proxy_msg_payload_len = 0; + ret = build_proxy_data_message(proxy_msg_hdr, payload, payload_len, &msg_buf); + utils_print("In %s, after build_proxy_data_message, the return value is %d\n", __func__, ret); + break; + default: +/* + * Message type is not supported!. + */ + error_print("build_proxy_general_message failed: message type is not supported!"); + free_shared_mem(engine->mem_pool, mem_addr); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("build_proxy_general_message failed: failed to build proxy message!"); +// free_shared_mem(engine->mem_pool, mem_addr); + SHM_POOL_QUEUE_HEAD_ROLLBACK(ring_buf); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* + * Compute the payload length and fill it into the corresponding field of the proxy message header. + */ + proxy_msg_hdr->payload_len = payload_len + proxy_msg_payload_len; + +/* + * In MEMORY_ALLOC_SHARED mode, the build_proxy_general_message function is responsible for + * enqueuing the constructed message into the shared memory FIFO queue (ring_buf) + */ +#if 0 + if(MEMORY_ALLOC_SHARED == alloc_mode){ + utils_print("before SHMP_QUEUE_ENQUEUE, header = %d, tail = %d\n", ring_buf->header, ring_buf->tail); + SHMP_QUEUE_ENQUEUE(ring_buf, ret); + utils_print("after SHMP_QUEUE_ENQUEUE, header = %d, tail = %d\n", ring_buf->header, ring_buf->tail); + + SHM_POOL_QUEUE_HEAD_ROLLBACK(ring_buf); + utils_print("after SHM_POOL_QUEUE_HEAD_ROLLBACK, header = %d, tail = %d\n", ring_buf->header, ring_buf->tail); + return ret; + } +#endif + + +#if 0 + int debug_cnt; + utils_print("shared queue capacity = %d, header = %d, tail = %d, block_size = %d\n", ring_buf->capacity, ring_buf->header, ring_buf->tail, ring_buf->block_size); + + debug_cnt = ring_buf->header; + + utils_print("Debug shared memory I/O\n"); + utils_print("virt addr = %lld\n", ring_buf->virt_addr1); + while(debug_cnt < ring_buf->capacity + 10){ + uint64_t debug_mem_addr; + SHM_POOL_QUEUE_ALLOC_FROM_HEADER(ring_buf, &debug_mem_addr); + utils_print("shared queue header = %d, tail = %d, addr = %lld, diff = %d\n", ring_buf->header, ring_buf->tail, debug_mem_addr, debug_mem_addr - ring_buf->virt_addr1); + debug_cnt++; + } +#endif + return FRONTEND_PROXY_PROCESS_OK; +} +#endif + + +/** + * @brief Frontend proxy message processing main entry + * + * @details As the core message distribution function of the frontend proxy, it is responsible for parsing the type field of the input proxy message, + * and automatically routing to the corresponding specialized processing function according to the message type to realize differentiated processing of different types of proxy messages. + * Supported message types and their corresponding processing functions are as follows: + * - Device-related messages: Call frontend_proxy_dev_msg_process + * - Strategy-related messages: Call frontend_proxy_strgy_msg_process + * - Session-related messages: Call frontend_proxy_sess_msg_process + * - Data-related messages: Call frontend_proxy_data_msg_process + * + * @param msg Pointer to the proxy message buffer, pointing to a uint8_t array containing the message type identifier and specific message content + * @return int Processing result status code + * - FRONTEND_PROXY_PROCESS_OK: Message distribution succeeded (Note: The specific processing result is guaranteed by the corresponding specialized function) + * - FRONTEND_PROXY_PROCESS_ERROR: Message type parsing failed or an error occurred during the distribution process + * + * @note 1. The input parameter 'msg' must be non-null and point to valid memory; otherwise, undefined behavior may occur + * 2. The parsing rule of the message type must be consistent with that of the backend proxy (backend_proxy_msg_process) + * 3. The return result of the specialized processing function does not affect the return status of the current function; only whether the distribution process is successful is fed back + */ +int frontend_proxy_msg_process(uint8_t *msg){ + utils_print("In %s\n", __func__); + ProxyMsgHeader *proxy_msg_hdr; + int proxy_proto_ver, msg_len, ret; + ProxyMsgType msg_type; + uint16_t frontend_sess_id, backend_sess_id; + uint8_t *msg_ptr; + + struct FrontendSession* sess; + + proxy_msg_hdr = (ProxyMsgHeader *)msg; + +/* + * Currently, the backend protocol stack does not differentiate the protocol version, we reserve the protocol version for future extensions. + */ + proxy_proto_ver = proxy_msg_hdr->version; + frontend_sess_id = proxy_msg_hdr->frontend_sess_id; + backend_sess_id = proxy_msg_hdr->backend_sess_id; + msg_type = proxy_msg_hdr->proxy_msg_type; + msg_len = proxy_msg_hdr->payload_len; + + utils_print("In %s, version = %d, frontend sess id = %d, backend sess id = %d, msg_type = %d, msg_len = %d\n", + __func__, proxy_proto_ver, frontend_sess_id, backend_sess_id, msg_type, msg_len); +/* + * Check the validity of the message type. + */ + if(!PROXY_MSG_TYPE_VALID(msg_type)){ + error_print("frontend_proxy_msg_process failed: unsupported message type!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + }// Unsupported message type. + +/* + * Check the validity of the message length. + */ + if(!PROXY_MSG_LEN_VALID(msg_type)){ + error_print("frontend_proxy_msg_process failed: message length error!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + }// Unsupported message type. + + msg_ptr = (uint8_t *)proxy_msg_hdr; + msg_ptr += PROXY_MSG_HDR_SIZE; + + if(PROXY_MSG_TYPE_DEV == msg_type){ + /* + * The frontend proxy delivers device messages from the frontend admin session to the backend proxy for the backend admin session. + */ + if (frontend_sess_id != FRONTEND_ADMIN_SESSION_ID || backend_sess_id != BACKEND_ADMIN_SESSION_ID){ + error_print("frontend_proxy_msg_process failed: only admin sessions can deliver and process device messages!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + ret = frontend_proxy_dev_msg_process(msg_ptr); + }else if(PROXY_MSG_TYPE_STRGY == msg_type){ + /* + * The frontend proxy delivers strategy messages from the frontend admin session to the backend proxy for the backend admin session. + */ + if (frontend_sess_id != FRONTEND_ADMIN_SESSION_ID || backend_sess_id != BACKEND_ADMIN_SESSION_ID){ + error_print("frontend_proxy_msg_process failed: only admin sessions can deliver and process strategy messages!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + ret = frontend_proxy_strgy_msg_process(msg_ptr); + }else if(PROXY_MSG_TYPE_SESS == msg_type){ + ret = frontend_proxy_sess_msg_process(frontend_sess_id, backend_sess_id, msg_ptr); + }else{ +/* + * When msg_type is PROXY_MSG_TYPE_DATA, the frontend_sess_id and backend_sess_id should be checked to determine whether the session (if it exists) + * is an application session. + */ + if(!APP_SESSION_ID_VALID(frontend_sess_id) || !APP_SESSION_ID_VALID(backend_sess_id)){ + error_print("Both the frontend session ID and backend session ID in the proxy data message must pass the application session ID validation!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + ret = frontend_proxy_data_msg_prosess(frontend_sess_id, backend_sess_id, msg_len, msg_ptr); + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/* + * Device message processing functions. + * frontend_proxy_dev_msg_process + * |->frontend_proxy_dev_msg_process_ver1 + * |->frontend_proxy_dev_msg_process_disable_ver1 + * |->frontend_proxy_dev_msg_process_enable_ver1 + * |->frontend_proxy_dev_msg_process_query_ver1 + */ +int frontend_proxy_dev_msg_process(uint8_t *msg){ + DevMsgHeader *dev_msg_hdr; + uint16_t version, msg_id, payload_len; + DevMsgType msg_type; + ActionType action_type; + int ret; + uint8_t *msg_data; + + ret = FRONTEND_PROXY_PROCESS_ERROR; + + dev_msg_hdr = (DevMsgHeader *)msg; + + if(NULL == dev_msg_hdr){ + error_print("frontend_proxy_dev_msg_process() failed: the msg pointer is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + version = dev_msg_hdr->version; + msg_type = dev_msg_hdr->msg_type; + msg_id = dev_msg_hdr->msg_id; + action_type = dev_msg_hdr->action_type; + payload_len = dev_msg_hdr->payload_len; + msg_data = msg + sizeof(DevMsgHeader); + +/* + * The frontend protocol stack only processes messages where the action_type is ACTION_TYPE_RESPONSE. + */ + if(ACTION_TYPE_RESPONSE != action_type){ + error_print("frontend_proxy_dev_msg_process() failed: the frontend protocol stack only processes the device message of the ACTION_TYPE_RESPONSE type!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } +/* + * Before processing device messages, the protocol stack should check the validity of parameters. + * + * Currently, the protocol stack only processes Version 1 device messages. + */ + + if(PROXY_PROTO_DEV_VERSION_1 == version){ + ret = frontend_proxy_dev_msg_process_ver1(msg_type, msg_id, action_type, payload_len, msg_data); + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_dev_msg_process_ver1(uint16_t msg_type, uint16_t msg_id, uint16_t action_type, uint16_t payload_len, uint8_t *msg_payload){ + int corr_len; + int ret = FRONTEND_PROXY_PROCESS_ERROR; + +/* + * Check whether the payload length matches the message type and signaling type. + */ + corr_len = DEV_MSG_PAYLOAD_LEN(msg_type, action_type); + + utils_print("In %s, corr_len = %d, payload_len = %d\n", __func__, corr_len, payload_len); + + if(PROXY_MSG_INVALID_LEN == corr_len || corr_len != payload_len){ + error_print("frontend_proxy_dev_msg_process_ver1() failed: invalid msg_type or payload length mismatch!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + switch(msg_type) { + case DEV_MSG_DISABLE: + ret = frontend_proxy_dev_msg_process_disable_ver1(payload_len, msg_payload); + break; + case DEV_MSG_ENABLE: + ret = frontend_proxy_dev_msg_process_enable_ver1(payload_len, msg_payload); + break; + case DEV_MSG_QUERY: + ret = frontend_proxy_dev_msg_process_query_ver1(payload_len, msg_payload); + break; + default: +/* + * Nothing to do, because the validation of the msg_type is checked before. + */ + break; + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_dev_msg_process_disable_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_dev_msg_process_enable_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_dev_msg_process_query_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/* + * Strategy message processing functions. + * frontend_proxy_strgy_msg_process + * |->frontend_proxy_strgy_msg_process_ver1 + * |->frontend_proxy_strgy_msg_process_set_ver1 + * |->frontend_proxy_strgy_msg_process_query_ver1 + */ + +int frontend_proxy_strgy_msg_process(uint8_t *msg){ + StrgyMsgHeader *strgymsg_hdr; + uint16_t version, msg_id, payload_len; + StrgyMsgType msg_type; + ActionType action_type; + int ret; + uint8_t *msg_data; + + ret = FRONTEND_PROXY_PROCESS_ERROR; + + if(NULL == strgymsg_hdr){ + error_print("frontend_proxy_strgy_msg_process() failed: the msg pointer is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + version = strgymsg_hdr->version; + msg_type = strgymsg_hdr->msg_type; + msg_id = strgymsg_hdr->msg_id; + action_type = strgymsg_hdr->action_type; + payload_len = strgymsg_hdr->payload_len; + msg_data = msg + sizeof(StrgyMsgHeader); + + +/* + * The frontend protocol stack only processes messages where the action_type is ACTION_TYPE_RESPONSE. + */ + if(ACTION_TYPE_RESPONSE != action_type){ + error_print("frontend_proxy_strgy_msg_process() failed: the frontend protocol stack only processes the strategy messages of type ACTION_TYPE_RESPONSE!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* + * Before processing device messages, the protocol stack should check the validity of parameters. + * + * Currently, the protocol stack only processes Version 1 strategy messages. + */ + if(PROXY_PROTO_STRGY_VERSION_1 == version){ + ret = frontend_proxy_strgy_msg_process_ver1(msg_type, msg_id, action_type, payload_len, msg_data); + } + + return FRONTEND_PROXY_PROCESS_OK; +} + +int frontend_proxy_strgy_msg_response(uint8_t *msg); + + +int frontend_proxy_strgy_msg_process_ver1(uint16_t msg_type, uint16_t msg_id, uint16_t action_type, uint16_t payload_len, uint8_t *msg_payload){ + int corr_len; + int ret = FRONTEND_PROXY_PROCESS_ERROR; + +/* + * Check whether the payload length matches the message type and signaling type. + */ + corr_len = STRGY_MSG_PAYLOAD_LEN(msg_type, action_type); + + utils_print("In %s, corr_len = %d, payload_len = %d\n", __func__, corr_len, payload_len); + + if(PROXY_MSG_INVALID_LEN == corr_len || corr_len != payload_len){ + error_print("backend_proxy_strgy_msg_process_ver1() failed: invalid msg_type or payload length mismatch!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + switch(msg_type) { + case STRGY_MSG_SET: + ret = frontend_proxy_strgy_msg_process_set_ver1(payload_len, msg_payload); + break; + case STRGY_MSG_QUERY: + ret = frontend_proxy_strgy_msg_process_query_ver1(payload_len, msg_payload); + break; + default: +/* + * Nothing to do, because the validation of the msg_type is checked before. + */ + break; + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_strgy_msg_process_set_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + +int frontend_proxy_strgy_msg_process_query_ver1(uint16_t payload_len, uint8_t *msg_payload){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Processes session messages in the frontend proxy + * @details This function serves as the core handler for session-related communication between front and backend proxies. + * It handles general session message processing by parsing the incoming message, coordinating with the specified + * frontend and backend sessions, and executing appropriate operations based on message content. + * The processing follows a hierarchical call structure: + * frontend_proxy_sess_msg_process + * |-> frontend_proxy_sess_msg_process_ver1 + * |-> frontend_proxy_sess_msg_process_active_create_ver1 + * |-> frontend_proxy_sess_msg_process_close_ver1 + * @param[in] frontend_sess_id 16-bit identifier of the frontend session, used to map the message to + * the corresponding frontend session context. + * @param[in] backend_sess_id 16-bit identifier of the backend session, used to associate the message + * with the relevant backend session state and resources. + * @param[in] msg Pointer to the session message data to be processed, containing the complete + * message content (e.g., operation type, parameters, metadata). Must not be NULL. + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK on successful message processing; + * returns FRONTEND_PROXY_PROCESS_ERROR if message parsing fails, session identifiers are invalid, or + * the requested operation cannot be completed. + */ +int frontend_proxy_sess_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint8_t *msg){ + SessMsgHeader *sess_msg_hdr; + uint16_t version, payload_len; + SessMsgType msg_type; + ActionType action_type; + SessIpProtoVersion ip_version; + int ret; + uint8_t *msg_data; + + ret = FRONTEND_PROXY_PROCESS_ERROR; + + sess_msg_hdr = (SessMsgHeader *)msg; + + + if(NULL == sess_msg_hdr){ + error_print("backend_proxy_sess_msg_process() failed: the msg pointer is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + version = sess_msg_hdr->version; + msg_type = sess_msg_hdr->msg_type; + action_type = sess_msg_hdr->action_type; + ip_version = sess_msg_hdr->ip_version; + payload_len = sess_msg_hdr->payload_len; + msg_data = msg + sizeof(SessMsgHeader); + + utils_print("In %s\n", __func__); + utils_print("In %s, version = %d, msg_type = %d, action type = %d, ip version = %d, payload len = %d, address = %p\n", + __func__, version, msg_type, action_type, ip_version, payload_len, &sess_msg_hdr); + + +#if 0 +/* + * The frontend protocol stack only processes messages where the action_type is ACTION_TYPE_RESPONSE. + */ + if(ACTION_TYPE_RESPONSE != action_type){ + error_print("frontend_proxy_sess_msg_process() failed: the frontend protocol stack only processes session messages of type ACTION_TYPE_RESPONSE!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } +#endif + +/* + * Before processing device messages, the protocol stack should check the validity of parameters. + * + * Currently, the protocol stack only processes Version 1 device messages. + */ + +#if 0 + SessParaIPv4 *debug_hdr = (SessParaIPv4 *)msg_data; + SessIPv4Params *debug_hdr2 = (SessIPv4Params *)msg_data; + utils_print("In %s, type is SessParaIPv4, dev_id = %d, trans_proto = %d, port = %d\n", __func__, debug_hdr->dev_id, debug_hdr->trans_proto, debug_hdr->port); + utils_print("In %s, type is SessIPv4Params, devive_selection = %d, transport_layer_proto = %d\n", + __func__, debug_hdr2->device_selection, debug_hdr2->transport_layer_proto); +#endif + + if(PROXY_PROTO_SESS_VERSION_1 == version){ + ret = frontend_proxy_sess_msg_process_ver1(frontend_sess_id, backend_sess_id, msg_type, action_type, ip_version, payload_len, msg_data); + } + + return ret; +} + + +int frontend_proxy_sess_msg_response(uint8_t *msg); + + +int frontend_proxy_sess_msg_process_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t msg_type, + uint16_t action_type, uint16_t ip_version, uint16_t payload_len, + uint8_t *msg_payload){ + int corr_len, ret; + +/* + * Check whether the payload length matches the message type and signaling type. + */ + + utils_print("In %s\n", __func__); + + corr_len = SESS_MSG_PAYLOAD_LEN(msg_type, action_type, ip_version); + + utils_print("corr_len = %d, payload_len = %d\n", corr_len, payload_len); + + if(PROXY_MSG_INVALID_LEN == corr_len || corr_len != payload_len){ + error_print("frontend_proxy_sess_msg_process_ver1 failed: invalid msg_type or payload length mismatch!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + switch(msg_type) { + case SESS_MSG_CREATE: + if(ACTION_TYPE_RESPONSE == action_type){ + ret = frontend_proxy_sess_msg_process_active_create_ver1(frontend_sess_id, backend_sess_id, ip_version, payload_len, msg_payload); + }else if(ACTION_TYPE_COMMAND == action_type){ + ret = frontend_proxy_sess_msg_process_passive_create_ver1(frontend_sess_id, backend_sess_id, ip_version, payload_len, msg_payload); + }else{ + error_print("frontend_proxy_sess_msg_process_ver1 failed: unsupported action type (neither ACTION_TYPE_RESPONSE nor ACTION_TYPE_COMMAND)!\n"); + ret = FRONTEND_PROXY_PROCESS_ERROR; + } + + break; + case SESS_MSG_CLOSE: + ret = frontend_proxy_sess_msg_process_close_ver1(frontend_sess_id, backend_sess_id, payload_len, msg_payload); + break; + default: +/* + * Nothing to do, because the validation of the msg_type is checked before. + */ + break; + } + + return ret; +} + + +/** + * @brief Processes version 1 of **active** session creation messages in the frontend proxy + * @details This function handles the processing logic for version 1 **active** session creation messages in the frontend proxy layer. + * It parses the incoming message payload, performs parameter validation (e.g., payload length check, IP version validity), + * and executes frontend-side **active** session creation operations. It is responsible for establishing the mapping between + * the frontend session and backend session, and ensuring the correct transmission of **active** session creation details + * for the version 1 message format. + * @param[in] frontend_sess_id 16-bit identifier of the frontend session, uniquely marks the session on the frontend side + * for associating with client requests and backend session mapping + * @param[in] backend_sess_id 16-bit identifier of the backend session, used by the frontend to track and associate with + * the corresponding backend-side session instance + * @param[in] ip_version 16-bit value indicating the IP protocol version (e.g., IPv4 = 4, IPv6 = 6) adopted by the current session + * @param[in] payload_len 16-bit length of the message payload (in bytes), specifies the valid data size in the msg_payload buffer + * @param[in] msg_payload Pointer to the buffer storing the version 1 **active** session creation message payload, containing + * detailed configuration parameters required for **active** session establishment + * @return int Execution result: Typically returns FRONTEND_PROXY_PROCESS_OK on successful processing (including payload parsing, + * validation passed, and **active** session creation completed), or FRONTEND_PROXY_PROCESS_ERROR if parameter invalidation, + * payload mismatch, or **active** session creation failure occurs. + */ +int frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload){ + return __frontend_proxy_sess_msg_process_active_create_ver1(frontend_sess_id, backend_sess_id, ip_version, payload_len, msg_payload); +} + + + + +int __frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload){ + struct FrontendSessionPool *pool; + struct FrontendSessionPoolOps *sess_pool_ops; + struct FrontendSession *sess; + SessOpRespData *resp_data; +#if 0 + SessParaIPv4 *para_ipv4; + SessParaIPv6 *para_ipv6; + IPv4PortTuple *ipv4_port_tuple; + IPv6PortTuple *ipv6_port_tuple; + struct IPv4Address *ipv4_addr; + struct IPv6Address *ipv6_addr; + struct SessMsgPara sess_para; +#endif + bool ip_ver_valid = true; + int ret; + +/* + * The main body of the session creation procedure lies in the function which the create_sess_step2 pointer points to. + * In __frontend_proxy_sess_msg_process_active_create_ver1, this function parses the session parameters and calls the function pointed to by the create_sess_step2 pointer to establish a new session. + */ + pool = frontend_get_high_speed_pool(); + + if(NULL == pool || NULL == pool->ops || NULL == pool->ops->search_sess){ + error_print("__frontend_proxy_sess_msg_process_active_create_ver1 failed: the high-speed session pool is not initialized correctly!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess_pool_ops = pool->ops; + sess = sess_pool_ops->search_sess(pool, frontend_sess_id); + + if(NULL == sess){ + error_print("__frontend_proxy_sess_msg_process_active_create_ver1 failed: search session failed, no matching frontend session found in high-speed session pool.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(NULL == sess_pool_ops->create_sess_step2){ + error_print("__frontend_proxy_sess_msg_process_active_create_ver1 failed: high-speed session pool operation function 'create_sess_step2' is not initialized (NULL pointer). Session creation step 2 cannot be executed.\n"); + } + + resp_data = (SessOpRespData *)msg_payload; + ret = sess_pool_ops->create_sess_step2(pool, sess, backend_sess_id, resp_data); + + if(FRONTEND_PROXY_PROCESS_OK == ret){ + sess->event_callback(sess, FRONTEND_SESS_EVENT_CONN); + }else{ + sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNOMAL); + } + + + return ret; +} + + +/** + * @brief Processes version 1 of **passive** session creation messages in the frontend proxy + * @details This function handles the processing logic for version 1 **passive** session creation messages in the frontend proxy layer. + * It parses the incoming message payload, performs parameter validation (e.g., payload length check, IP version validity), + * and executes frontend-side **passive** session creation operations. It is responsible for establishing the mapping between + * the frontend session and backend session, and ensuring the correct transmission of **passive** session creation details + * for the version 1 message format. + * @param[in] frontend_sess_id 16-bit identifier of the frontend session, which is specifically **FRONTEND_HANDOVER_SESSION_ID** + * for associating with client requests and backend session mapping during passive session establishment + * @param[in] backend_sess_id 16-bit identifier of the backend session, used by the frontend to track and associate with + * the corresponding backend-side session instance + * @param[in] ip_version 16-bit value indicating the IP protocol version (e.g., IPv4 = SESS_IPV4_PROTO, IPv6 = SESS_IPV6_PROTO) adopted + * by the current session + * @param[in] payload_len 16-bit length of the message payload (in bytes), specifies the valid data size in the msg_payload buffer + * @param[in] msg_payload Pointer to the buffer storing the version 1 **passive** session creation message payload, containing + * detailed configuration parameters required for **passive** session establishment + * @return int Execution result: Typically returns FRONTEND_PROXY_PROCESS_OK on successful processing (including payload parsing, + * validation passed, and **passive** session creation completed), or FRONTEND_PROXY_PROCESS_ERROR if parameter invalidation, + * payload mismatch, or **passive** session creation failure occurs. + */ +int frontend_proxy_sess_msg_process_passive_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload){ + return __frontend_proxy_sess_msg_process_passive_create_ver1(frontend_sess_id, backend_sess_id, ip_version, payload_len, msg_payload); +} + + +int __frontend_proxy_sess_msg_process_passive_create_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t ip_version, uint16_t payload_len, uint8_t *msg_payload){ + FrontendEngine *eng; + struct SharedMemoryPoolQueue *rx_queue, *tx_queue; + struct FrontendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *new_sess = NULL; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + GeneralProxyMsgHeader proxy_msg_hdr; + struct SessMsgPara msg_para; + SessParaIPv4 *para_ipv4; + SessParaIPv6 *para_ipv6; + IPv4PortTuple *ipv4_port_tuple; + IPv6PortTuple *ipv6_port_tuple; + SessOpRespData resp_data; + struct IPv4Address *ipv4_addr; + uint8_t *proxy_msg; + uint32_t msg_size; + int new_sess_id = 0; + int ret = FRONTEND_PROXY_PROCESS_OK; + + eng = frontend_get_global_engine(); + + if(NULL == eng || NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: frontend engine instance has not been successfully initialized!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess_pool = eng->sess_pool; + sess_pool_ops = sess_pool->ops; + + if(SESS_IPV4_PROTO != ip_version){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: IP protocol version not supported, only IPv4 is supported currently, \ + IPv6 support will be expanded in the future!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_PARAMETER_INVALID; + goto create_sess_error; + } + +/* + * For the session-creation command sent from the backend to the frontend, + * the valid value of the frontend session ID in the message shall be FRONTEND_HANDOVER_SESSION_ID (0xFF). + */ + if(FRONTEND_HANDOVER_SESSION_ID != frontend_sess_id){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: invalid session creation command!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_PARAMETER_INVALID; + goto create_sess_error; + } + + +/* + * Alloc resource for the new creating session. + */ + new_sess = malloc(sizeof(struct FrontendSession)); + + if(NULL == new_sess){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: failed to allocate memory!"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_RESOURCE_INSUFFICIENT; + goto create_sess_error; + } + + new_sess->event_callback = default_session_event_callback; + + new_sess_id = allocate_id(&sess_pool->id_queue); + if(0 == new_sess_id){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: failed to allocate session ID!"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_RESOURCE_INSUFFICIENT; + goto create_sess_error; + } + + proxy_msg_hdr.outer_header.frontend_sess_id = new_sess_id; + + +/* + * Fill the fields of the SessMsgPara structure instance. + * This function/module is responsible for populating all required parameters into the SessMsgPara struct, + * which provides core data support for subsequent session creation operations. + */ + para_ipv4 = (SessParaIPv4 *)msg_payload; + memset(&msg_para, 0, sizeof(struct SessMsgPara)); + msg_para.frontend_sess_id = frontend_sess_id; + msg_para.backend_sess_id = backend_sess_id; + msg_para.dev_id = para_ipv4->dev_id; + msg_para.ip_version = ip_version; + msg_para.trans_proto = para_ipv4->trans_proto; + msg_para.ip_port_tuple.ipv4_port_tuple.port = para_ipv4->port; + + ipv4_addr = &msg_para.ip_port_tuple.ipv4_port_tuple.ipv4_addr; + memcpy(ipv4_addr, ¶_ipv4->ipv4_addr, sizeof(struct IPv4Address)); + + ret = sess_pool_ops->create_sess_passive(sess_pool, new_sess, &msg_para); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("__frontend_proxy_sess_msg_process_passive_create_ver1 failed: "); + goto create_sess_error; + } + + return ret; + +create_sess_error: +/* + * Reclaim resources. + */ + + + if(NULL == new_sess){ + free(new_sess); + } + + if(0 != new_sess_id){ + release_id(&sess_pool->id_queue, new_sess_id); + } + + return FRONTEND_PROXY_PROCESS_ERROR; +} + +/** + * @brief Processes version 1 of session close messages in the frontend proxy + * @details This function handles the processing logic for version 1 session close messages in the frontend proxy layer. + * It parses the message payload, validates the validity of frontend and backend session identifiers, + * executes frontend-side session termination operations, and cleans up associated resources (e.g., session context, + * connection mappings). It is specifically designed for the version 1 session close message format, + * coordinating the termination of the frontend-backend associated session and ensuring proper resource release. + * @param[in] frontend_sess_id 16-bit identifier of the frontend session, used to locate and target the frontend session to be closed + * @param[in] backend_sess_id 16-bit identifier of the backend session, used to associate and notify the corresponding backend session for termination + * @param[in] payload_len 16-bit length of the message payload (in bytes), indicating the valid data size in the msg_payload buffer + * @param[in] msg_payload Pointer to the session close message payload, containing detailed parameters for the close operation (e.g., termination reason). Must not be NULL. + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK on successful processing of the close message, session termination, and resource cleanup; + * Returns FRONTEND_PROXY_PROCESS_ERROR if payload is invalid, session identifiers are incorrect, parsing fails, or session closure/resource cleanup encounters errors. + */ +int frontend_proxy_sess_msg_process_close_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t payload_len, uint8_t *msg_payload){ + return __frontend_proxy_sess_msg_process_close_ver1(frontend_sess_id, backend_sess_id, payload_len, msg_payload); +} + + + +int __frontend_proxy_sess_msg_process_close_ver1(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t payload_len, uint8_t *msg_payload){ + struct FrontendSessionPool *pool; + struct FrontendSessionPoolOps *sess_pool_ops; + struct FrontendSession *sess; + SessOpRespData *resp_data; + uint8_t status, code; + + pool = frontend_get_high_speed_pool(); + + if(NULL == pool || NULL == pool->ops || NULL == pool->ops->search_sess){ + error_print("__frontend_proxy_sess_msg_process_close_ver1 failed: the high-speed session pool is not initialized correctly!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess_pool_ops = pool->ops; + sess = sess_pool_ops->search_sess(pool, frontend_sess_id); + + if(NULL == sess){ + error_print("__frontend_proxy_sess_msg_process_close_ver1 failed: search session failed, no matching frontend session found in high-speed session pool.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + resp_data = (SessOpRespData *)msg_payload; + + sess_pool_ops->close_sess_step2(pool, sess, resp_data); + +#if 0 + resp_data = (SessOpRespData *)msg_payload; + status = resp_data->status; + + if(SESS_OP_STATUS_SUCCESS != status){ + utils_print("In %s, the session creating procedurec failed, the error code is %d\n", __func__, code); + sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNOMAL); + }else{ + sess->event_callback(sess, FRONTEND_SESS_EVENT_CLOSE); + } +#endif + + sess_pool_ops->delete_sess(pool, sess); + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Processes proxy data messages for frontend proxy (between frontend and backend sessions) + * This function handles the processing of data messages that need to be proxied between a frontend session and its + * corresponding backend session on the frontend proxy side. It involves message validation, protocol format adaptation, + * and routing to the target session (frontend or backend) based on the provided session identifiers and message content, + * ensuring seamless data transmission across the proxy link. + * @param frontend_sess_id Unique identifier of the frontend session (source/destination of the message) + * @param backend_sess_id Unique identifier of the backend session (counterpart session for proxying) + * @param data_len Length of the message data in bytes (specifies valid range of the msg buffer) + * @param msg Pointer to the message data buffer (uint8_t array) to be processed/proxied + * @return int Processing result status: + * FRONTEND_PROXY_PROCESS_OK: Message processed and proxied successfully + * FRONTEND_PROXY_PROCESS_ERROR: Failed to process or proxy the message (e.g., invalid session IDs, + * invalid message format, protocol adaptation failure, or forwarding failure) + * @note 1. The message buffer (msg) is assumed to contain valid data conforming to the proxy protocol; its length + * should match the actual data length specified by data_len to avoid out-of-bounds access; + * Callers must ensure frontend_sess_id and backend_sess_id refer to active, valid sessions (the frontend + * session is managed by the frontend proxy, and the backend session is the associated counterpart), + * otherwise processing errors will occur; + * This function does not take ownership of the msg buffer; the caller is responsible for managing its + * lifecycle (e.g., allocation and release) to prevent memory leaks. + */ +int frontend_proxy_data_msg_prosess(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t data_len, uint8_t *msg){ +/* + * STEP 1. Search for the destination frontend session instance in the session pool using backend_sess_id. If it fails to find + * the appropriate session instance, frontend_proxy_data_msg_process shall return FRONTEND_PROXY_PROCESS_ERROR; + * otherwise, proceed to STEP 2. + * STEP 2. Construct a struct SessMsgSeg object, bind the data message to this SessMsgSeg object, and then link this SessMsgSeg object + * to the msg_b2f queue of the session instance. + * STEP 3. Link the frontend session instance to the queue_b2f of the session pool instance to which the session instance belongs. + * The frontend proxy protocol will process all sessions in queue_b2f and forward all data messages in each msg_b2f queue after + * it receives all the data messages in the shared-memory queue. This procedure exists outside frontend_proxy_data_msg_process; + * we just make a note here to help readers maintain a consistent understanding. + */ + struct FrontendEngine_ *eng; + struct FrontendSessionPool *s_pool; + struct FrontendSessionPoolOps *ops; + struct FrontendSession *sess; + struct SharedMemoryPool *mem_pool; + struct SessMsgSeg *msg_seg; + int ret; + + eng = frontend_get_global_engine(); + + if(NULL == eng || NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("frontend_proxy_data_msg_prosess failed: eng, eng->sess_pool, or eng->sess_pool->ops is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + s_pool = eng->sess_pool; + ops = eng->sess_pool->ops; + mem_pool = eng->mem_pool; + + if(NULL == ops->search_sess){ + error_print("frontend_proxy_data_msg_prosess failed: ops->search_sess (session searching function) is not initialized!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess = ops->search_sess(s_pool, frontend_sess_id); + + if(NULL == sess){ + error_print("frontend_proxy_data_msg_prosess failed: no frontend session found for the specified frontend_sess_id!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + msg_seg = sess_msg_seg_alloc(data_len, SESS_MSG_SEG_DYNAMIC_ALLOC, msg, mem_pool); + + if(NULL == msg_seg){ + error_print("frontend_sess_send failed: insufficient memory for allocating message segment instance!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + utils_print("In %s, after sess_msg_seg_alloc\n", __func__); +/* + * Insert the message segment into the back-to-front message queue. + */ + SESS_MSG_SEG_INSERT_QUEUE(sess, msg_seg, b2f); + utils_print("In %s, after SESS_MSG_SEG_INSERT_QUEUE\n", __func__); + + FRONTEND_SESS_LINK_TO_QUEUE(sess, b2f); + + utils_print("In %s, after FRONTEND_SESS_LINK_TO_QUEUE\n", __func__); + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_proxy_data_msg_recv(struct FrontendSession *sess, uint8_t *msg); +int frontend_proxy_data_msg_send(struct FrontendSession *sess, uint8_t *msg); \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c new file mode 100644 index 0000000..f468d93 --- /dev/null +++ b/projects/sel4test/apps/front/src/main.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "frontend_api.h" +#include "senario_test.h" + +extern void tailq_test(); +extern void uthash_test(); +int main(void){ + printf("Running tests\n"); + FrontendEngine *eng; + struct FrontendSession *sess; + int ret; +#if 0 + seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); + unsigned long long *vaddrs=(unsigned long long *)*Ptr2ShmemCommBuff; + int* a = malloc(10*sizeof(int)); + *a = 1; + printf("a = %d, addr of a = %p\n", *a, a); + tailq_test(); + uthash_test(); +#endif + frontend_engine_init(); + + eng = frontend_get_global_engine(); + sess = frontend_sess_new(eng); + ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.100:8080"); + + test_proxy_scenario_multi_type_msg_build_frontend(eng); + test_proxy_scenario_msg_read_from_rx_queue_frontend(eng); + + return 0; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c new file mode 100644 index 0000000..27c7c76 --- /dev/null +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -0,0 +1,445 @@ +#include "senario_test.h" + +/* + * GeneralProxyMsgHeader dev_enable_msg_hdr, strgy_query_msg_hdr, sess_create_msg_hdr, data_msg_hdr; + */ +GeneralProxyMsgHeader dev_enable_msg_hdr = { + .outer_header = { + .version = PROXY_PROTO_VERSION_1, // Protocol version, fixed to 1 as specified + .proxy_msg_type = PROXY_MSG_TYPE_DEV, // Proxy message type: Device message (0) + .frontend_sess_id = FRONTEND_ADMIN_SESSION_ID, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy + .backend_sess_id = BACKEND_ADMIN_SESSION_ID, // Backend admin session ID, used to match backend-backend sessions in frontend proxy + .payload_len = sizeof(DevMsgHeader) + sizeof(DevMsgReport) // Payload length, set according to actual payload + }, + .inner_header.dev_hdr = { // Use device message inner header + .version = PROXY_PROTO_DEV_VERSION_1, // Protocol version, fixed to 1 + .msg_type = DEV_MSG_ENABLE, // Message type: Enable (1) + .msg_id = 0, // Message ID, used for command-response matching + .action_type = ACTION_TYPE_RESPONSE, // Signaling type: Response (1) + .payload_len = sizeof(DevMsgReport) // Payload length, set according to actual payload + } +}; + + +// Strategy set message header +GeneralProxyMsgHeader strgy_set_msg_hdr = { + .outer_header = { + .version = PROXY_PROTO_VERSION_1, // Protocol version, fixed to 1 as specified + .proxy_msg_type = PROXY_MSG_TYPE_STRGY, // Proxy message type: Strategy message (1) + .frontend_sess_id = FRONTEND_ADMIN_SESSION_ID, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy + .backend_sess_id = BACKEND_ADMIN_SESSION_ID, // Backend admin session ID, used to match backend-backend sessions in frontend proxy + .payload_len = sizeof(StrgyMsgHeader) + sizeof(StrgyMsgReport) // Payload length, set according to actual payload + }, + .inner_header.strgy_hdr = { // Use strategy message inner header + .version = PROXY_PROTO_STRGY_VERSION_1, // Protocol version, fixed to 1 + .msg_type = STRGY_MSG_SET, // Message type: Set (0) + .msg_id = 0, // Message ID, used for command-response matching + .action_type = ACTION_TYPE_RESPONSE, // Signaling type: Response (0) + .payload_len = sizeof(StrgyMsgReport) // Payload length, set according to actual payload + } +}; + + +// Session create message header +GeneralProxyMsgHeader sess_create_msg_hdr = { + .outer_header = { + .version = PROXY_PROTO_VERSION_1, // Protocol version, fixed to 1 as specified + .proxy_msg_type = PROXY_MSG_TYPE_SESS, // Proxy message type: Session message (2) + .frontend_sess_id = 1, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy + .backend_sess_id = 1, // Backend admin session ID, used to match backend-backend sessions in backend proxy + .payload_len = sizeof(SessMsgHeader) + sizeof(SessOpRespData) // Payload length, set according to actual payload + }, + .inner_header.sess_hdr = { // Use session message inner header + .version = PROXY_PROTO_SESS_VERSION_1, // Protocol version, fixed to 1 + .msg_type = SESS_MSG_CREATE, // Message type: Create (1) + .action_type = ACTION_TYPE_RESPONSE, // Signaling type: RESPONSE (1) + .ip_version = SESS_IPV4_PROTO, // IP version: IPv4 (4), can be changed to IPv4 (6) if needed + .payload_len = sizeof(SessOpRespData) // Payload length, set according to actual payload + } +}; + + +// data message header +GeneralProxyMsgHeader proxy_data_msg_hdr = { + .outer_header = { + .version = PROXY_PROTO_VERSION_1, // Protocol version, fixed to 1 as specified + .proxy_msg_type = PROXY_MSG_TYPE_DATA, // Proxy message type: Session message (3) + .frontend_sess_id = 1, // Frontend session ID, used to match frontend-backend sessions in frontend proxy + .backend_sess_id = 1, // Backend session ID, used to match backend-backend sessions in frontend proxy + .payload_len = 0 // Payload length, set according to actual payload + }, +}; + +/** + * @brief Simulate backend-end service responses, construct proxy messages via build_proxy_general_message, and inject them into the shared memory RX queue of the back-end engine + * @details Implements the core responsibility of "scenario functions" in the "Backend Protocol Stack Unit Test.doc": + * 1. Calls the existing build_proxy_general_message to construct complete proxy messages (supports both shared memory and caller-allocated memory modes); + * 2. Injects the constructed messages into the front-end engine's shared memory RX queue in compliance with FIFO read-write rules (avoids queue overflow or data overwriting); + * 3. Returns detailed injection results to locate issues like queue initialization exceptions or message construction failures, + * and triggers the front-end engine's request processing logic (e.g., engine_run reads from RX queue). + * + * @param[in] engine Pointer to a FrontendEngine object containing the front-end proxy's global context (e.g., runtime configuration, memory pool handles). + * Required by build_proxy_general_message for message construction (e.g., accessing memory management resources). + * Must not be NULL (consistent with build_proxy_general_message's parameter constraint). + * @param[in] msg_header Pointer to a GeneralProxyMsgHeader structure specifying the header of the injected message (e.g., message type, payload length). + * Must not be NULL (passed to build_proxy_general_message as the "header" parameter) and comply with the protocol specification in the document. + * @param[in] msg_payload Pointer to the const uint8_t buffer containing the message payload (business data like device status, strategy config). + * Can be NULL only if msg_payload_len is 0 (consistent with build_proxy_general_message's payload constraint). + * @param[in] msg_payload_len Length of msg_payload in bytes. Must be non-negative and match msg_header->payload_len (if the header has a payload length field) + * to ensure message consistency (follows build_proxy_general_message's parameter rule). + * @param[in] alloc_mode Memory allocation mode for message construction, using the same MemoryAllocMode enum as build_proxy_general_message: + * - MEMORY_ALLOC_SHARED: Allocates memory in the shared memory FIFO ring buffer (rx_queue acts as ring_buf for build_proxy_general_message); + * - MEMORY_ALLOC_CALLER: Requires the caller to pre-allocate memory (build_proxy_general_message populates the pre-allocated buffer). + * @param[out] result_msg Double pointer to receive the address of the constructed message (consistent with build_proxy_general_message's "result_msg" parameter): + * - MEMORY_ALLOC_SHARED: Points to the message in the rx_queue's FIFO ring buffer (no caller deallocation needed); + * - MEMORY_ALLOC_CALLER: Points to the caller's pre-allocated buffer (populated with the complete message). + * Must not be NULL. + * @param[out] result_desc Buffer to store detailed injection results (e.g., "Request injected successfully into RX queue", "RX queue full, injection failed"). + * Helps locate test startup issues (document's "injection status feedback" requirement). Must not be NULL. + * @param[in] desc_len Maximum length of the result_desc buffer to prevent buffer overflow (ensures safe result storage). + * + * @return int Status code following the back-end protocol stack's unified specification (consistent with build_proxy_general_message's return type): + * - BACKEND_PROXY_PROCESS_OK: Message constructed successfully and injected into RX queue; + * - BACKEND_PROXY_PROCESS_ERROR: General error (e.g., build_proxy_general_message fails, rx_queue uninitialized); + * + * @note 1. Depends on the existing build_proxy_general_message function (must ensure its correctness before using this injection function); + * 2. Before injection, confirm FrontendEngine is initialized (engine_init returns BACKEND_PROXY_PROCESS_OK) and rx_queue is ready (document's test prerequisite); + * 3. For MEMORY_ALLOC_SHARED, ensure rx_queue is a valid SharedMemoryPoolQueue (used for FIFO ring buffer operations in build_proxy_general_message); + * 4. For MEMORY_ALLOC_CALLER, the caller must guarantee the pre-allocated buffer (result_msg) is large enough to hold the complete message (header + payload) + * (follows build_proxy_general_message's constraint for this mode); + * 5. Supports all message types defined in the protocol document by configuring msg_header->message_type. + */ +int scenario_msg_inject_frontend(FrontendEngine *engine, + GeneralProxyMsgHeader *msg_header, + const uint8_t *msg_payload, + size_t msg_payload_len, + MemoryAllocMode alloc_mode, + uint8_t **result_msg, + char *result_desc, + size_t desc_len){ + int ret; + + // 1. Check if BackendEngine pointer is NULL (engine initialization is a prerequisite per "Backend Protocol Stack Unit Test.doc") + if (engine == NULL) + { + error_print("scenario_msg_inject_frontend failed: BackendEngine pointer is NULL, engine must be initialized!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 2. Check if engine->rx_queue (shared memory RX queue) is NULL (queue initialization is required for message injection, per document) + if (engine->rx_queue == NULL) + { + error_print("scenario_msg_inject_frontend failed: engine->rx_queue (SharedMemoryPoolQueue) is NULL, RX queue must be initialized via engine_init_shared_mem_queue"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 3. Check if GeneralProxyMsgHeader pointer is NULL (valid header is required for message parsing, per document's message handling rules) + if (msg_header == NULL) + { + error_print("scenario_msg_inject_frontend failed: GeneralProxyMsgHeader pointer is NULL, valid message header is required"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 4. Check if result_msg double pointer is NULL (used to store constructed message address, per document's scenario function requirements) + if (result_msg == NULL) + { + error_print("scenario_msg_inject_frontend failed: result_msg double pointer is NULL, cannot store address of constructed message"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 5. Check if result_desc pointer is NULL (used to feedback injection status, per document's "injection status feedback" requirement) + if (result_desc == NULL) + { + error_print("scenario_msg_inject_frontend failed: result_desc pointer is NULL, cannot store injection result description"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 6. Check consistency between msg_payload and msg_payload_len (empty payload requires len=0, per document's data message integrity rules) + if (msg_payload == NULL && msg_payload_len > 0) + { + error_print("scenario_msg_inject_frontend failed: msg_payload is NULL but msg_payload_len > 0, violates payload integrity rules"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 7. Check if MemoryAllocMode is valid (covers undefined modes, aligns with document's "shared memory/caller-allocated dual mode") + if (alloc_mode != MEMORY_ALLOC_SHARED && alloc_mode != MEMORY_ALLOC_CALLER) + { + error_print("scenario_msg_inject failed: invalid MemoryAllocMode, only MEMORY_ALLOC_SHARED and MEMORY_ALLOC_CALLER are supported"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // 8. Check if desc_len is valid (prevents buffer overflow when writing result_desc, per safe coding practices in document context) + if (desc_len == 0) + { + error_print("scenario_msg_inject failed: desc_len is 0, insufficient buffer size for result_desc"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* ------------------------------ + * Normal logic starts here (e.g., call build_proxy_general_message, inject to engine->rx_queue) + *------------------------------ + * Example: Initialize result_desc with success info first (if no errors) + * strncpy(result_desc, "scenario_msg_inject: request injection started", desc_len - 1); + * result_desc[desc_len - 1] = '\0'; + * ... (call build_proxy_general_message, handle queue injection, etc.) + */ + + utils_print("In %s, before enter build_proxy_general_message, build message type = %d, msg_payload_len = %d\n", __func__, msg_header->outer_header.proxy_msg_type, msg_payload_len); + ret = build_proxy_general_message(engine, msg_header, msg_payload, msg_payload_len, result_msg, alloc_mode, engine->rx_queue); + + return ret; +} + + +/** + * @brief Inject a device message into the frontend engine + * @details Handles injection of device-related messages, processing according to + * device management logic and returning results via output parameters. + * + * @param engine Pointer to the BackendEngine instance + * @return int Return code indicating processing result: + * - FRONTEND_PROXY_PROCESS_OK: Message injected and processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to inject or process the message + */ +int device_msg_inject_frontend(FrontendEngine *engine){ + GeneralProxyMsgHeader *dev_msg_hdr; + DevMsgReport dev_msg_resp; + int ret, desc_len = 100; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + + dev_msg_hdr = &dev_enable_msg_hdr; + + dev_msg_resp.status = SESS_OP_STATUS_SUCCESS; + dev_msg_resp.error = SESS_OP_CODE_SUCCESS; + dev_msg_resp.data = 0xFF; + + + res_string = res_buf; + desc_string = desc_buf; + + utils_print("In %s, before enter scenario_msg_inject\n", __func__); + ret = scenario_msg_inject_frontend(engine, dev_msg_hdr, &dev_msg_resp, sizeof(dev_msg_resp), MEMORY_ALLOC_SHARED, res_string, desc_string, desc_len); + + return ret; +} + + + +/** + * @brief Inject a strategy message into the frpmtend engine + * @details Handles injection of strategy/policy-related messages, processing according to + * strategy management logic and returning results via output parameters. + * + * @param engine Pointer to the BackendEngine instance + * @return int Return code indicating processing result: + * - FRONTEND_PROXY_PROCESS_OK: Message injected and processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to inject or process the message + */ +int strategy_msg_inject_frontend(FrontendEngine *engine){ + GeneralProxyMsgHeader *strgy_msg_hdr; + StrgyMsgReport strgy_resp; + int ret, desc_len = 100; + + uint8_t **res_string; + + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + + + strgy_msg_hdr = &strgy_set_msg_hdr; + strgy_resp.error = STRGY_OP_STATUS_SUCCESS; + strgy_resp.status = STRGY_OP_CODE_SUCCESS; + res_string = res_buf; + desc_string = desc_buf; + + ret = scenario_msg_inject_frontend(engine, strgy_msg_hdr, &strgy_resp, sizeof(strgy_resp), MEMORY_ALLOC_SHARED, res_string, desc_string, desc_len); + + return ret; +} + + +/** + * @brief Inject a session message into the frontend engine + * @details Handles injection of session-related messages, processing according to + * session management logic and returning results via output parameters. + * + * @param engine Pointer to the BackendEngine instance + * @return int Return code indicating processing result: + * - FRONTEND_PROXY_PROCESS_OK: Message injected and processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to inject or process the message + */ +int session_msg_inject_frontend(FrontendEngine *engine){ + GeneralProxyMsgHeader *sess_msg_hdr; + SessOpRespData sess_msg_resp; + int ret, desc_len = 100; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + + sess_msg_hdr = &sess_create_msg_hdr; + sess_msg_resp.status = SESS_OP_STATUS_SUCCESS; + sess_msg_resp.code = SESS_OP_CODE_SUCCESS; + res_string = res_buf; + desc_string = desc_buf; + + ret = scenario_msg_inject_frontend(engine, sess_msg_hdr, &sess_msg_resp, sizeof(sess_msg_resp), MEMORY_ALLOC_SHARED, res_string, desc_string, desc_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Inject a data message into the frontend engine + * @details Handles injection of data/content-related messages, processing according to + * data processing logic and returning results via output parameters. + * + * @param engine Pointer to the BackendEngine instance + * @return int Return code indicating processing result: + * - FRONTEND_PROXY_PROCESS_OK: Message injected and processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to inject or process the message + */ +int data_msg_inject_frontend(FrontendEngine *engine){ + GeneralProxyMsgHeader *data_msg_hdr; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + char data_buf[100]; + int ret, desc_len = 100; + + memset(data_buf, 0, sizeof(data_buf)); + snprintf(data_buf, sizeof(data_buf), "test msg"); + + utils_print("strlen(test msg) = %d\n", strlen("test msg")); + utils_print("content of data_buf = %s\n", data_buf); + + data_msg_hdr = &proxy_data_msg_hdr; + res_string = res_buf; + desc_string = desc_buf; + + data_msg_hdr->outer_header.payload_len = strlen("test msg"); + + utils_print("outer_header.payload_len = %d\n", data_msg_hdr->outer_header.payload_len); + utils_print("In %s, version = %d, type = %d\n", __func__, data_msg_hdr->outer_header.version, data_msg_hdr->outer_header.proxy_msg_type); + DUMP_BUFFER_CONTENT(data_buf, 8, "%c"); + + ret = scenario_msg_inject_frontend(engine, data_msg_hdr, data_buf, strlen(data_buf), MEMORY_ALLOC_SHARED, res_string, desc_string, desc_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int test_proxy_scenario_multi_type_msg_build_frontend(FrontendEngine *engine){ + device_msg_inject_frontend(engine); + strategy_msg_inject_frontend(engine); + session_msg_inject_frontend(engine); + data_msg_inject_frontend(engine); + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Upper-layer scenario-based test function for the frontend proxy layer, which uniformly reads multi-type proxy messages from the shared memory RX queue + * and simulates the data reading process. + * @details Designed based on the core responsibilities of "Scenario Functions", serving as a standardized entry for unit testing: + * Automatically reads all core proxy message types that may exist in the queue, including device messages, strategy messages, session messages, and data messages + * (the actual types and quantities are uncertain, depending on the injected content); + * Calls the underlying message reading functions (e.g., frontend_engine_rx_queue_get, scenario_msg_read) to complete the parsing and extraction of structured messages; + * Obtains the shared memory RX queue handle from the input FrontendEngine global context (which must be initialized in advance), and reads messages from the queue following FIFO rules; + * Does not require external input of message parameters (e.g., expected msg type, msg ID). All test verification logic (such as checking message structure validity, matching preset parameters) is + * based on the test scenarios (e.g., verifying that session messages contain preset session ID = 1, data messages match preset payload length) to ensure test consistency. + * @param[in] engine Pointer to the FrontendEngine global context, which must meet the requirements in the document: + * Must be successfully initialized via the engine_init function (returning FRONTEND_PROXY_PROCESS_OK) to ensure that resources such as the memory pool handle and + * shared memory RX queue (engine->rx_queue) are ready + * The context must contain valid runtime configurations (e.g., shared memory queue size, message parsing rules) to avoid resource unavailability errors during message reading or parsing + * @return int Follows the unified error code specification in the document, with return value meanings as follows: + * FRONTEND_PROXY_PROCESS_OK: All existing types of proxy messages in the RX queue are successfully read and parsed; + * FRONTEND_PROXY_PROCESS_ERROR: All abnormal scenarios are covered, including uninitialized FrontendEngine, NULL internal resources (e.g., rx_queue), empty shared memory RX queue (when messages + * are expected), and failed calls to underlying message reading/parsing functions + * @note 1. Precondition: The engine must be initialized before calling this function, and messages must be pre-injected into the RX queue (e.g., via test_proxy_scenario_multi_type_msg_build_frontend) (refer to the normal scenario process in "Test Process - Message Reading Test" of "Frontend Proxy Layer Unit Test.doc"), otherwise a process error will be returned directly; + * Message coverage: The current version supports reading 4 core message types, which may exist in any combination (one or more types) depending on the injection scenario. + * Device messages: Verify consistency with preset "device status query" commands; + * Strategy messages: Verify consistency with preset "query strategy configuration" commands (matching the "strategy configuration command parsing" scenario); + * Session messages: Verify consistency with preset "create session" (valid device ID) and "close session" (valid session ID) commands (matching the "session command parsing" scenario); + * Data messages: Verify consistency with preset boundary scenarios (empty payload and maximum-length payload (4088 bytes)); + * Result feedback: The function internally uses error_print to print detailed logs of message reading/parsing (e.g., "Device message read successfully", "Data message parsing failed: + * invalid payload length"), + * and the log format complies with the log specification for "message processing failure"; + * Subsequent verification: After message reading is completed, the function may trigger internal verification logic (e.g., checking message count, comparing with injected content) to confirm + * the correctness of the reading process, thus completing the full test link of "inject-read-verify". + */ +int test_proxy_scenario_msg_read_from_rx_queue_frontend(FrontendEngine *engine){ + struct SharedMemoryPoolQueue *rx_queue, *tx_queue; + struct BackendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *cur_sess, *next_sess; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + uint8_t *proxy_msg, **p_proxy_msg; + uint8_t content[8]; + ProxyMsgHeader *proxy_msg_hdr; + uint32_t msg_size; + int ret; + + rx_queue = engine->rx_queue; +// proxy_msg = content; + p_proxy_msg = &proxy_msg; + + if(NULL == rx_queue){ + error_print("test_proxy_scenario_msg_read_from_rx_queue_frontend failed: the rx_queue of the engine is not initialized!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +#if 1 + proxy_msg = frontend_engine_rx_queue_get_msg(rx_queue, PROXY_MSG_HDR_PLUS_MAX_SIZE, &ret, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get_msg\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + frontend_proxy_msg_process(proxy_msg); + + proxy_msg = frontend_engine_rx_queue_get_msg(rx_queue, PROXY_MSG_HDR_PLUS_MAX_SIZE, &ret, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get_msg\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + frontend_proxy_msg_process(proxy_msg); + + proxy_msg = frontend_engine_rx_queue_get_msg(rx_queue, PROXY_MSG_HDR_PLUS_MAX_SIZE, &ret, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get_msg\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + frontend_proxy_msg_process(proxy_msg); + + proxy_msg = frontend_engine_rx_queue_get_msg(rx_queue, PROXY_MSG_HDR_PLUS_MAX_SIZE, &ret, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get_msg\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + frontend_proxy_msg_process(proxy_msg); +#endif + +#if 0 + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + +// DUMP_BUFFER_CONTENT(proxy_msg, msg_size, "%d"); + + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); +// frontend_proxy_msg_process(proxy_msg); + + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + + ret = frontend_engine_rx_queue_get(rx_queue, &proxy_msg, PROXY_MSG_HDR_PLUS_MAX_SIZE, &msg_size); + utils_print("In %s, after enter frontend_engine_rx_queue_get\n", __func__); + utils_print("rx queue header is %d, tail is %d, msg_size is %d, address of msg is %p\n", rx_queue->header, rx_queue->tail, msg_size, proxy_msg); + + + +#endif + return FRONTEND_PROXY_PROCESS_OK; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c new file mode 100644 index 0000000..52ef346 --- /dev/null +++ b/projects/sel4test/apps/front/src/session.c @@ -0,0 +1,246 @@ + +#include "session.h" +#include "common_utils.h" + +/** + * @brief Allocates and initializes a SessMsgSeg structure + * + * @param len Length of the data buffer (in bytes) + * @param type Memory source type of the data buffer (dynamic allocation or shared memory) + * @param shared_data Pointer to shared memory data (valid only when data_src is SESS_MSG_SEG_SHARED_MEM) + * @param mem_pool Pointer to the SharedMemoryPool instance (valid only when type is SESS_MSG_SEG_SHARED_MEM). + Used to associate the SessMsgSeg with the shared memory pool for management (e.g., validation, release tracking). + * + * @return Pointer to the newly allocated SessMsgSeg on success; NULL on failure + * + * @note - If data_src is SESS_MSG_SEG_DYNAMIC_ALLOC: allocates data buffer with malloc() + * - If data_src is SESS_MSG_SEG_SHARED_MEM: uses shared_data directly (does not allocate new memory) + * - Initializes TAILQ entry to default state + */ +struct SessMsgSeg *sess_msg_seg_alloc(size_t len, SessMsgSegType type, uint8_t *shared_data, struct SharedMemoryPool *mem_pool){ + struct SessMsgSeg *msg_seg; + msg_seg = malloc(sizeof(struct SessMsgSeg)); + + if(NULL == msg_seg){ + error_print("sess_msg_seg_alloc failed: insurficient memory resource for message segment!"); + return NULL; + } + + switch(type) { + case SESS_MSG_SEG_DYNAMIC_ALLOC: + msg_seg->data = malloc(len); + + if(NULL == msg_seg->data){ + error_print("sess_msg_seg_alloc failed: insurficient memory resource for message data!"); + goto msg_alloc_error; + } + break; + case SESS_MSG_SEG_SHARED_MEM: +/* + * The memory for storing data is prealloc from the memory pool. + */ + if(NULL == mem_pool){ + error_print("sess_msg_seg_alloc failed: Shared memory pool (mem_pool) cannot be NULL when using SESS_MSG_SEG_SHARED_MEM type!"); + goto msg_alloc_error; + } + + msg_seg->data = shared_data; + msg_seg->mem_pool = mem_pool; + break; + + default: +/* + * Unsupported message segment type. + */ + error_print("sess_msg_seg_alloc failed: unsupported message segment type!"); + goto msg_alloc_error; + } + + msg_seg->type = type; + msg_seg->len = len; + + return msg_seg; +msg_alloc_error: + free(msg_seg); + return NULL; +} + + + +/** + * @brief Lightweight allocation and initialization of a SessMsgSeg structure + * + * A minimal version of message segment allocation that only initializes the core type field, + * with no data buffer allocation or data copy operations, ensuring ultra-low overhead. + * + * @param type Type of the message segment (must be a valid enumeration value < SESS_MSG_SEG_TYPE_MAX) + * + * @return Pointer to the newly allocated and initialized SessMsgSeg on success; NULL on failure + * (e.g., invalid type, insufficient memory for the SessMsgSeg structure itself) + * + * @note - Lightweight design: Does not allocate data buffers, not associate shared data, + * and only initializes the `type` field and default state of basic structure members. + * - Default initializations: The `len` field is set to 0, `shared_data` to NULL, + * reference count (`ref_cnt`) to 1, and linked list pointer (`next`) to NULL. + * - If data association is required (e.g., binding shared data), use supplementary interfaces + * or the full-version `sess_msg_seg_alloc()` function. + * - The structure's memory is allocated via the default lightweight memory mechanism + * (no external memory pool dependency, ensuring minimal allocation overhead). + */ +struct SessMsgSeg* sess_msg_seg_alloc_lite(SessMsgSegType type){ + struct SessMsgSeg *msg_seg; + + if(SESS_MSG_SEG_DYNAMIC_ALLOC != type || SESS_MSG_SEG_SHARED_MEM != type){ + error_print("sess_msg_seg_alloc failed: invalid! message segment type!\n"); + return NULL; + } + + msg_seg = malloc(sizeof(struct SessMsgSeg)); + + if(NULL == msg_seg){ + error_print("sess_msg_seg_alloc failed: insurficient memory resource for message segment!\n"); + return NULL; + } + + msg_seg->type = type; + + return msg_seg; +} + +/** + * @brief Releases a SessMsgSeg structure and its associated resources + * + * @param seg_ptr Double pointer to the SessMsgSeg to be released (will be set to NULL after release) + * + * @note - If type is SESS_MSG_SEG_DYNAMIC_ALLOC: frees the data buffer with free() + * - If type is SESS_MSG_SEG_SHARED_MEM: does not free data (managed by shared memory system) + * - Safely handles NULL input (no operation performed) + */ +void sess_msg_seg_free(struct SessMsgSeg **seg_ptr){ + struct SessMsgSeg *msg_seg; + + if(NULL == seg_ptr || NULL == *seg_ptr){ + error_print("sess_msg_seg_free failed: input pointer is invalid!"); + return; + } + + msg_seg = *seg_ptr; + + + switch(msg_seg->type) { + case SESS_MSG_SEG_DYNAMIC_ALLOC: + if(NULL == msg_seg->data){ + return; + } + free(msg_seg->data); + break; + case SESS_MSG_SEG_SHARED_MEM: +/* + * The memory belongs to the shared memory pool. + */ + break; + + default: +/* + * Unsupported message segment type. + */ + error_print("sess_msg_seg_free failed: unsupported message segment type!"); + } + + free(msg_seg); +} + + +/** + * @brief Release all SessMsgSeg elements in the SessMsgQueue + * + * This function traverses the SessMsgQueue, releases each SessMsgSeg element + * according to its memory type, and finally clears the queue. + * + * For dynamic allocation type (SESS_MSG_SEG_DYNAMIC_ALLOC): + * - Free the data buffer allocated by malloc() + * - Free the SessMsgSeg structure itself + * + * For shared memory type (SESS_MSG_SEG_SHARED_MEM): + * - Do not free the shared data buffer (managed by SharedMemoryPool) + * - Only free the SessMsgSeg structure itself + * + * @param queue Pointer to the SessMsgQueue to be cleared + */ +void sess_msg_queue_free_all(struct SessMsgQueue *queue) { + if (queue == NULL) { + return; // Avoid null pointer operation + } + + struct SessMsgSeg *seg, *next_seg; + + + TAILQ_FOREACH_SAFE(seg, queue, entry, next_seg) { + /* 1. Remove the segment from the queue */ + TAILQ_REMOVE(queue, seg, entry); + + /* 2. Deallocate memory based on segment type */ + if (seg->type == SESS_MSG_SEG_DYNAMIC_ALLOC) { + // Free dynamically allocated data buffer + free(seg->data); + } else if (seg->type == SESS_MSG_SEG_SHARED_MEM) { + + // if (current_seg->mem_pool) { + // shared_memory_pool_release(current_seg->mem_pool, current_seg->data); + // } + } + /* 3. Free the segment structure itself */ + free(seg); + + }// TAILQ_FOREACH_SAFE + + +#if 0 + TAILQ_FOREACH(seg, queue, entry){ + // Manually save the next node before releasing current node + next_seg = TAILQ_NEXT(seg, entry); + + // Remove current segment from the queue + TAILQ_REMOVE(queue, seg, entry); + + // Free resources based on memory type + if (SESS_MSG_SEG_DYNAMIC_ALLOC == seg->type) { + // Free dynamically allocated data buffer + if (NULL != seg->data) { + free(seg->data); + seg->data = NULL; + } + } + + if (SESS_MSG_SEG_SHARED_MEM == seg->type) { + // Free dynamically allocated data buffer + if (NULL != seg->data || NULL != seg->mem_pool) { + free_shared_mem(seg->mem_pool, (uint64_t)seg->data); + } + } + + + // Shared memory data is managed by SharedMemoryPool, no need to free here + + // Free the SessMsgSeg structure itself + free(seg); + + // Move to next node (since current node is freed) + seg = next_seg; + } +#endif + + TAILQ_INIT(queue); +} + + +int session_send(struct FrontendSession* sess, const uint8_t* data, uint32_t size) +{ + return 0; +} + + +int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size) +{ + return 0; +} diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c new file mode 100644 index 0000000..5d53be7 --- /dev/null +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -0,0 +1,801 @@ +#include +#include + +#include "session_pool.h" +#include "message.h" +#include "engine.h" +#include "shared_mem_io.h" +#include "common_utils.h" + +FrontendEngine *p_g_fr_eng = NULL; +FrontendEngine g_fr_eng; +struct FrontendSessionPool *front_high_speed_pool = NULL; +//ops +// int frontend_high_speed_create_sess(struct FrontendSessionPool *s_pool, struct FrontendSession **sess, struct SessMsgPara *para); +int frontend_high_speed_create_sess_step1(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para); +int frontend_high_speed_create_sess_step2(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, uint16_t sess_id, SessOpRespData *resp); +int frontend_high_speed_create_sess_passive(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para); +int frontend_high_speed_insert_sess(struct FrontendSessionPool* s_pool, struct FrontendSession *sess); +struct FrontendSession* frontend_high_speed_search_sess(struct FrontendSessionPool *s_pool, uint16_t id); +int frontend_high_speed_delete_sess(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); +void frontend_high_speed_destroy_pool(struct FrontendSessionPool *s_pool); +int frontend_high_speed_close_sess_step1(struct FrontendSessionPool *s_pool, struct FrontendSession *sess); +int frontend_high_speed_close_sess_step2(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, SessOpRespData *resp); + + + + +struct FrontendSessionPoolOps fr_high_speed_pool_ops = { + .create_sess_step1 = frontend_high_speed_create_sess_step1, + .create_sess_step2 = frontend_high_speed_create_sess_step2, + .insert_sess = frontend_high_speed_insert_sess, + .search_sess = frontend_high_speed_search_sess, + .delete_sess = frontend_high_speed_delete_sess, + .data_process_b2f = frontend_high_speed_data_process_b2f, + .data_process_f2b = frontend_high_speed_data_process_f2b, + .close_sess_step1 = frontend_high_speed_close_sess_step1, + .close_sess_step2 = frontend_high_speed_close_sess_step2, + .destroy_pool = frontend_high_speed_destroy_pool +}; + +/** + * @brief Get the global FrontendEngine instance + * + * This function provides a global access point and returns a pre-initialized + * FrontendEngine singleton pointer. It is suitable for scenarios where the same + * frontend engine instance needs to be shared throughout the entire program. + * + * @return FrontendEngine* Pointer to the global FrontendEngine instance, guaranteed + * to be non-null (provided the global instance is properly initialized) + * @note 1. The global instance `p_g_fr_eng` must be initialized before calling this function; + * otherwise, a null pointer or wild pointer may be returned. + * 2. This function is not thread-safe. In a multi-threaded environment, ensure the + * instance is fully initialized before invoking this function. + * 3. Directly modifying the state of the instance pointed to by the returned pointer + * is not recommended. If modifications are required, use the interfaces provided + * by the FrontendEngine class. + */ +FrontendEngine *frontend_get_global_engine(){ + return p_g_fr_eng; +} + +struct FrontendSessionPool *frontend_get_high_speed_pool(){ + return front_high_speed_pool; +} + +//helper func +void fill_id_queue(struct FrontendSessionIDQueue *id_q) +{ + uint16_t q_num = 1024; + for (uint16_t i = 1; i <= q_num; i++) + { + struct FrontendSessionID* id_e = (struct FrontendSessionID*)malloc( + sizeof(struct FrontendSessionID)); + if (!id_e) { + printf("Memory allocate failed!\n"); + exit(1); + } + id_e->id = i; + TAILQ_INSERT_TAIL(id_q, id_e, entry); + // printf("push %p %d\n", id_e, id_e->id); + } +} + +uint16_t allocate_id(struct FrontendSessionIDQueue *id_q) +{ + uint16_t res = 0; + + if(!TAILQ_EMPTY(id_q)) + { + struct FrontendSessionID *id_e = TAILQ_FIRST(id_q); + res = id_e->id; + // printf("pop %p %d\n", id_e, res); + TAILQ_REMOVE(id_q, id_e, entry); + free(id_e); + } + return res; +} + +void release_id(struct FrontendSessionIDQueue *id_q, uint16_t id) +{ + struct FrontendSessionID* id_e = (struct FrontendSessionID*)malloc( + sizeof(struct FrontendSessionID)); + if (!id_e) { + printf("Memory allocate failed!\n"); + exit(1); + } + id_e->id = id; + TAILQ_INSERT_TAIL(id_q, id_e, entry); + // printf("push %p %d\n", id_e, id_e->id); +} + + +/** + * @brief Initialize the frontend high-speed session pool + * + * This function initializes the `FrontendSessionPool` instance, including validating input parameters, + * setting core pool attributes, initializing internal linked queues, and binding pool operation functions. + * The initialization process follows these key steps: + * 1. Check if the input `pool` pointer is non-NULL (avoid null pointer dereference); + * 2. Set fixed pool name, default capacity (1024 sessions), and initialize current session count to 0; + * 3. Initialize three internal TAILQ linked queues (ID management queue, f2b/b2f data queues); + * 4. Populate the ID queue with available session IDs via `fill_id_queue()`; + * 5. Bind the pre-defined pool operation set `fr_high_speed_pool_ops` to the pool; + * 6. Initialize the hash table pointer to NULL (to be allocated or initialized later if needed). + * + * @param[in,out] pool Pointer to the FrontendSessionPool instance to be initialized. + * Must point to a pre-allocated memory block (cannot be NULL). + * + * @return int Return code indicating initialization result: + * - FRONTEND_PROXY_PROCESS_OK: Initialization succeeded; + * - FRONTEND_PROXY_PROCESS_ERROR: Initialization failed (input `pool` is NULL). + * + * @note 1. The `pool` instance must be pre-allocated (static or dynamic memory) before calling this function; + * 2. The pool capacity is fixed to 1024 in this implementation (modify `pool->capacity` manually if dynamic adjustment is needed); + * 3. The TAILQ queues (`id_queue`, `queue_f2b`, `queue_b2f`) rely on the system's TAILQ macro implementation (ensure relevant headers are included); + * 4. The `fr_high_speed_pool_ops` must be a pre-defined valid operation set (contains pool-related operation interfaces like session allocation/release); + * 5. The `pool->engine` assignment is commented out; uncomment it if the pool needs to associate with the global FrontendEngine instance, + * and ensure `get_global_frontend_engine()` is called after `frontend_engine_init()` to avoid null pointer risks. + * @warning Passing a NULL `pool` pointer will directly return an error and print a log via `error_print()`. + */ +int frontend_high_speed_init_pool(struct FrontendSessionPool *pool) +{ + if(NULL == pool){ + error_print("frontend_high_speed_init_pool failed: the point of pool is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + pool->pool_name = "frontend_high_speed_pool"; + pool->capacity = 1024; + pool->sess_num = 0; + + TAILQ_INIT(&pool->id_queue); + fill_id_queue(&pool->id_queue); + + TAILQ_INIT(&pool->queue_f2b); + TAILQ_INIT(&pool->queue_b2f); + + pool->htable = NULL; + pool->ops = &fr_high_speed_pool_ops; + pool->engine = frontend_get_global_engine(); + return FRONTEND_PROXY_PROCESS_OK; +} + + +void inc_sess_num(struct FrontendSessionPool *pool) +{ + pool->sess_num++; +} + +void dec_sess_num(struct FrontendSessionPool *pool) +{ + pool->sess_num--; +} + +void print_pool(struct FrontendSessionPool *s_pool) { + struct FrontendSession *s; + + for (s = s_pool->htable; s != NULL; s = s->hh.next) { + printf("sess id %d\n", s->backend_sess_id); + } +} + +void frontend_high_speed_delete_all_sess(struct FrontendSessionPool *s_pool) +{ + struct FrontendSession *current_sess, *tmp; + + HASH_ITER(hh, s_pool->htable, current_sess, tmp) { + HASH_DEL(s_pool->htable, current_sess); /* delete it */ + free(current_sess); /* free it */ + } +} + +//ops + +/** + * @brief Step 1 of high-speed session creation process in the frontend proxy + * @details This function serves as the first step in the frontend high-speed session creation workflow. + * It extracts necessary parameters from the input SessMsgPara structure, constructs a session creation Command message, + * and sends the constructed message to the backend proxy through the shared memory queue. + * The function focuses on message construction and inter-proxy communication, laying the foundation for subsequent + * session establishment steps between the frontend and backend. + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing frontend session-related resources + * (e.g., session pool initialization status, resource allocation constraints) + * @param[in,out] sess Double pointer to FrontendSession, used to return the pointer of the to-be-created high-speed session + * (the function may initialize the session context and assign it to this pointer for subsequent operations) + * @param[in] para Pointer to the SessMsgPara structure, containing core parameters required for constructing the session creation Command message + * (e.g., session identifiers, protocol configuration, connection parameters) + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the session creation Command message is successfully constructed + * and placed into the shared memory queue; Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/para is NULL, parameter validation fails, + * message construction fails, or the shared memory queue send operation encounters exceptions. + */ + int frontend_high_speed_create_sess_step1(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, + struct SessMsgPara *para){ + FrontendEngine *engine; + struct FrontendSessionPoolOps *sess_pool_ops; + uint16_t frontend_sess_id, new_sess_id, dev_id; + GeneralProxyMsgHeader proxy_msg_hdr; + SessIPv4Params ipv4_para; + uint8_t *msg_payload; + int ret; + uint8_t **res_msg, *res_buf[100] = {NULL}; + + + engine = s_pool->engine; + sess_pool_ops = s_pool->ops; + res_msg = res_buf; + + utils_print("In %s\n", __func__); + utils_print("The address of engine is %p\n", engine); + utils_print("The address of engine ops is %p\n", engine->ops); + utils_print("The address of session pool ops is %p\n", sess_pool_ops); +#if 0 + utils_print("In %s, the address of the engine is %p, ops is %p, hs_backend_eng_ops address is %p, and chooes_dev is %p\n", + __func__, engine, engine->ops, get_hs_backend_engine_ops(), engine->ops->choose_dev); +#endif + + if(NULL == engine){ + error_print("frontend_high_speed_create_sess_step1 fails: the session pool does not belong to any engine, or the engine is not initialized successfully!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + if(NULL == sess_pool_ops || NULL == sess_pool_ops->insert_sess){ + error_print("frontend_high_speed_create_sess_step1 fails: the session pool operation function set is not initialized correctly!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + + new_sess_id = allocate_id(&s_pool->id_queue); + if(0 == new_sess_id){ + error_print("frontend_high_speed_create_sess_step1 failed: failed to allocating session ID!"); + goto create_sess_error; + } + + +/* + * Initialize session object. + */ + sess->frontend_sess_id = new_sess_id; + sess->backend_sess_id = para->backend_sess_id; + sess->ip_version = para->ip_version; + sess->state_f2b &= FRONTEND_SESS_LINKED_TO_QUEUE; + sess->state_b2f &= FRONTEND_SESS_LINKED_TO_QUEUE; + sess->sess_state = FRONTEND_SESS_INITIALIZE; +// sess->data_process_callback = callback; + TAILQ_INIT(&sess->msg_f2b); + utils_print("In %s, TAILQ_EMPTY(&new_sess->msg_f2b) returns %d\n", __func__, TAILQ_EMPTY(&sess->msg_f2b)); + TAILQ_INIT(&sess->msg_b2f); + utils_print("In %s, TAILQ_EMPTY(&new_sess->msg_b2f) returns %d\n", __func__, TAILQ_EMPTY(&sess->msg_b2f)); + + +/* + * Construct a SESSION-CREATION command message and send it to the backend proxy to notify the latter of session creation. + */ + memset(&proxy_msg_hdr, 0, sizeof(GeneralProxyMsgHeader)); + memset(&ipv4_para, 0, sizeof(SessIPv4Params)); + + proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_SESS, + proxy_msg_hdr.outer_header.frontend_sess_id = sess->frontend_sess_id; + proxy_msg_hdr.outer_header.backend_sess_id = sess->backend_sess_id; + proxy_msg_hdr.outer_header.payload_len = sizeof(SessMsgHeader) + sizeof(SessIPv4Params); + + proxy_msg_hdr.inner_header.sess_hdr.version = PROXY_PROTO_SESS_VERSION_1; + proxy_msg_hdr.inner_header.sess_hdr.msg_type = SESS_MSG_CREATE; + proxy_msg_hdr.inner_header.sess_hdr.action_type = ACTION_TYPE_COMMAND; + proxy_msg_hdr.inner_header.sess_hdr.ip_version = para->ip_version; + proxy_msg_hdr.inner_header.sess_hdr.payload_len = sizeof(SessIPv4Params); + + ipv4_para.device_selection = para->dev_id; + ipv4_para.transport_layer_proto = para->trans_proto; + memcpy(&ipv4_para.dest_endpoint, ¶->ip_port_tuple.ipv4_port_tuple, sizeof(ipv4_para.dest_endpoint)); + + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &ipv4_para, sizeof(ipv4_para), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + + if(ret != FRONTEND_PROXY_PROCESS_OK){ + error_print("frontend_high_speed_create_sess_step1 failed: failed to build proxy general message or send to shared memory TX queue!"); + goto create_sess_error; + } +/* + * Insert the session instance into the session pool. + */ + sess->sess_state = FRONTEND_SESS_CONNECTING; + ret = sess_pool_ops->insert_sess(s_pool, sess); + + if(ret != FRONTEND_PROXY_PROCESS_OK){ + error_print("frontend_high_speed_create_sess_step1 failed: failed to insert the session instance into the session pool!"); + goto create_sess_error; + } + + return FRONTEND_PROXY_PROCESS_OK; + +create_sess_error: +/* + * Reclaim resources. + * The memory occupied by the session instance is reclaimed in the function pointed to by the delete_sess pointer. + */ + + if(0 != new_sess_id){ + release_id(&s_pool->id_queue, new_sess_id); + } + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + +/** + * @brief Step 2 of high-speed session creation process in the frontend proxy + * @details This function serves as the second step in the frontend high-speed session creation workflow, focusing on processing the backend proxy's response. + * If the response (resp) indicates successful session creation, the function updates the backend_sess_id member of the target FrontendSession + * with the backend-assigned sess_id. If the response returns a failure, the function prints a log message displaying the failure reason + * maintained in resp, reclaims the resources occupied by the target session (sess), and removes the session from the FrontendSessionPool (s_pool). + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing frontend session resources (e.g., removing invalid sessions from the pool) + * @param[in,out] sess Pointer to FrontendSession, the target high-speed session to be updated or resource-reclaimed; + * On success, its backend_sess_id is updated with sess_id; On failure, the session resources are reclaimed + * @param[in] sess_id 16-bit session ID assigned by the backend proxy, used to update the backend_sess_id of the target FrontendSession when the response is successful + * @param[in] resp Pointer to the SessOpRespData structure, containing the backend's session creation response status (success/failure) and corresponding failure reason + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the response is processed successfully (either session update or resource reclamation completed); + * Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/sess/resp is NULL, session update fails, or resource reclamation/removal from the pool encounters exceptions. + */ +int frontend_high_speed_create_sess_step2(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, uint16_t sess_id, SessOpRespData *resp){ + FrontendEngine *engine; + struct FrontendSessionPoolOps *sess_pool_ops; + uint16_t frontend_sess_id, new_sess_id, dev_id; + uint8_t status, code; + + status = resp->status; + code = resp->code; + sess_pool_ops = s_pool->ops; +/* + * The backend proxy cannot set up a session. It replies to the frontend proxy with the SESS_OP_STATUS_ERROR status and the errno-based reason. + * The frontend proxy, per the protocol, should record the errno-based reason, remove the session instance from the session pool, and release its resources. + */ + if(SESS_OP_STATUS_SUCCESS != status){ + utils_print("In %s, the session creating procedurec failed, the error code is %d\n", __func__, code); + release_id(&s_pool->id_queue, sess->frontend_sess_id); + } + + sess->backend_sess_id = sess_id; + sess->sess_state = FRONTEND_SESS_CONNECTED; + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Passive creation processing function for high-speed session in the frontend proxy + * @details This function is the core processing entry for the **passive creation workflow** of high-speed sessions in the frontend proxy, + * which is distinguished from the active session creation process. It is triggered by the session creation request/command from the backend proxy, + * extracts the necessary core parameters from the input SessMsgPara structure, and performs strict validity verification on all key parameters + * related to passive session creation, including the specified IP protocol version and core session parameters (e.g., frontend session ID, protocol configuration parameters). + * Meanwhile, it manages the frontend session resources through the FrontendSessionPool instance, initializes the context of the high-speed session to be passively created + * based on the validated IP protocol version (IPv4/IPv6), and assigns the relevant session attributes, resource information and network layer configuration + * to the FrontendSession structure. This function focuses on the core logic of passive trigger-based session parameter verification (including IP version validation), + * IP version-based session context initialization and frontend session resource allocation, and completes the key processing of high-speed session passive creation + * in the frontend proxy, laying a solid foundation for the subsequent establishment of the front-backend session link and the normal data interaction of the high-speed session. + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing frontend session-related resources + * (e.g., session pool initialization status, resource allocation constraints, session life cycle management, valid session quantity limit) + * @param[in,out] sess Pointer to the FrontendSession structure, used to initialize the context of the passively created high-speed session + * (the function will assign the relevant session attributes, resource information, protocol configuration and IP version-related network layer settings to this pointer + * for subsequent session link maintenance and data interaction operations) + * @param[in] para Pointer to the SessMsgPara structure, containing core session parameters required for passive creation of the high-speed session + * (e.g., session identifiers delivered by the backend, protocol configuration parameters, front-backend connection parameters, valid session ID range) + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the high-speed session passive creation logic is processed successfully, + * including all parameter (IP version + core session parameters) verification passed, IP version-based session context initialized completely, + * frontend session resource allocated normally and successful response to the backend for sending the new session creation command; + * Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/para is NULL, ip_version takes an invalid value, + * core session parameter validity verification fails, session context initialization fails, frontend session pool resource is insufficient, + * or other exceptions occur during the passive creation processing of the high-speed session. + */ +int frontend_high_speed_create_sess_passive(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, struct SessMsgPara *para){ + FrontendEngine *engine; + struct FrontendSessionPoolOps *sess_pool_ops; + GeneralProxyMsgHeader proxy_msg_hdr; + SessOpRespData resp_data; + uint8_t **res_msg, *res_buf[100] = {NULL}; + int ret; + +/* + * Check input parameters. + */ + if(NULL == s_pool || NULL == s_pool->engine || NULL == s_pool->engine->tx_queue || NULL == s_pool->ops || NULL == s_pool->ops->insert_sess || + NULL == s_pool->ops->search_sess || NULL == sess || NULL == para){ + error_print("frontend_high_speed_create_sess_passive failed: null value detected in critical input (s_pool/engine/tx_queue/ops/insert_sess/search_sess/sess/para)!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + engine = s_pool->engine; + sess_pool_ops = s_pool->ops; + +/* + * Fill information for constructing the session-creation response message. + */ + memset(&proxy_msg_hdr, 0, sizeof(GeneralProxyMsgHeader)); + proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_SESS, + proxy_msg_hdr.outer_header.backend_sess_id = para->backend_sess_id; + proxy_msg_hdr.outer_header.frontend_sess_id = para->frontend_sess_id; + proxy_msg_hdr.outer_header.payload_len = sizeof(SessMsgHeader) + sizeof(SessOpRespData); + + proxy_msg_hdr.inner_header.sess_hdr.version = PROXY_PROTO_SESS_VERSION_1; + proxy_msg_hdr.inner_header.sess_hdr.msg_type = SESS_MSG_CREATE; + proxy_msg_hdr.inner_header.sess_hdr.action_type = ACTION_TYPE_RESPONSE; + proxy_msg_hdr.inner_header.sess_hdr.ip_version = para->ip_version; + proxy_msg_hdr.inner_header.sess_hdr.payload_len = sizeof(SessOpRespData); + + if(SESS_IPV4_PROTO != para->ip_version){ + error_print("frontend_high_speed_create_sess_passive failed: IP protocol version not supported, only IPv4 is supported currently, \ + IPv6 support will be expanded in the future!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_PARAMETER_INVALID; + goto create_sess_passive_error; + } + +/* + * Check whether the frontend session ID exists in the session pool. + */ + if(sess_pool_ops->search_sess(s_pool, sess->frontend_sess_id)){ + error_print("frontend_high_speed_create_sess_passive failed: frontend session ID already exists in session pool!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_PARAMETER_INVALID; + goto create_sess_passive_error; + } + + + sess->state_f2b &= FRONTEND_SESS_LINKED_TO_QUEUE; + sess->sess_state = FRONTEND_SESS_CONNECTED; +// new_sess->data_process_callback = callback; + TAILQ_INIT(&sess->msg_f2b); + utils_print("In %s, TAILQ_EMPTY(&new_sess->msg_f2b) returns %d\n", __func__, TAILQ_EMPTY(&sess->msg_f2b)); + TAILQ_INIT(&sess->msg_b2f); + utils_print("In %s, TAILQ_EMPTY(&new_sess->msg_b2f) returns %d\n", __func__, TAILQ_EMPTY(&sess->msg_b2f)); + + ret = sess_pool_ops->insert_sess(s_pool, sess); + + if(ret != FRONTEND_PROXY_PROCESS_OK){ + error_print("frontend_high_speed_create_sess_passive failed: failed to insert the session instance into the session pool!\n"); + resp_data.status = SESS_OP_STATUS_FAIL; + resp_data.code = SESS_OP_CODE_RESOURCE_INSUFFICIENT; + goto create_sess_passive_error; + } + + resp_data.status = SESS_OP_STATUS_SUCCESS; + resp_data.code = SESS_OP_CODE_SUCCESS; + + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_high_speed_create_sess_passive: failed to build proxy response message for passive session creation!\n"); + } + + return ret; + +create_sess_passive_error: + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_high_speed_create_sess_passive: failed to build error notification message!\n"); + } + + return FRONTEND_PROXY_PROCESS_ERROR; +} + + +int frontend_high_speed_insert_sess(struct FrontendSessionPool *s_pool, struct FrontendSession *sess) +{ + struct FrontendSession *s; + HASH_FIND(hh, s_pool->htable, &sess->frontend_sess_id, sizeof(uint16_t), s); + if(s == NULL) + { + HASH_ADD(hh, s_pool->htable, frontend_sess_id, sizeof(uint16_t), sess); + inc_sess_num(s_pool); + printf("add %d\n", sess->frontend_sess_id); + }else{ + goto insert_error; + } + return FRONTEND_PROXY_PROCESS_OK; +insert_error: + return FRONTEND_PROXY_PROCESS_ERROR; +} + +struct FrontendSession *frontend_high_speed_search_sess(struct FrontendSessionPool *s_pool, uint16_t id) +{ + struct FrontendSession* s = NULL; + HASH_FIND(hh, s_pool->htable, &id, sizeof(uint16_t), s); + return s; +} + +int frontend_high_speed_delete_sess(struct FrontendSessionPool *s_pool, struct FrontendSession *sess) +{ + uint16_t frontend_sess_id, backend_sess_id; + int ret, ip_version; + FrontendEngine* *eng; + + + if(NULL == s_pool || NULL == sess || NULL == s_pool->engine){ + error_print("high_speed_delete_sess failed: session pool (s_pool), session (sess), or engine (s_pool->engine) is NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + frontend_sess_id = sess->frontend_sess_id; + backend_sess_id = sess->backend_sess_id; + ip_version = sess->ip_version; + + HASH_DEL(s_pool->htable, sess); + release_id(&s_pool->id_queue, frontend_sess_id); + + dec_sess_num(s_pool); + + FRONTEND_SESS_UNLINK_FROM_QUEUE(sess, f2b); + + sess_msg_queue_free_all(&sess->msg_f2b); + sess_msg_queue_free_all(&sess->msg_b2f); + + free(sess); + +/* + * TODO: send a close message to backend +*/ + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Step 1 of high-speed session closure process in the frontend proxy + * @details This function serves as the first step in the frontend high-speed session closure workflow. + * Its core responsibilities include validating the validity of the target session, marking the session state as "closing", + * preparing core parameters required for the closure process (e.g., session ID, connection context), + * and laying the foundation for subsequent closure coordination with the backend (such as sending closure notifications, + * resource synchronization). The function focuses on preprocessing and state initialization before session closure, + * ensuring the orderly initiation of the entire closure process. + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing global resources of the frontend session pool + * (e.g., session state tracking, resource allocation constraints, overall pool state verification) + * @param[in,out] sess Pointer to the FrontendSession instance to be closed; the function will modify the session's state + * (e.g., mark as closing) and read its core information for closure initialization. Must point to a valid session + * in an active or closable state. + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the closure initialization is successful + * (session validity verified, state marked as closing, core parameters prepared); + * Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/sess is NULL, the session is in an illegal state (e.g., already closed/invalid), + * session state marking fails, or core closure parameter preparation encounters exceptions. + * @note This function is only executed when the frontend actively initiates a session closure. For scenarios where the backend + * actively closes the session, only the frontend_high_speed_close_sess_step2 function will be invoked instead. + */ +int frontend_high_speed_close_sess_step1(struct FrontendSessionPool *s_pool, struct FrontendSession *sess){ + FrontendEngine *engine; + struct FrontendSessionPoolOps *sess_pool_ops; + uint16_t frontend_sess_id, backend_sess_id; + GeneralProxyMsgHeader proxy_msg_hdr; + uint8_t *msg_payload; + int ret; + uint8_t **res_msg, *res_buf[100] = {NULL}; + + + engine = s_pool->engine; + sess_pool_ops = s_pool->ops; + res_msg = res_buf; + + if(NULL == engine || NULL == engine->ops){ + error_print("frontend_high_speed_close_sess_step1 fails: the session pool does not belong to any engine, or the engine is not initialized successfully!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + if(NULL == sess_pool_ops){ + error_print("frontend_high_speed_close_sess_step1 fails: the session pool operation function set is not initialized correctly!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + memset(&proxy_msg_hdr, 0, sizeof(GeneralProxyMsgHeader)); + + proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_SESS, + proxy_msg_hdr.outer_header.frontend_sess_id = sess->frontend_sess_id; + proxy_msg_hdr.outer_header.backend_sess_id = sess->backend_sess_id; + proxy_msg_hdr.outer_header.payload_len = sizeof(SessMsgHeader) + sizeof(SessIPv4Params); + + proxy_msg_hdr.inner_header.sess_hdr.version = PROXY_PROTO_SESS_VERSION_1; + proxy_msg_hdr.inner_header.sess_hdr.msg_type = SESS_MSG_CLOSE; + proxy_msg_hdr.inner_header.sess_hdr.action_type = ACTION_TYPE_COMMAND; + proxy_msg_hdr.inner_header.sess_hdr.ip_version = sess->ip_version; + proxy_msg_hdr.inner_header.sess_hdr.payload_len = 0; + + ret = build_proxy_general_message(engine, &proxy_msg_hdr, NULL, 0, res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + + if(ret != FRONTEND_PROXY_PROCESS_OK){ + error_print("frontend_high_speed_close_sess_step1 failed: failed to build proxy general message or send to shared memory TX queue!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Step 2 of high-speed session closure process in the frontend proxy + * @details This function serves as the second step in the frontend high-speed session closure workflow, focusing on processing the backend proxy's session closure response. + * Its core responsibilities include validating the validity of the backend response data, releasing all resources occupied by the target session (e.g., connection handles, memory buffers, + * shared memory mappings), removing the session from the FrontendSessionPool, and synchronizing the final closure state. If the backend response (resp) indicates successful closure, + * the function performs orderly resource reclamation and pool cleanup; if the response returns a failure, the function logs the failure reason contained in resp and executes forced + * resource release to avoid leaks, ensuring the session is completely invalidated. + * @param[in] s_pool Pointer to the FrontendSessionPool instance, used for managing frontend session resources (e.g., removing the closed session from the pool, verifying pool validity) + * @param[in,out] sess Pointer to the FrontendSession instance to be closed; the function will release its occupied resources, update its final state to "closed", + * and remove it from the session pool. Must point to a valid session that has initiated the closure process. + * @param[in] resp Pointer to the SessOpRespData structure, containing the backend's session closure response status (success/failure) and corresponding failure reason + * (e.g., abnormal backend session termination, resource release failure) + * @return int Execution result: Returns FRONTEND_PROXY_PROCESS_OK if the backend response is processed successfully (resource reclamation, session removal from pool completed); + * Returns FRONTEND_PROXY_PROCESS_ERROR if s_pool/sess/resp is NULL, response data validation fails, session resource reclamation fails, or session removal from the pool encounters exceptions. + * @note This function is only invoked when the backend actively initiates a session closure. For scenarios where the frontend actively closes the session, this function will not be executed + * (only frontend_high_speed_close_sess_step1 will be used in the frontend-initiated closure process). + */ +int frontend_high_speed_close_sess_step2(struct FrontendSessionPool *s_pool, struct FrontendSession *sess, SessOpRespData *resp){ + uint8_t status, code; + int ret; + + status = resp->code; + + if(SESS_OP_STATUS_SUCCESS != status){ + utils_print("In %s, the session creating procedurec failed, the error code is %d\n", __func__, code); + sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNOMAL); + ret = FRONTEND_PROXY_PROCESS_ERROR; + }else{ + sess->event_callback(sess, FRONTEND_SESS_EVENT_CLOSE); + ret = FRONTEND_PROXY_PROCESS_OK; + } + + return ret; +} + + +int frontend_high_speed_data_process_b2f(struct FrontendSession *sess){ + + struct SessMsgSeg *cur_seg, *next_seg; + + TAILQ_FOREACH_SAFE(cur_seg, &sess->msg_b2f, entry, next_seg) { + + utils_print("cur_seg->len = %d, msg = %s\n", cur_seg->len, cur_seg->data); + if (cur_seg->data && cur_seg->len > 0){ + uint8_t buf[cur_seg->len+1]; + utils_print("seg size = %d\n", cur_seg->len); + memcmp(buf, cur_seg->data, cur_seg->len); + + /* + * TODO: link to callback function, and pass the buf data to callback function + */ + sess->data_process_callback(sess, buf, cur_seg->len); + + }else{ + /* Unable to recv the data in the queue */ + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + /* 2. Remove the segment from the queue */ + TAILQ_REMOVE(&sess->msg_b2f, cur_seg, entry); + + /* 3. Deallocate memory based on segment type */ + if (cur_seg->type == SESS_MSG_SEG_DYNAMIC_ALLOC) { + // Free dynamically allocated data buffer + free(cur_seg->data); + } else if (cur_seg->type == SESS_MSG_SEG_SHARED_MEM) { + + // if (current_seg->mem_pool) { + // shared_memory_pool_release(current_seg->mem_pool, current_seg->data); + // } + } + /* 4. Free the segment structure itself */ + free(cur_seg); + + }// TAILQ_FOREACH_SAFE + + return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_high_speed_data_process_f2b(struct FrontendSession *sess){ + struct SessMsgSeg *cur_seg, *next_seg; + GeneralProxyMsgHeader data_msg_hdr; + uint8_t *payload, **res_pointer; + uint8_t *res_buf[100] = {NULL}; + int ret; + + memset(&data_msg_hdr, 0 , sizeof(data_msg_hdr)); + + data_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + data_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_DATA; + data_msg_hdr.outer_header.backend_sess_id = sess->backend_sess_id; + data_msg_hdr.outer_header.frontend_sess_id = sess->frontend_sess_id; + res_pointer = res_buf; + +/* + * Core Logic: Safely traverse the f2b message queue, convert data in the queue to data-type messages + * and send them via the shared-memory TX queue by calling the build_proxy_general_message function. + * Safe traversal of the session's f2b message queue (TAILQ doubly linked list) to avoid iteration exceptions + * caused by node deletion during traversal. + * Parameter Description: + * cur_seg: Current traversed message segment node + * &sess->msg_f2b: Message queue head (f2b direction: frontend to backend message queue) + * entry: Member used for linking nodes in the list (standard field of TAILQ linked list nodes) + * next_seg: Temporarily stores the next node to ensure traversal can continue after deleting the current node + */ + TAILQ_FOREACH_SAFE(cur_seg, &sess->msg_f2b, entry, next_seg) { + data_msg_hdr.outer_header.payload_len = cur_seg->len; +/* + * Call the proxy general message construction function to assemble the data-type message + * Parameters: Engine instance, message header pointer, data segment pointer, data length, + * result storage pointer, memory allocation mode (shared memory), additional parameters + */ + ret = build_proxy_general_message(sess->eng, &data_msg_hdr, cur_seg->data, cur_seg->len, res_pointer, MEMORY_ALLOC_SHARED, sess->eng->tx_queue); + + if(FRONTEND_PROXY_PROCESS_OK == ret){ + TAILQ_REMOVE(&sess->msg_f2b, cur_seg, entry); + sess_msg_seg_free(cur_seg); + }else if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + error_print("frontend_high_speed_data_process_f2b stops: the shared-memory TX queue is full!\n"); + return FRONTEND_PROXY_PROCESS_AGAIN; + }else{ + error_print("frontend_high_speed_data_process_f2b failed: fail to build data message!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + }// TAILQ_FOREACH_SAFE(cur_seg, &sess->msg_f2b, entry, next_seg) + + return FRONTEND_PROXY_PROCESS_OK; +} + + +void frontend_high_speed_destroy_pool(struct FrontendSessionPool *s_pool) +{ + s_pool->pool_name = NULL; + s_pool->capacity = 0; + s_pool->sess_num = 0; + s_pool->ops = NULL; + frontend_high_speed_delete_all_sess(s_pool); + + //todo, clear queue +} + + + +/** + * @brief Default event callback function for frontend sessions + * + * This is the default callback implementation for handling frontend session events, + * which is invoked by the session framework when specific events (defined in + * FrontendSessionEvent enumeration) occur during the lifecycle of a FrontendSession. + * It provides basic event processing logic as a fallback when no custom callback is specified. + * + * @param[in] sess Pointer to the FrontendSession instance that triggered the event; + * must be a valid non-NULL pointer pointing to an active session + * @param[in] event The specific event type that occurred, one of the values in + * FrontendSessionEvent enumeration (FRONTEND_SESS_EVENT_CONN, + * FRONTEND_SESS_EVENT_RECVDATA, FRONTEND_SESS_EVENT_TIMEOUT, FRONTEND_SESS_EVENT_CLOSE) + * + * @note 1. This callback is called automatically by the session management framework, + * and should not be invoked manually by the user; + * 2. The default implementation typically includes basic operations such as + * logging, simple resource handling, or triggering default business flows + * (e.g., initiating connection setup on FRONTEND_SESS_EVENT_CONN, processing received + * data on FRONTEND_SESS_EVENT_RECVDATA, or preparing for session closure on FRONTEND_EVENT_SESS_CLOSE); + * 3. Users can replace this default callback with a custom implementation to + * achieve personalized event processing logic; + * 4. Ensure the 'sess' pointer is valid when the callback is triggered (the framework + * guarantees this under normal circumstances), invalid pointers may lead to undefined behavior; + * 5. The processing logic in this callback should be non-blocking to avoid blocking the + * session framework's event loop. + */ +void default_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event){ + if(FRONTEND_SESS_EVENT_CONN == event){ + + }else if(FRONTEND_SESS_EVENT_RECVDATA == event){ + + }else if(FRONTEND_SESS_EVENT_CLOSE == event){ + + }else if(FRONTEND_SESS_EVENT_ABNOMAL == event){ + + } + + return; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/shared_mem_io.c b/projects/sel4test/apps/front/src/shared_mem_io.c new file mode 100644 index 0000000..9ab1c89 --- /dev/null +++ b/projects/sel4test/apps/front/src/shared_mem_io.c @@ -0,0 +1,397 @@ +#include "shared_mem_io.h" +#include "frontend_proto.h" + + + +int init_shared_mem_pool(struct SharedMemoryPool *mem_pool){ + return FRONTEND_PROXY_PROCESS_OK; +} + +void free_shared_mem_pool(struct SharedMemoryPool *mem_pool){ + +} + + +uint64_t alloc_shared_mem(struct SharedMemoryPool *mem_pool){ + if(NULL == mem_pool) + return ERROR_SHARED_MEM_ADDR; + + return 0; +} + + +void free_shared_mem(struct SharedMemoryPool *mem_pool, uint64_t addr){ + +} + + + +int init_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + +void free_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool){ + +} + + + +int fetch_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +int release_shared_mem_pool_lock(struct SharedMemoryPoolLock *mem_pool){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + + +/** + * Create and initialize a shared memory pool queue in frontend side. + * + * @param config Pointer to the SharedMemoryPoolQueueConfig structure containing configuration + * parameters (e.g., underlying shared memory pool, lock settings) for queue creation. + * @return Pointer to the newly created SharedMemoryPoolQueue on success; NULL on failure + * (e.g., invalid config, insufficient memory for queue instance) + */ +struct SharedMemoryPoolQueue *shared_mem_pool_queue_create_frontend(const SharedMemoryPoolQueueConfig *config){ + struct SharedMemoryPoolQueue *queue; + int fd; + void *virt_addr; + + if(!IS_VALID_SHM_CONF_MAP_MODE(config)){ + error_print("shared_mem_pool_queue_create_frontend failed: invalid config (NULL pointer), or invalid map mode\n"); + return NULL; + } + + queue = malloc(sizeof(struct SharedMemoryPoolQueue)); + + if(NULL == queue){ + error_print("shared_mem_pool_queue_create_frontend failed: failed to allocate memory for SharedMemoryPoolDeque instance\n"); + return NULL; + } + +// queue->pool = pool; +// queue->length = 0; + + queue->map_mode1 = config->map_mode; + queue->pool = config->pool; + queue->block_size = config->block_size; + queue->capacity = config->capacity; + queue->virt_addr1 = config->virt_addr; + queue->phy_addr = config->phy_addr; + queue->header = 0; + queue->tail = 0; + +#if 0 + fd = open("/dev/mem", O_RDWR | O_SYNC); + + if(fd < 0){ + error_print("shared_mem_pool_queue_create_frontend failed: failed to open /dev/mem"); + free(queue); + return NULL; + } +#endif + // Calculate page-aligned physical address (mmap requires the offset to be a multiple of the page size) + off_t phys_page_off = queue->phy_addr & ~(sysconf(_SC_PAGESIZE) - 1); + size_t page_offset = queue->phy_addr - phys_page_off; + + utils_print("phys_page_off = %d, page_offset = %d\n", phys_page_off, page_offset); + + // Map physical memory to user space +#if 0 + virt_addr = mmap( + NULL, // Let the kernel automatically allocate virtual address + queue->capacity * queue->block_size + page_offset, // Mapping size (including intra-page offset) +// queue->block_size + page_offset, // Mapping size (including intra-page offset) +// PROT_READ | PROT_WRITE, // Read and write permissions + PROT_READ, // Read and write permissions +// MAP_SHARED, // Shared mapping + MAP_SHARED , // Shared mapping + fd, // File descriptor for /dev/mem + phys_page_off // Page-aligned physical address + ); + + utils_print("errno = %d, reasion is %s\n", errno, strerror(errno)); + + if (virt_addr == MAP_FAILED) { + error_print("shared_mem_pool_queue_create_frontend failed: mmap failed"); + close(fd); + free(queue); + return NULL; + } +#endif + + virt_addr = malloc(config->block_size * (config->capacity + 1)); + + if(NULL == virt_addr){ + error_print("shared_mem_pool_queue_create_frontend failed: failed to allocate memory for the queue!"); + close(fd); + free(queue); + return NULL; + } + + queue->virt_addr1 = virt_addr; + + utils_print("queue->virt_addr1 = %lld\n", queue->virt_addr1); + + return queue; +} + + + + +/** + * @brief Initializes a SharedMemoryPoolQueue instance with specified configuration + * + * This function initializes all members of a SharedMemoryPoolQueue structure using parameters + * provided in the configuration. It validates input, copies base parameters, calculates derived + * properties (like max element count), and sets initial queue state (empty state with zero length). + * + * @param queue Pointer to the SharedMemoryPoolQueue instance to be initialized + * @param config Pointer to a SharedMemoryPoolQueueConfig structure containing initialization parameters + * + * @return int Returns FRONTEND_PROXY_PROCESS_OK if initialization succeeds; + * Returns FRONTEND_PROXY_PROCESS_ERROR if input parameters are invalid (NULL pointers) + * + * @note Derived parameters (capacity, surplus) are calculated based on + * capacity and block_size to ensure consistent queue state. Invalid inputs + * prevent any modification to the queue instance. + */ +int shared_mem_pool_queue_initialize(struct SharedMemoryPoolQueue *queue, const SharedMemoryPoolQueueConfig *config){ + // Validate input parameters to prevent null pointer dereference + if (NULL == queue || NULL == config || NULL == config->pool) { + error_print("shared_mem_pool_queue_initialize failed: input parameter(s) is/are NULL!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Validate block size is positive to avoid division by zero in capacity calculation + if(0 == config->block_size){ + error_print("shared_mem_pool_queue_initialize failed: block size should be a positive number!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Initialize base members from configuration parameters + queue->pool = config->pool; + queue->phy_addr = config->phy_addr; + queue->virt_addr1 = config->virt_addr; + queue->capacity = config->capacity; + queue->block_size = config->block_size; + + // Calculate maximum number of items (avoid division by zero) + queue->capacity = config->block_size; + + // Initialize queue to empty state + queue->header = 0; // Start with head at initial position + queue->tail = 0; // Start with tail at initial position +// queue->length = 0; // No elements in empty queue +// queue->surplus = config->capacity; // All slots available initially + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * @brief Perform address mapping for physical memory pages allocated to the shared memory pool queue + * + * This function establishes address mapping relationships for the physical memory pages allocated to the shared memory pool queue, + * adapting to the address space differences between the frontend (microkernel-OS) and backend (Linux): + * - Frontend (microkernel-OS): Updates the corresponding address mapping table (table2) or virtual address (virt_addr2) + * based on the queue's map_mode2 configuration. + * - Backend (Linux): Updates the corresponding address mapping table (table1) or virtual address (virt_addr1) + * based on the queue's map_mode1 configuration. + * After successful mapping, both the frontend and backend can access the queue's shared physical memory through their respective + * virtual addresses or mapping tables, ensuring memory accessibility for FIFO data transmission. + * + * @param[in,out] queue Pointer to the shared memory pool queue instance for which physical pages need to be mapped. + * Must contain valid configuration information such as mapping modes (map_mode1/map_mode2) and + * address mapping tables (table1/table2). The mapping results will be updated to the virtual address + * fields (virt_addr1/virt_addr2) or address mapping tables of this structure. + * @param[in] page_num Total number of physical pages to be mapped (unsigned 32-bit integer). Must be greater than 0 and + * consistent with the length of the page_phy array, serving as the count basis for the mapping operation. + * @param[in] page_phy Array storing the physical addresses of the pages to be mapped. Each element corresponds to the + * starting physical address of a physical page. The length of the array must be equal to page_num, + * and the addresses must be valid physical addresses allocated from the shared memory pool. + * + * @return FRONTEND_PROXY_PROCESS_OK Mapping succeeded. All specified physical pages have completed front-end and back-end + * address association, and the relevant address fields of the queue have been updated. + * @return FRONTEND_PROXY_PROCESS_ERROR Mapping failed. Possible reasons include: + * 1. queue is a null pointer or page_num == 0; + * 2. page_phy is a null pointer or contains invalid physical addresses; + * 3. Invalid configuration of mapping modes (map_mode1/map_mode2); + * 4. Failure to execute address space mapping operations on the front-end or back-end; + * 5. Insufficient storage space in the address mapping tables (table1/table2). + * + * @note 1. The frontend runs on microkernel-OS and the backend runs on Linux; the mapping operation must adapt to the memory + * management mechanisms of both sides respectively. + * 2. The mapping logic depends on the queue's map_mode1 (Linux side) and map_mode2 (microkernel side) configurations, + * which must be initialized in advance. + * 3. Physical page addresses must be allocated from the shared memory pool associated with the queue (pool field) to + * ensure address validity and shared accessibility. + * 4. If the mapping mode is SHARE_MEM_MAP_MODE_CONTIGUOUS_BOTH, virt_addr1/virt_addr2 will be updated; + * if it is SHARE_MEM_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL, the table1/table2 mapping tables will be populated. + */ +int shared_mem_pool_queue_frontend_setup_pages(struct SharedMemoryPoolQueue *queue, uint32_t page_num, uint64_t page_phy[]){ + int page_cnt = 0; +/* + * Validate core parameter validity: queue/physical address array is non-null, page count is in valid range (1~MAX_MAP_TABLE_ENTRY_COUNT) + */ + if(NULL == queue || NULL == page_phy || 0 == page_num || page_num > MAX_MAP_TABLE_ENTRY_COUNT){ + error_print("shared_mem_pool_queue_frontend_setup_pages failed: invalid input parameters - queue/page_phy NULL or invalid page_num!\n"); + } + + + + return FRONTEND_PROXY_PROCESS_OK; +} + + + +/** + * Destroy a shared memory pool queue and release associated resources. + * + * @param queue Pointer to the SharedMemoryPoolDeque to destroy. Passing NULL is safe (no operation). + * @return FRONTEND_PROXY_PROCESS_OK on success; + * FRONTEND_PROXY_PROCESS_ERROR on failure. + */ +int shared_mem_pool_queue_destroy(struct SharedMemoryPoolQueue* queue){ + return FRONTEND_PROXY_PROCESS_OK; +} + + + + +/** + * @brief Sends a variable-size block (up to block_size) to the SharedMemoryPoolQueue: One Copy + * + * Stores a variable-length data block (with size ≤ block_size) into the queue using one-copy semantics. + * The data is transferred to a shared memory slot through a single copy operation. Regardless of the + * actual data size, the entire block_size space in shared memory is occupied to maintain fixed-size + * slot management. Explicitly handles full queue cases. + * + * @param queue Pointer to the SharedMemoryPoolQueue instance + * @param data Input pointer to the variable-length data block to be sent. Must point to valid memory + * containing the data (not a pointer to a pointer). + * @param data_size Size of the input data block (must be > 0 and ≤ queue->block_size for valid operation) + * + * @return int Returns: + * - FRONTEND_PROXY_PROCESS_ERROR: Success, data was copied to shared memory and queue state updated + * - FRONTEND_PROXY_PROCESS_AGAIN: Queue is full (next tail position equals header) + * - FRONTEND_PROXY_PROCESS_OK: Invalid input parameters (NULL pointers for queue or data), + * invalid block_size (0 bytes), data_size = 0, or data_size > queue->block_size + * + * @note The input data block (pointed to by data) must remain valid until the copy operation completes. + * One-copy semantics involve a single data transfer (e.g., via memcpy) from the input buffer to the + * target shared memory slot. + * Critical behavior: Even if data_size < block_size, the entire block_size space in shared memory is + * reserved and counted as occupied (surplus decreases by block_size, not data_size). + * Queue state (tail, length, surplus) is updated via the SHMP_QUEUE_ENQUEUE macro after the copy completes. + * Address calculation: Target slot address is derived from queue->virt_addr + (tail * block_size). + */ +int shared_mem_pool_queue_send_oc(struct SharedMemoryPoolQueue *queue, + const void *data, + size_t data_size) +{ + uint8_t *base_addr; + int ret; + + // Validate input parameters + if (NULL == queue || NULL == data) { + error_print("shared_mem_pool_queue_send_oc failed: invalid input parameters (NULL pointers)!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Validate block size configuration + if (queue->block_size == 0) { + error_print("shared_mem_pool_queue_send_oc failed: invalid block size (0 bytes)!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Validate input data size range + if (data_size == 0 || data_size > queue->block_size) { + error_print("shared_mem_pool_queue_send_oc failed: invalid data size!"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + /* + * Check if queue is full (next header position equals tail) + * Queue uses header for writing and tail for reading: (header+1) % max == tail means full + */ + if ((queue->header + 1) % queue->capacity == queue->tail) { + error_print("shared_mem_pool_queue_send_oc: queue is full!"); + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + // Calculate target address in shared memory (write to header position) + base_addr = (uint8_t *)queue->virt_addr1; + + // Perform one-copy operation to shared memory slot + memcpy(base_addr + queue->header * queue->block_size, data, data_size); + + // Update queue state via macro (moves header forward and adjusts length/surplus) + SHMP_QUEUE_ENQUEUE(queue, ret); + + return ret; +} + + + +/** + * @brief Receives a fixed-size block from the SharedMemoryPoolQueue: Zero Copy + * + * Retrieves one fixed-size memory block (of size block_size) from the front of the queue using zero-copy semantics. + * The data is directly accessed from the shared memory slot without copying by returning a pointer to the memory + * location in the shared pool. Explicitly handles empty queue cases. + * + * @param queue Pointer to the SharedMemoryPoolQueue instance + * @param buffer Output parameter to store the pointer to the received block. Points directly to shared memory. + * @param out_data_size Output parameter to store the actual size of the received block (always equal to queue->block_size) + * + * @return int Returns: + * - FRONTEND_PROXY_PROCESS_OK: Success, a block was retrieved and queue state updated + * - FRONTEND_PROXY_PROCESS_AGAIN: Queue is empty (header == tail) + * - FRONTEND_PROXY_PROCESS_ERROR: Invalid input parameters (NULL pointers) or invalid block size (0 bytes) + * + * @note The returned buffer MUST NOT be freed, as it points to shared memory. The buffer's validity is managed by + * queue operations. out_data_size is always set to queue->block_size for successful operations. + * Queue state (header, length, surplus) is updated via the SHMP_QUEUE_DEQUEUE macro after retrieving the block. + * Address calculation: Block address is derived from queue->virt_addr + (tail * block_size). + */ +int shared_mem_pool_queue_recv_zc(struct SharedMemoryPoolQueue *queue, + void **buffer, + size_t *out_data_size) { + uint8_t *base_addr; + int ret; + // Validate input parameters + if (NULL == queue || NULL == buffer || NULL == out_data_size) { + error_print("shared_mem_pool_queue_recv_zc failed: invalid input parameters (NULL pointers)"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Validate block size (should never happen if init was successful) + if (queue->block_size == 0) { + error_print("shared_mem_pool_queue_recv_zc failed: invalid block size (0 bytes)"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Check if queue is empty (header == tail and length == 0) + if (queue->header == queue->tail) { + error_print("shared_mem_pool_queue_recv_zc: queue is empty"); + return FRONTEND_PROXY_PROCESS_AGAIN; + } + + base_addr = (uint8_t *)queue->virt_addr1; + *buffer = base_addr + (queue->tail + 1) * queue->block_size; + *out_data_size = queue->block_size; + + utils_print("In %s, message address is %p\n", __func__, *buffer); + + SHMP_QUEUE_DEQUEUE(queue, ret); + + return ret; +} -- Gitee From 2e93deebf63dfd3809806c6fe61f723de999df1e Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:35:35 +0800 Subject: [PATCH 046/133] upload source files --- .../sel4test/apps/front/unittest/queue_test.c | 395 +++++++++++++++ .../apps/front/unittest/uthash_test.c | 455 ++++++++++++++++++ 2 files changed, 850 insertions(+) create mode 100644 projects/sel4test/apps/front/unittest/queue_test.c create mode 100644 projects/sel4test/apps/front/unittest/uthash_test.c diff --git a/projects/sel4test/apps/front/unittest/queue_test.c b/projects/sel4test/apps/front/unittest/queue_test.c new file mode 100644 index 0000000..f1f743f --- /dev/null +++ b/projects/sel4test/apps/front/unittest/queue_test.c @@ -0,0 +1,395 @@ +#include +#include +#include +#include "queue.h" + +typedef struct test_node { + int id; + char name[32]; + TAILQ_ENTRY(test_node) entries; +} test_node_t; + +// 定义队列头 +TAILQ_HEAD(test_queue, test_node); +typedef struct test_queue test_queue_t; + +// 打印队列内容的函数 +void print_queue(test_queue_t *head, const char *description) { + test_node_t *np; + printf("%s: [", description); + int first = 1; + TAILQ_FOREACH(np, head, entries) { + if (!first) printf(", "); + printf("(%d:%s)", np->id, np->name); + first = 0; + } + printf("]\n"); +} + +// 释放队列中所有节点 +void clear_queue(test_queue_t *head) { + test_node_t *np; + while (!TAILQ_EMPTY(head)) { + np = TAILQ_FIRST(head); + TAILQ_REMOVE(head, np, entries); + free(np); + } +} + +int test_tailq_insert_head() { + test_queue_t head; + test_node_t *node1, *node2; + + printf("\n=== Testing TAILQ_INSERT_HEAD ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + // 插入第一个节点 + TAILQ_INSERT_HEAD(&head, node1, entries); + print_queue(&head, "After inserting first node"); + + // 插入第二个节点到头部 + TAILQ_INSERT_HEAD(&head, node2, entries); + print_queue(&head, "After inserting second node at head"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_insert_tail() { + test_queue_t head; + test_node_t *node1, *node2, *node3; + + printf("\n=== Testing TAILQ_INSERT_TAIL ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 插入节点到尾部 + TAILQ_INSERT_TAIL(&head, node1, entries); + print_queue(&head, "After inserting first node at tail"); + + TAILQ_INSERT_TAIL(&head, node2, entries); + print_queue(&head, "After inserting second node at tail"); + + TAILQ_INSERT_TAIL(&head, node3, entries); + print_queue(&head, "After inserting third node at tail"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_insert_after() { + test_queue_t head; + test_node_t *node1, *node2, *node3; + + printf("\n=== Testing TAILQ_INSERT_AFTER ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 先插入前两个节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + print_queue(&head, "After inserting first two nodes"); + + // 在第一个节点后插入第三个节点 + TAILQ_INSERT_AFTER(&head, node1, node3, entries); + print_queue(&head, "After inserting third node after first"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_remove() { + test_queue_t head; + test_node_t *node1, *node2, *node3, *node4; + + printf("\n=== Testing TAILQ_REMOVE ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + node4 = malloc(sizeof(test_node_t)); + node4->id = 4; + strcpy(node4->name, "Fourth"); + + // 插入所有节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + TAILQ_INSERT_TAIL(&head, node3, entries); + TAILQ_INSERT_TAIL(&head, node4, entries); + print_queue(&head, "After inserting all nodes"); + + // 删除中间节点 + TAILQ_REMOVE(&head, node2, entries); + print_queue(&head, "After removing second node"); + free(node2); + + // 删除头部节点 + TAILQ_REMOVE(&head, node1, entries); + print_queue(&head, "After removing first node"); + free(node1); + + // 删除尾部节点 + TAILQ_REMOVE(&head, node4, entries); + print_queue(&head, "After removing last node"); + free(node4); + + // 清理剩余节点 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_FOREACH() { + test_queue_t head; + test_node_t *node1, *node2, *node3, *np; + int count; + + printf("\n=== Testing TAILQ_FOREACH ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 插入节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + TAILQ_INSERT_TAIL(&head, node3, entries); + print_queue(&head, "After inserting nodes"); + + // 遍历并计数 + count = 0; + TAILQ_FOREACH(np, &head, entries) { + count++; + } + printf("Counted %d nodes using TAILQ_FOREACH\n", count); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_FOREACH_REVERSE() { + test_queue_t head; + test_node_t *node1, *node2, *node3, *np; + int count; + + printf("\n=== Testing TAILQ_FOREACH_REVERSE ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 插入节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + TAILQ_INSERT_TAIL(&head, node3, entries); + print_queue(&head, "After inserting nodes"); + + // 反向遍历并打印 + printf("Reverse traversal: ["); + count = 0; + TAILQ_FOREACH_REVERSE(np, &head, test_queue, entries) { + if (count > 0) printf(", "); + printf("(%d:%s)", np->id, np->name); + count++; + } + printf("]\n"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_empty_and_first() { + test_queue_t head; + test_node_t *node, *first; + + printf("\n=== Testing TAILQ_EMPTY and TAILQ_FIRST ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 检查空队列 + if (TAILQ_EMPTY(&head)) { + printf("Queue is empty as expected\n"); + } + + first = TAILQ_FIRST(&head); + if (first == NULL) { + printf("TAILQ_FIRST returned NULL for empty queue as expected\n"); + } + + // 添加一个节点 + node = malloc(sizeof(test_node_t)); + node->id = 1; + strcpy(node->name, "Only"); + TAILQ_INSERT_TAIL(&head, node, entries); + print_queue(&head, "After inserting one node"); + + // 检查非空队列 + if (!TAILQ_EMPTY(&head)) { + printf("Queue is not empty as expected\n"); + } + + first = TAILQ_FIRST(&head); + if (first != NULL) { + printf("TAILQ_FIRST returned node (%d:%s)\n", first->id, first->name); + } + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +int test_tailq_next() { + test_queue_t head; + test_node_t *node1, *node2, *node3, *np; + + printf("\n=== Testing TAILQ_NEXT ===\n"); + TAILQ_INIT(&head); + + print_queue(&head, "Initial queue"); + + // 创建测试节点 + node1 = malloc(sizeof(test_node_t)); + node1->id = 1; + strcpy(node1->name, "First"); + + node2 = malloc(sizeof(test_node_t)); + node2->id = 2; + strcpy(node2->name, "Second"); + + node3 = malloc(sizeof(test_node_t)); + node3->id = 3; + strcpy(node3->name, "Third"); + + // 插入节点 + TAILQ_INSERT_TAIL(&head, node1, entries); + TAILQ_INSERT_TAIL(&head, node2, entries); + TAILQ_INSERT_TAIL(&head, node3, entries); + print_queue(&head, "After inserting nodes"); + + // 使用TAILQ_NEXT遍历 + printf("Traversal using TAILQ_NEXT: ["); + int count = 0; + for (np = TAILQ_FIRST(&head); np != NULL; np = TAILQ_NEXT(np, entries)) { + if (count > 0) printf(", "); + printf("(%d:%s)", np->id, np->name); + count++; + } + printf("]\n"); + + // 清理 + clear_queue(&head); + print_queue(&head, "After clearing"); + + return 0; +} + +void tailq_test(){ + printf("Testing TAILQ operations\n"); + + test_tailq_insert_head(); + test_tailq_insert_tail(); + test_tailq_insert_after(); + test_tailq_remove(); + test_tailq_FOREACH(); + test_tailq_FOREACH_REVERSE(); + test_tailq_empty_and_first(); + test_tailq_next(); + + printf("\nAll TAILQ tests completed!\n"); +} + \ No newline at end of file diff --git a/projects/sel4test/apps/front/unittest/uthash_test.c b/projects/sel4test/apps/front/unittest/uthash_test.c new file mode 100644 index 0000000..937c5bb --- /dev/null +++ b/projects/sel4test/apps/front/unittest/uthash_test.c @@ -0,0 +1,455 @@ +#include +#include +#include +#include "uthash.h" + +// 定义测试结构体 +typedef struct test_item { + int id; + char name[32]; + UT_hash_handle hh; +} test_item_t; + +typedef struct string_item { + char key[32]; + int value; + UT_hash_handle hh; +} string_item_t; + +typedef struct ptr_item { + void *ptr_key; + int data; + UT_hash_handle hh; +} ptr_item_t; + +// 打印哈希表内容的函数 +void print_hash_int(test_item_t *head, const char *description) { + test_item_t *current; + printf("%s: [", description); + int first = 1; + for (current = head; current != NULL; current = current->hh.next) { + if (!first) printf(", "); + printf("(%d:%s)", current->id, current->name); + first = 0; + } + printf("]\n"); +} + +void print_hash_str(string_item_t *head, const char *description) { + string_item_t *current; + printf("%s: [", description); + int first = 1; + for (current = head; current != NULL; current = current->hh.next) { + if (!first) printf(", "); + printf("(%s:%d)", current->key, current->value); + first = 0; + } + printf("]\n"); +} + +void print_hash_ptr(ptr_item_t *head, const char *description) { + ptr_item_t *current; + printf("%s: [", description); + int first = 1; + for (current = head; current != NULL; current = current->hh.next) { + if (!first) printf(", "); + printf("(%p:%d)", current->ptr_key, current->data); + first = 0; + } + printf("]\n"); +} + +// 释放哈希表中所有节点 +void clear_hash_int(test_item_t **head) { + test_item_t *current, *tmp; + HASH_ITER(hh, *head, current, tmp) { + HASH_DEL(*head, current); + free(current); + } +} + +void clear_hash_str(string_item_t **head) { + string_item_t *current, *tmp; + HASH_ITER(hh, *head, current, tmp) { + HASH_DEL(*head, current); + free(current); + } +} + +void clear_hash_ptr(ptr_item_t **head) { + ptr_item_t *current, *tmp; + HASH_ITER(hh, *head, current, tmp) { + HASH_DEL(*head, current); + free(current); + } +} + +int test_hash_add_find_int() { + test_item_t *users = NULL; + test_item_t *item1, *item2, *item3, *found; + + printf("\n=== Testing HASH_ADD_INT and HASH_FIND_INT ===\n"); + print_hash_int(users, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + item2 = malloc(sizeof(test_item_t)); + item2->id = 2; + strcpy(item2->name, "Bob"); + + item3 = malloc(sizeof(test_item_t)); + item3->id = 3; + strcpy(item3->name, "Charlie"); + + // 添加节点 + HASH_ADD_INT(users, id, item1); + print_hash_int(users, "After adding first item"); + + HASH_ADD_INT(users, id, item2); + print_hash_int(users, "After adding second item"); + + HASH_ADD_INT(users, id, item3); + print_hash_int(users, "After adding third item"); + + // 查找节点 + int search_id = 2; + HASH_FIND_INT(users, &search_id, found); + if (found) { + printf("Found item with id %d: %s\n", found->id, found->name); + } + + // 查找不存在的节点 + search_id = 5; + HASH_FIND_INT(users, &search_id, found); + if (!found) { + printf("Item with id %d not found as expected\n", search_id); + } + + // 清理 + clear_hash_int(&users); + print_hash_int(users, "After clearing"); + + return 0; +} + +int test_hash_add_find_str() { + string_item_t *items = NULL; + string_item_t *item1, *item2, *item3, *found; + + printf("\n=== Testing HASH_ADD_STR and HASH_FIND_STR ===\n"); + print_hash_str(items, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(string_item_t)); + strcpy(item1->key, "apple"); + item1->value = 10; + + item2 = malloc(sizeof(string_item_t)); + strcpy(item2->key, "banana"); + item2->value = 20; + + item3 = malloc(sizeof(string_item_t)); + strcpy(item3->key, "cherry"); + item3->value = 30; + + // 添加节点 + HASH_ADD_STR(items, key, item1); + print_hash_str(items, "After adding first item"); + + HASH_ADD_STR(items, key, item2); + print_hash_str(items, "After adding second item"); + + HASH_ADD_STR(items, key, item3); + print_hash_str(items, "After adding third item"); + + // 查找节点 + HASH_FIND_STR(items, "banana", found); + if (found) { + printf("Found item with key %s: %d\n", found->key, found->value); + } + + // 查找不存在的节点 + HASH_FIND_STR(items, "orange", found); + if (!found) { + printf("Item with key 'orange' not found as expected\n"); + } + + // 清理 + clear_hash_str(&items); + print_hash_str(items, "After clearing"); + + return 0; +} + +int test_hash_add_find_ptr() { + ptr_item_t *items = NULL; + ptr_item_t *item1, *item2, *item3, *found; + int dummy1, dummy2, dummy3; + void *search_ptr; + + printf("\n=== Testing HASH_ADD_PTR and HASH_FIND_PTR ===\n"); + print_hash_ptr(items, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(ptr_item_t)); + item1->ptr_key = &dummy1; + item1->data = 100; + + item2 = malloc(sizeof(ptr_item_t)); + item2->ptr_key = &dummy2; + item2->data = 200; + + item3 = malloc(sizeof(ptr_item_t)); + item3->ptr_key = &dummy3; + item3->data = 300; + + // 添加节点 + HASH_ADD_PTR(items, ptr_key, item1); + print_hash_ptr(items, "After adding first item"); + + HASH_ADD_PTR(items, ptr_key, item2); + print_hash_ptr(items, "After adding second item"); + + HASH_ADD_PTR(items, ptr_key, item3); + print_hash_ptr(items, "After adding third item"); + + // 查找节点 + search_ptr = &dummy2; + HASH_FIND_PTR(items, &search_ptr, found); + if (found) { + printf("Found item with ptr_key %p: %d\n", found->ptr_key, found->data); + } + + // 查找不存在的节点 + search_ptr = NULL; + HASH_FIND_PTR(items, &search_ptr, found); + if (!found) { + printf("Item with ptr_key NULL not found as expected\n"); + } + + // 清理 + clear_hash_ptr(&items); + print_hash_ptr(items, "After clearing"); + + return 0; +} + +int test_hash_delete() { + test_item_t *users = NULL; + test_item_t *item1, *item2, *item3, *found; + int search_id; + + printf("\n=== Testing HASH_DEL ===\n"); + print_hash_int(users, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + item2 = malloc(sizeof(test_item_t)); + item2->id = 2; + strcpy(item2->name, "Bob"); + + item3 = malloc(sizeof(test_item_t)); + item3->id = 3; + strcpy(item3->name, "Charlie"); + + // 添加所有节点 + HASH_ADD_INT(users, id, item1); + HASH_ADD_INT(users, id, item2); + HASH_ADD_INT(users, id, item3); + print_hash_int(users, "After adding all items"); + + // 删除中间节点 + search_id = 2; + HASH_FIND_INT(users, &search_id, found); + if (found) { + HASH_DEL(users, found); + free(found); + printf("Deleted item with id %d\n", search_id); + } + print_hash_int(users, "After deleting middle item"); + + // 删除第一个节点 + search_id = 1; + HASH_FIND_INT(users, &search_id, found); + if (found) { + HASH_DEL(users, found); + free(found); + printf("Deleted item with id %d\n", search_id); + } + print_hash_int(users, "After deleting first item"); + + // 清理剩余节点 + clear_hash_int(&users); + print_hash_int(users, "After clearing"); + + return 0; +} + +int test_hash_count() { + test_item_t *users = NULL; + test_item_t *item1, *item2, *item3; + int count; + + printf("\n=== Testing HASH_COUNT ===\n"); + print_hash_int(users, "Initial hash"); + + count = HASH_COUNT(users); + printf("Initial count: %d\n", count); + + // 创建测试节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + item2 = malloc(sizeof(test_item_t)); + item2->id = 2; + strcpy(item2->name, "Bob"); + + item3 = malloc(sizeof(test_item_t)); + item3->id = 3; + strcpy(item3->name, "Charlie"); + + // 添加节点并检查计数 + HASH_ADD_INT(users, id, item1); + count = HASH_COUNT(users); + printf("Count after adding first item: %d\n", count); + print_hash_int(users, "Hash after adding first item"); + + HASH_ADD_INT(users, id, item2); + count = HASH_COUNT(users); + printf("Count after adding second item: %d\n", count); + print_hash_int(users, "Hash after adding second item"); + + HASH_ADD_INT(users, id, item3); + count = HASH_COUNT(users); + printf("Count after adding third item: %d\n", count); + print_hash_int(users, "Hash after adding third item"); + + // 删除节点并检查计数 + HASH_DEL(users, item2); + free(item2); + count = HASH_COUNT(users); + printf("Count after deleting one item: %d\n", count); + print_hash_int(users, "Hash after deleting one item"); + + // 清理 + clear_hash_int(&users); + count = HASH_COUNT(users); + printf("Count after clearing: %d\n", count); + print_hash_int(users, "After clearing"); + + return 0; +} + +int test_hash_iter() { + test_item_t *users = NULL, *current, *tmp; + test_item_t *item1, *item2, *item3; + int count; + + printf("\n=== Testing HASH_ITER ===\n"); + print_hash_int(users, "Initial hash"); + + // 创建测试节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + item2 = malloc(sizeof(test_item_t)); + item2->id = 2; + strcpy(item2->name, "Bob"); + + item3 = malloc(sizeof(test_item_t)); + item3->id = 3; + strcpy(item3->name, "Charlie"); + + // 添加节点 + HASH_ADD_INT(users, id, item1); + HASH_ADD_INT(users, id, item2); + HASH_ADD_INT(users, id, item3); + print_hash_int(users, "After adding items"); + + // 使用HASH_ITER遍历并计数 + count = 0; + HASH_ITER(hh, users, current, tmp) { + count++; + printf("Visited item: (%d:%s)\n", current->id, current->name); + } + printf("Total items visited: %d\n", count); + + // 清理 + clear_hash_int(&users); + print_hash_int(users, "After clearing"); + + return 0; +} + + +int test_hash_replace() { + test_item_t *users = NULL; + test_item_t *item1, *item2, *replaced; + int search_id; + + printf("\n=== Testing HASH_REPLACE ===\n"); + print_hash_int(users, "Initial hash"); + + // 创建初始节点 + item1 = malloc(sizeof(test_item_t)); + item1->id = 1; + strcpy(item1->name, "Alice"); + + // 添加初始节点 + HASH_ADD_INT(users, id, item1); + print_hash_int(users, "After adding initial item"); + + // 创建替换节点 + item2 = malloc(sizeof(test_item_t)); + item2->id = 1; // 相同的ID + strcpy(item2->name, "Alicia"); // 不同的名字 + + // 替换节点 + HASH_REPLACE_INT(users, id, item2, replaced); + if (replaced) { + printf("Replaced item: (%d:%s)\n", replaced->id, replaced->name); + free(replaced); + } + print_hash_int(users, "After replacing item"); + + // 尝试替换不存在的节点 + test_item_t *item3 = malloc(sizeof(test_item_t)); + item3->id = 2; + strcpy(item3->name, "Bob"); + + HASH_REPLACE_INT(users, id, item3, replaced); + if (replaced) { + printf("Replaced item: (%d:%s)\n", replaced->id, replaced->name); + free(replaced); + } else { + printf("No item replaced, added new item instead\n"); + } + print_hash_int(users, "After attempting to replace non-existent item"); + + // 清理 + clear_hash_int(&users); + print_hash_int(users, "After clearing"); + + return 0; +} + +void uthash_test() { + printf("Testing UTHASH operations\n"); + + test_hash_add_find_int(); + test_hash_add_find_str(); + test_hash_add_find_ptr(); + test_hash_delete(); + test_hash_count(); + test_hash_iter(); + test_hash_replace(); + printf("\nAll UTHASH tests completed!\n"); +} \ No newline at end of file -- Gitee From 2f6caa6d14a555d2200727b10dd294ac74d64db5 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 10:36:39 +0800 Subject: [PATCH 047/133] upload source files --- projects/sel4test/apps/front/CMakeLists.txt | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 projects/sel4test/apps/front/CMakeLists.txt diff --git a/projects/sel4test/apps/front/CMakeLists.txt b/projects/sel4test/apps/front/CMakeLists.txt new file mode 100644 index 0000000..cee3b63 --- /dev/null +++ b/projects/sel4test/apps/front/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.16.0) + +project(front C) + +find_package(musllibc REQUIRED) +find_package(util_libs REQUIRED) +find_package(seL4_libs REQUIRED) + +# Setup build environment +musllibc_setup_build_environment_with_sel4runtime() +sel4_import_libsel4() +util_libs_import_libraries() +sel4_libs_import_libraries() + +# Build front application +file(GLOB + deps + src/main.c + src/common_utils.c + src/frontend_proto.c + src/session.c + src/session_pool.c + src/shared_mem_io.c + src/dev.c + src/engine.c + src/frontend_api.c + src/senario_test.c + unittest/*.c +) +# list(SORT deps) + +add_executable(front EXCLUDE_FROM_ALL ${deps}) + +# Add include directories for front headers (本地 include 目录) +target_include_directories(front PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +target_link_libraries(front + PUBLIC + sel4_autoconf + muslc + sel4 + sel4runtime + sel4utils + sel4platsupport + sel4muslcsys + sel4allocman + PRIVATE + utils +) + +# Set executable properties +target_compile_options(front PRIVATE -g -O2) +set_property(TARGET front PROPERTY C_STANDARD 99) + +# Set this image as the rootserver +include(rootserver) +DeclareRootserver(front) \ No newline at end of file -- Gitee From 46643da38c7fa7ad1a244887d440129b360c7c23 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Feb 2026 16:21:02 +0800 Subject: [PATCH 048/133] adding hyperamp shared memory queue codes --- projects/sel4test/CMakeLists.txt | 2 + projects/sel4test/apps/front/CMakeLists.txt | 1 + .../apps/front/include/hyperamp_shm_queue.h | 309 +++++++++++++++ .../apps/front/src/hyperamp_shm_queue.c | 368 ++++++++++++++++++ .../sel4test/apps/front/src/shared_mem_io.c | 2 +- 5 files changed, 681 insertions(+), 1 deletion(-) create mode 100644 projects/sel4test/apps/front/include/hyperamp_shm_queue.h create mode 100644 projects/sel4test/apps/front/src/hyperamp_shm_queue.c diff --git a/projects/sel4test/CMakeLists.txt b/projects/sel4test/CMakeLists.txt index 1f8fafe..5d52c60 100755 --- a/projects/sel4test/CMakeLists.txt +++ b/projects/sel4test/CMakeLists.txt @@ -36,6 +36,8 @@ if(Sel4testApp STREQUAL "hello-world") add_subdirectory(apps/hello-world) elseif(Sel4testApp STREQUAL "hyperamp-server") add_subdirectory(apps/hyperamp-server) +elseif(Sel4testApp STREQUAL "front") + add_subdirectory(apps/front) else() add_subdirectory(apps/sel4test-driver) endif() diff --git a/projects/sel4test/apps/front/CMakeLists.txt b/projects/sel4test/apps/front/CMakeLists.txt index cee3b63..ef69efe 100644 --- a/projects/sel4test/apps/front/CMakeLists.txt +++ b/projects/sel4test/apps/front/CMakeLists.txt @@ -21,6 +21,7 @@ file(GLOB src/session.c src/session_pool.c src/shared_mem_io.c + src/hyperamp_shm_queue.c src/dev.c src/engine.c src/frontend_api.c diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h new file mode 100644 index 0000000..5430bba --- /dev/null +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -0,0 +1,309 @@ +/** + * @file hyperamp_shm_queue.h + * @brief HyperAMP Shared Memory Queue – seL4 version + * + * This is a streamlined seL4 version, copied and modified from the hvisor-tool project. + */ + +#ifndef HYPERAMP_SHM_QUEUE_SEL4_H +#define HYPERAMP_SHM_QUEUE_SEL4_H + +#include +#include + +/* ==================== Constant Definitions ==================== */ + +#define HYPERAMP_ERROR_ADDR UINT64_MAX +#define HYPERAMP_MAX_MAP_TABLE_ENTRIES 125 /* Makes the queue control region exactly 4KB (1 page) */ + +#define HYPERAMP_OK 0 +#define HYPERAMP_ERROR (-1) + +/* Memory mapping modes */ +typedef enum { + HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH = 0, + HYPERAMP_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL +} HyperampMapMode; + +/* Message constants */ +#define HYPERAMP_MSG_HDR_SIZE 8 +#define HYPERAMP_MSG_MIN_SIZE 1 +#define HYPERAMP_MSG_MAX_SIZE 4088 +#define HYPERAMP_MSG_HDR_PLUS_MAX_SIZE (HYPERAMP_MSG_HDR_SIZE + HYPERAMP_MSG_MAX_SIZE) + +/* Queue magic number */ +#define HYPERAMP_QUEUE_MAGIC 0x48415150 // "HAQP" + +/* ==================== Memory Barriers and Cache Operations ==================== */ + +#if defined(__aarch64__) || defined(__arm__) + #define HYPERAMP_DMB() __asm__ volatile("dmb sy" ::: "memory") + #define HYPERAMP_DSB() __asm__ volatile("dsb sy" ::: "memory") + #define HYPERAMP_ISB() __asm__ volatile("isb" ::: "memory") + + /* Data cache clean – flush cache lines to main memory (used for shared memory writes) */ + static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { + volatile char *p = (volatile char *)addr; + volatile char *end = p + size; + /* ARM64 cache line size is typically 64 bytes */ + for (; p < end; p += 64) { + __asm__ volatile("dc cvac, %0" : : "r"(p) : "memory"); + } + __asm__ volatile("dsb sy" ::: "memory"); + } + + /* Data cache invalidate – discard cached contents and force reload from memory (used for shared memory reads) */ + static inline void hyperamp_cache_invalidate(volatile void *addr, size_t size) { + volatile char *p = (volatile char *)addr; + volatile char *end = p + size; + for (; p < end; p += 64) { + __asm__ volatile("dc civac, %0" : : "r"(p) : "memory"); + } + __asm__ volatile("dsb sy" ::: "memory"); + } +#else + #define HYPERAMP_DMB() __asm__ volatile("mfence" ::: "memory") + #define HYPERAMP_DSB() __asm__ volatile("mfence" ::: "memory") + #define HYPERAMP_ISB() __asm__ volatile("" ::: "memory") + + static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { + (void)addr; (void)size; + __asm__ volatile("mfence" ::: "memory"); + } + + static inline void hyperamp_cache_invalidate(volatile void *addr, size_t size) { + (void)addr; (void)size; + __asm__ volatile("mfence" ::: "memory"); + } +#endif + +#define HYPERAMP_BARRIER() do { HYPERAMP_DMB(); HYPERAMP_DSB(); } while(0) + +/* ==================== Software Spinlock ==================== */ + +typedef struct { + volatile uint32_t lock_value; + volatile uint32_t owner_zone_id; + volatile uint32_t lock_count; + volatile uint32_t contention_count; +} __attribute__((packed)) HyperampSpinlock; + +static inline void hyperamp_spinlock_init(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + + volatile uint8_t *p = (volatile uint8_t *)lock; + for (size_t i = 0; i < sizeof(HyperampSpinlock); i++) { + p[i] = 0; + } + HYPERAMP_BARRIER(); + + /* Flush the lock state to main memory */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +} + +static inline void hyperamp_spinlock_lock(volatile HyperampSpinlock *lock, uint32_t zone_id) +{ + if (!lock) return; + + int spin_count = 0; + const int max_spin = 100000; + + while (1) { + HYPERAMP_BARRIER(); + + volatile uint32_t current = lock->lock_value; + + if (current == 0) { + lock->lock_value = 1; + HYPERAMP_BARRIER(); + + volatile uint32_t verify = lock->lock_value; + if (verify == 1) { + lock->owner_zone_id = zone_id; + lock->lock_count++; + HYPERAMP_BARRIER(); + + /* Flush the updated lock state to main memory, + ensuring other cores/VMs observe that the lock is held */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); + return; + } + } + + lock->contention_count++; + spin_count++; + + if (spin_count > max_spin) { + spin_count = 0; +#if defined(__aarch64__) || defined(__arm__) + __asm__ volatile("yield" ::: "memory"); +#else + __asm__ volatile("pause" ::: "memory"); +#endif + } + + for (volatile int i = 0; i < 100; i++) { + HYPERAMP_BARRIER(); + } + } +} + +static inline void hyperamp_spinlock_unlock(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + + HYPERAMP_BARRIER(); + lock->owner_zone_id = 0; + lock->lock_value = 0; + HYPERAMP_BARRIER(); + + /* Critical: flush the unlocked state to main memory, + ensuring other cores/VMs observe that the lock is released */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +} + +/* ==================== Address Mapping Table Entry ==================== */ + +typedef struct { + uint64_t virt_addr; + uint64_t phy_addr; +} __attribute__((packed)) HyperampMapTableEntry; + +/* ==================== Shared Memory Pool Queue ==================== */ + +typedef struct { + uint8_t map_mode1; + uint8_t map_mode2; + uint16_t header; + uint16_t tail; + uint16_t capacity; + uint16_t block_size; + uint16_t _reserved; + + uint64_t phy_addr; + uint64_t virt_addr1; + uint64_t virt_addr2; + + HyperampMapTableEntry table1[HYPERAMP_MAX_MAP_TABLE_ENTRIES]; + HyperampMapTableEntry table2[HYPERAMP_MAX_MAP_TABLE_ENTRIES]; + + HyperampSpinlock queue_lock; + + uint32_t magic; + uint32_t version; + uint32_t enqueue_count; + uint32_t dequeue_count; + +} __attribute__((packed)) HyperampShmQueue; + +/* ==================== Message Header Structure ==================== */ + +typedef struct { + uint8_t version; + uint8_t proxy_msg_type; + uint16_t frontend_sess_id; + uint16_t backend_sess_id; + uint16_t payload_len; +} __attribute__((packed)) HyperampMsgHeader; + +/* Message types */ +typedef enum { + HYPERAMP_MSG_TYPE_DEV = 0, + HYPERAMP_MSG_TYPE_STRGY = 1, + HYPERAMP_MSG_TYPE_SESS = 2, + HYPERAMP_MSG_TYPE_DATA = 3, + HYPERAMP_MSG_TYPE_SERVICE = 0x10, + HYPERAMP_MSG_TYPE_BULK = 0x20 // Bulk data transfer (payload is a descriptor) +} HyperampMsgType; + +// Bulk Transfer configuration +#define BULK_BUFFER_OFFSET 0x100000 // Offset at 1MB +#define BULK_BUFFER_SIZE (2 * 1024 * 1024) // 2MB buffer size + +// Bulk Transfer descriptor (transmitted as payload) +typedef struct { + uint32_t offset; // Data offset + uint32_t length; // Data length + uint32_t service_id; // Service ID (1=Encrypt, 2=Decrypt) + int32_t status; // 0=Request, 1=Success, <0=Error +} HyperampBulkDescriptor; + +// ==================== Signature Verification ==================== + +// Service IDs +#define SERVICE_ECHO 0 +#define SERVICE_ENCRYPT 1 +#define SERVICE_DECRYPT 2 +#define SERVICE_VERIFY_ONLY 3 // Signature verification only +#define SERVICE_VERIFY_ENCRYPT 4 // Verify signature then encrypt +#define SERVICE_VERIFY_DECRYPT 5 // Verify signature then decrypt +#define SERVICE_VALIDATE_ENCRYPT 7 // Validate fields then encrypt (for object detection data) +#define SERVICE_VALIDATE_DECRYPT 8 // Validate fields then decrypt (for object detection data) + +// Field validation status codes +#define VALIDATE_OK 0 +#define VALIDATE_FAILED_MISSING -10 // Required field missing + +// Signature verification status codes +#define AUTH_OK 0 +#define AUTH_FAILED_BAD_MAGIC -1 +#define AUTH_FAILED_BAD_SIG -2 +#define AUTH_FAILED_BAD_LEN -3 + +// Signature header magic +#define SIG_MAGIC 0x53494731 // "SIG1" + +// Simplified signature header (for prototype verification) +typedef struct { + uint32_t magic; // Must be SIG_MAGIC (0x53494731) + uint16_t sig_len; // Signature length (ECDSA: 70–72 bytes) + uint16_t reserved; + uint32_t payload_len; // Original data length + uint8_t signature[72]; // ECDSA-P256 signature (max 72 bytes) +} __attribute__((packed)) HyperampSignedHeader; + + + +/* ==================== Queue Configuration Structure ==================== */ + +/** + * @brief Queue initialization configuration + */ +typedef struct { + uint16_t map_mode; // Memory mapping mode + uint16_t capacity; // Queue capacity + uint16_t block_size; // Block size + uint16_t _reserved; + uint64_t phy_addr; // Physical address + uint64_t virt_addr; // Virtual address +} HyperampQueueConfig; + + +int hyperamp_queue_is_initialized(volatile HyperampShmQueue *queue); + + + +int hyperamp_queue_init(volatile HyperampShmQueue *queue, + const HyperampQueueConfig *config, + int is_creator); + + +int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + const void *data, + size_t data_len, + volatile void *virt_base); + + +int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + void *data, + size_t max_len, + size_t *actual_len, + volatile void *virt_base); + + + + +#endif /* HYPERAMP_SHM_QUEUE_SEL4_H */ \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c new file mode 100644 index 0000000..892b801 --- /dev/null +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -0,0 +1,368 @@ +#include "hyperamp_shm_queue.h" + +// Trusted public key (user-generated ECDSA-P256 public key in DER format) +static const unsigned char TRUSTED_PUBKEY_DER[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0x1b, 0x2e, 0xcc, 0x7e, 0x35, 0xd0, 0xbe, 0xda, 0x02, + 0xce, 0x77, 0x25, 0xf0, 0xbf, 0xa6, 0x87, 0x4f, 0x40, 0xa4, 0xe4, 0xff, + 0xaf, 0xee, 0xb4, 0x5d, 0x12, 0xee, 0x5c, 0x4a, 0x87, 0x07, 0xfe, 0x07, + 0xbf, 0x40, 0xce, 0xb0, 0xb4, 0xa7, 0xcc, 0x7d, 0x7a, 0x85, 0xaf, 0xd3, + 0x23, 0x8d, 0x16, 0xf1, 0x8c, 0x1a, 0x89, 0xca, 0x0c, 0x79, 0x07, 0x43, + 0xca, 0xb9, 0x28, 0xf1, 0xfb, 0xfb, 0x43 +}; +static const unsigned int TRUSTED_PUBKEY_DER_LEN = 91; + + + +/* ==================== Secure Memory Operations ==================== */ + +static inline void hyperamp_safe_memset(volatile void *dst, uint8_t val, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)dst; + for (size_t i = 0; i < len; i++) { + p[i] = val; + } + HYPERAMP_BARRIER(); +} + +static inline void hyperamp_safe_memcpy(volatile void *dst, const volatile void *src, size_t len) +{ + volatile uint8_t *d = (volatile uint8_t *)dst; + const volatile uint8_t *s = (const volatile uint8_t *)src; + for (size_t i = 0; i < len; i++) { + d[i] = s[i]; + } + HYPERAMP_BARRIER(); +} + +static inline uint16_t hyperamp_safe_read_u16(const volatile void *addr, size_t offset) +{ + const volatile uint8_t *p = (const volatile uint8_t *)addr; + uint16_t val = 0; + for (int i = 0; i < 2; i++) { + val |= ((uint16_t)p[offset + i]) << (i * 8); + } + HYPERAMP_BARRIER(); + return val; +} + +static inline uint32_t hyperamp_safe_read_u32(const volatile void *addr, size_t offset) +{ + const volatile uint8_t *p = (const volatile uint8_t *)addr; + uint32_t val = 0; + for (int i = 0; i < 4; i++) { + val |= ((uint32_t)p[offset + i]) << (i * 8); + } + HYPERAMP_BARRIER(); + return val; +} + +static inline uint64_t hyperamp_safe_read_u64(const volatile void *addr, size_t offset) +{ + const volatile uint8_t *p = (const volatile uint8_t *)addr; + uint64_t val = 0; + for (int i = 0; i < 8; i++) { + val |= ((uint64_t)p[offset + i]) << (i * 8); + } + HYPERAMP_BARRIER(); + return val; +} + +/* ==================== Queue Operation Functions (Minimal Version, No printf) ==================== */ + + + +/** + * @brief Check whether the queue has been initialized + */ +int hyperamp_queue_is_initialized(volatile HyperampShmQueue *queue) +{ + if (!queue) return 0; + + HYPERAMP_BARRIER(); + + // Avoid using magic field (offset 4052 > 4096, crosses page boundary); + // instead use capacity field (offset 6) + size_t capacity_offset = offsetof(HyperampShmQueue, capacity); + volatile uint8_t *p = (volatile uint8_t *)queue; + + uint16_t capacity = 0; + for (int i = 0; i < 2; i++) { + capacity |= ((uint16_t)p[capacity_offset + i]) << (i * 8); + } + + HYPERAMP_BARRIER(); + // Initialization indicator: capacity > 0 + return (capacity > 0); +} + +/** + * @brief Enqueue data into the shared memory queue + * @param queue [in/out] Pointer to the shared memory queue control structure (header and enqueue_count will be updated internally) + * @param zone_id [in] Caller's zone ID (used for acquiring the spinlock) + * @param data [in] Source data buffer containing the data to write + * @param data_len [in] Number of bytes to write (must be <= queue block_size) + * @param virt_base [in] Base virtual address of the shared memory data region (used to compute write offset) + * @return HYPERAMP_OK on success, HYPERAMP_ERROR on failure (invalid args, oversized data, or full queue) + */ +int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + const void *data, + size_t data_len, + volatile void *virt_base) +{ + if (!queue || !data || data_len == 0) return HYPERAMP_ERROR; + + // Safely read block_size and capacity + uint16_t block_size = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, block_size)); + uint16_t capacity = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, capacity)); + + if (data_len > block_size) return HYPERAMP_ERROR; + + // Acquire lock + hyperamp_spinlock_lock(&queue->queue_lock, zone_id); + + // Safely read header and tail + uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); + uint16_t tail = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, tail)); + + // Compute new header + uint16_t new_header = header + 1; + if (new_header >= capacity) { + new_header -= capacity; + } + + // Check if queue is full + if (new_header == tail) { + hyperamp_spinlock_unlock(&queue->queue_lock); + return HYPERAMP_ERROR; + } + + // Compute write address + uint64_t write_addr = (uint64_t)virt_base + (uint64_t)(header + 1) * block_size; + + // Write data + hyperamp_safe_memcpy((volatile void *)write_addr, data, data_len); + + // Update header (byte-by-byte) + volatile uint8_t *p = (volatile uint8_t *)queue; + size_t header_offset = offsetof(HyperampShmQueue, header); + p[header_offset] = new_header & 0xFF; + p[header_offset + 1] = (new_header >> 8) & 0xFF; + + // Update enqueue_count (byte-by-byte) + size_t enqueue_offset = offsetof(HyperampShmQueue, enqueue_count); + uint32_t enqueue_count = hyperamp_safe_read_u32(queue, enqueue_offset); + enqueue_count++; + for (int i = 0; i < 4; i++) { + p[enqueue_offset + i] = (enqueue_count >> (i * 8)) & 0xFF; + } + + HYPERAMP_BARRIER(); + + /* Flush written data to memory */ + hyperamp_cache_clean((volatile void *)write_addr, data_len); + /* Flush queue control block to memory */ + hyperamp_cache_clean((volatile void *)queue, 64); /* Only flush first 64 bytes of control fields */ + + // Release lock + hyperamp_spinlock_unlock(&queue->queue_lock); + + return HYPERAMP_OK; +} + +/** + * @brief Dequeue data from the shared memory queue + * @param queue [in/out] Pointer to the shared memory queue control structure (tail and dequeue_count will be updated internally) + * @param zone_id [in] Caller's zone ID (used for acquiring the spinlock) + * @param data [out] Destination buffer to store dequeued data + * @param max_len [in] Maximum capacity of the destination buffer (to prevent overflow) + * @param actual_len [out] Actual number of bytes read (can be NULL if not needed) + * @param virt_base [in] Base virtual address of the shared memory data region (used to compute read offset) + * @return HYPERAMP_OK on success, HYPERAMP_ERROR on failure (invalid args or empty queue) + */ +int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + void *data, + size_t max_len, + size_t *actual_len, + volatile void *virt_base) +{ + if (!queue || !data || max_len == 0) return HYPERAMP_ERROR; + + /* Invalidate cache before reading to ensure latest data is fetched */ + hyperamp_cache_invalidate((volatile void *)queue, 64); + + // Acquire lock + hyperamp_spinlock_lock(&queue->queue_lock, zone_id); + + // Safely read header, tail, block_size, capacity + uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); + uint16_t tail = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, tail)); + uint16_t block_size = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, block_size)); + uint16_t capacity = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, capacity)); + + // Check if queue is empty + if (tail == header) { + hyperamp_spinlock_unlock(&queue->queue_lock); + return HYPERAMP_ERROR; + } + + // Compute read address + uint64_t read_addr = (uint64_t)virt_base + (uint64_t)(tail + 1) * block_size; + + /* Invalidate data region cache to ensure fresh data is read */ + hyperamp_cache_invalidate((volatile void *)read_addr, block_size); + + // Determine actual read length + size_t read_len = (max_len < block_size) ? max_len : block_size; + + // Read data + hyperamp_safe_memcpy(data, (const volatile void *)read_addr, read_len); + + if (actual_len) { + *actual_len = read_len; + } + + // Update tail (byte-by-byte) + uint16_t new_tail = tail + 1; + if (new_tail >= capacity) { + new_tail -= capacity; + } + + volatile uint8_t *p = (volatile uint8_t *)queue; + size_t tail_offset = offsetof(HyperampShmQueue, tail); + p[tail_offset] = new_tail & 0xFF; + p[tail_offset + 1] = (new_tail >> 8) & 0xFF; + + // Update dequeue_count (byte-by-byte) + size_t dequeue_offset = offsetof(HyperampShmQueue, dequeue_count); + uint32_t dequeue_count = hyperamp_safe_read_u32(queue, dequeue_offset); + dequeue_count++; + for (int i = 0; i < 4; i++) { + p[dequeue_offset + i] = (dequeue_count >> (i * 8)) & 0xFF; + } + + HYPERAMP_BARRIER(); + + // Release lock + hyperamp_spinlock_unlock(&queue->queue_lock); + + return HYPERAMP_OK; +} + +/** + * @brief Initialize the shared memory queue (seL4 can now initialize queues itself!) + * @param queue Pointer to the queue + * @param config Configuration parameters + * @param is_creator Whether this process is the queue creator (creator initializes all fields) + * @return HYPERAMP_OK on success, HYPERAMP_ERROR on failure + */ +int hyperamp_queue_init(volatile HyperampShmQueue *queue, + const HyperampQueueConfig *config, + int is_creator) +{ + if (!queue || !config) return HYPERAMP_ERROR; + if (config->block_size == 0 || config->capacity == 0) return HYPERAMP_ERROR; + + if (is_creator) { + // Creator: use safe byte-by-byte writes + volatile uint8_t *p = (volatile uint8_t *)queue; + + // Write map_mode1 and map_mode2 + p[0] = config->map_mode; + p[1] = config->map_mode; + HYPERAMP_BARRIER(); + + // Write header (uint16_t, offset 2) + p[2] = 0; + p[3] = 0; + HYPERAMP_BARRIER(); + + // Write tail (uint16_t, offset 4) + p[4] = 0; + p[5] = 0; + HYPERAMP_BARRIER(); + + // Write capacity (uint16_t, offset 6) + uint16_t cap = config->capacity; + p[6] = cap & 0xFF; + p[7] = (cap >> 8) & 0xFF; + HYPERAMP_BARRIER(); + + // Write block_size (uint16_t, offset 8) + uint16_t bs = config->block_size; + p[8] = bs & 0xFF; + p[9] = (bs >> 8) & 0xFF; + HYPERAMP_BARRIER(); + + // Write _reserved (uint16_t, offset 10) + p[10] = 0; + p[11] = 0; + HYPERAMP_BARRIER(); + + // Write phy_addr (uint64_t, offset 12) – byte-by-byte + uint64_t pa = config->phy_addr; + for (int i = 0; i < 8; i++) { + p[12 + i] = (pa >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + // Write virt_addr1 (uint64_t, offset 20) + uint64_t va = config->virt_addr; + for (int i = 0; i < 8; i++) { + p[20 + i] = (va >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + // Write virt_addr2 (uint64_t, offset 28) – zero out + for (int i = 0; i < 8; i++) { + p[28 + i] = 0; + } + HYPERAMP_BARRIER(); + + // Initialize spinlock + size_t lock_offset = offsetof(HyperampShmQueue, queue_lock); + volatile HyperampSpinlock *lock = (volatile HyperampSpinlock *)&p[lock_offset]; + hyperamp_spinlock_init(lock); + + // Write magic (uint32_t) + size_t magic_offset = offsetof(HyperampShmQueue, magic); + uint32_t magic = HYPERAMP_QUEUE_MAGIC; + for (int i = 0; i < 4; i++) { + p[magic_offset + i] = (magic >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + // Write version (uint32_t) + uint32_t version = 1; + for (int i = 0; i < 4; i++) { + p[magic_offset + 4 + i] = (version >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + // Write enqueue_count (uint32_t) – zero out + for (int i = 0; i < 4; i++) { + p[magic_offset + 8 + i] = 0; + } + HYPERAMP_BARRIER(); + + // Write dequeue_count (uint32_t) – zero out + for (int i = 0; i < 4; i++) { + p[magic_offset + 12 + i] = 0; + } + HYPERAMP_BARRIER(); + + /* Critical: flush entire queue structure to memory, + ensuring other CPUs/Zones can observe the initialization */ + hyperamp_cache_clean((volatile void *)queue, sizeof(HyperampShmQueue)); + + } else { + // Non-creator: only set own virtual address + queue->virt_addr2 = config->virt_addr; + HYPERAMP_BARRIER(); + } + + return HYPERAMP_OK; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/shared_mem_io.c b/projects/sel4test/apps/front/src/shared_mem_io.c index 9ab1c89..e5b1537 100644 --- a/projects/sel4test/apps/front/src/shared_mem_io.c +++ b/projects/sel4test/apps/front/src/shared_mem_io.c @@ -1,6 +1,6 @@ #include "shared_mem_io.h" #include "frontend_proto.h" - +// #include "hyperamp_shm_queue.h" int init_shared_mem_pool(struct SharedMemoryPool *mem_pool){ -- Gitee From 291884b84c9107dbdf815695583afe50d0e353c4 Mon Sep 17 00:00:00 2001 From: xj009 Date: Tue, 10 Feb 2026 13:51:07 +0800 Subject: [PATCH 049/133] hyper shared queue --- projects/sel4test/apps/front/include/engine.h | 12 +-- .../apps/front/include/hyperamp_shm_queue.h | 75 ------------------ .../apps/front/include/shared_mem_io.h | 4 + .../apps/front/src/hyperamp_shm_queue.c | 76 +++++++++++++++++++ 4 files changed, 87 insertions(+), 80 deletions(-) diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index f299d05..2d02579 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -83,11 +83,13 @@ typedef struct FrontendEngine_{ * failed by backend). The state will be restored to FRONTEND_CMD_READY after proper handling of FRONTEND_CMD_OK and FRONTEND_CMD_ERROR. */ FrontendCmdState eng_cmd_state; - struct FrontendSessionPool *sess_pool; // Session pool - struct SharedMemoryPool *mem_pool; // Shared memory pool - struct SharedMemoryPoolLock *mem_pool_lock; // Shared memory pool lock - struct SharedMemoryPoolQueue *rx_queue; // RX queue - struct SharedMemoryPoolQueue *tx_queue; // TX queue + struct FrontendSessionPool *sess_pool; // Session pool + struct SharedMemoryPool *mem_pool; // Shared memory pool + struct SharedMemoryPoolLock *mem_pool_lock; // Shared memory pool lock + struct SharedMemoryPoolQueue *rx_queue; // RX queue + struct SharedMemoryPoolQueue *tx_queue; // TX queue + HyperampShmQueue *hyper_rx_queue; // Hyper RX queue + HyperampShmQueue *hyper_tx_queue; struct FrontendEngOps *ops; } FrontendEngine; diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index 5430bba..6362db7 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -88,81 +88,6 @@ typedef struct { volatile uint32_t contention_count; } __attribute__((packed)) HyperampSpinlock; -static inline void hyperamp_spinlock_init(volatile HyperampSpinlock *lock) -{ - if (!lock) return; - - volatile uint8_t *p = (volatile uint8_t *)lock; - for (size_t i = 0; i < sizeof(HyperampSpinlock); i++) { - p[i] = 0; - } - HYPERAMP_BARRIER(); - - /* Flush the lock state to main memory */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); -} - -static inline void hyperamp_spinlock_lock(volatile HyperampSpinlock *lock, uint32_t zone_id) -{ - if (!lock) return; - - int spin_count = 0; - const int max_spin = 100000; - - while (1) { - HYPERAMP_BARRIER(); - - volatile uint32_t current = lock->lock_value; - - if (current == 0) { - lock->lock_value = 1; - HYPERAMP_BARRIER(); - - volatile uint32_t verify = lock->lock_value; - if (verify == 1) { - lock->owner_zone_id = zone_id; - lock->lock_count++; - HYPERAMP_BARRIER(); - - /* Flush the updated lock state to main memory, - ensuring other cores/VMs observe that the lock is held */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); - return; - } - } - - lock->contention_count++; - spin_count++; - - if (spin_count > max_spin) { - spin_count = 0; -#if defined(__aarch64__) || defined(__arm__) - __asm__ volatile("yield" ::: "memory"); -#else - __asm__ volatile("pause" ::: "memory"); -#endif - } - - for (volatile int i = 0; i < 100; i++) { - HYPERAMP_BARRIER(); - } - } -} - -static inline void hyperamp_spinlock_unlock(volatile HyperampSpinlock *lock) -{ - if (!lock) return; - - HYPERAMP_BARRIER(); - lock->owner_zone_id = 0; - lock->lock_value = 0; - HYPERAMP_BARRIER(); - - /* Critical: flush the unlocked state to main memory, - ensuring other cores/VMs observe that the lock is released */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); -} - /* ==================== Address Mapping Table Entry ==================== */ typedef struct { diff --git a/projects/sel4test/apps/front/include/shared_mem_io.h b/projects/sel4test/apps/front/include/shared_mem_io.h index 256d981..71906ef 100644 --- a/projects/sel4test/apps/front/include/shared_mem_io.h +++ b/projects/sel4test/apps/front/include/shared_mem_io.h @@ -10,6 +10,7 @@ #include #include "session.h" +#include "hyperamp_shm_queue.h" #define ERROR_SHARED_MEM_ADDR UINT64_MAX @@ -21,6 +22,9 @@ #define HSNET_TX_PHY_ADDR_BASE HSNET_RX_PHY_ADDR_BASE + HSNET_MEM_BLOCK_SIZE * MAX_MAP_TABLE_ENTRY_COUNT + + + struct SharedMemoryPoolLock{ int value; // Just for placehoder. We will redefine this struct after receiving the partner's document. }; diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index 892b801..01a0306 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -15,6 +15,82 @@ static const unsigned int TRUSTED_PUBKEY_DER_LEN = 91; +static inline void hyperamp_spinlock_init(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + + volatile uint8_t *p = (volatile uint8_t *)lock; + for (size_t i = 0; i < sizeof(HyperampSpinlock); i++) { + p[i] = 0; + } + HYPERAMP_BARRIER(); + + /* Flush the lock state to main memory */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +} + +static inline void hyperamp_spinlock_lock(volatile HyperampSpinlock *lock, uint32_t zone_id) +{ + if (!lock) return; + + int spin_count = 0; + const int max_spin = 100000; + + while (1) { + HYPERAMP_BARRIER(); + + volatile uint32_t current = lock->lock_value; + + if (current == 0) { + lock->lock_value = 1; + HYPERAMP_BARRIER(); + + volatile uint32_t verify = lock->lock_value; + if (verify == 1) { + lock->owner_zone_id = zone_id; + lock->lock_count++; + HYPERAMP_BARRIER(); + + /* Flush the updated lock state to main memory, + ensuring other cores/VMs observe that the lock is held */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); + return; + } + } + + lock->contention_count++; + spin_count++; + + if (spin_count > max_spin) { + spin_count = 0; +#if defined(__aarch64__) || defined(__arm__) + __asm__ volatile("yield" ::: "memory"); +#else + __asm__ volatile("pause" ::: "memory"); +#endif + } + + for (volatile int i = 0; i < 100; i++) { + HYPERAMP_BARRIER(); + } + } +} + +static inline void hyperamp_spinlock_unlock(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + + HYPERAMP_BARRIER(); + lock->owner_zone_id = 0; + lock->lock_value = 0; + HYPERAMP_BARRIER(); + + /* Critical: flush the unlocked state to main memory, + ensuring other cores/VMs observe that the lock is released */ + hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +} + + /* ==================== Secure Memory Operations ==================== */ static inline void hyperamp_safe_memset(volatile void *dst, uint8_t val, size_t len) -- Gitee From b5d4490a25e15c52a8123ff4d39476dc2e31664d Mon Sep 17 00:00:00 2001 From: xj009 Date: Tue, 10 Feb 2026 18:23:37 +0800 Subject: [PATCH 050/133] Integrate Hyperamp shared memory queues into frontend proxy --- projects/sel4test/apps/front/include/engine.h | 7 +- .../apps/front/include/hardware_config.h | 146 ++++++++++++++++++ .../apps/front/include/hyperamp_shm_queue.h | 33 ++++ projects/sel4test/apps/front/src/engine.c | 100 ++++++++++++ 4 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 projects/sel4test/apps/front/include/hardware_config.h diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index 2d02579..ff52c1f 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -88,8 +88,8 @@ typedef struct FrontendEngine_{ struct SharedMemoryPoolLock *mem_pool_lock; // Shared memory pool lock struct SharedMemoryPoolQueue *rx_queue; // RX queue struct SharedMemoryPoolQueue *tx_queue; // TX queue - HyperampShmQueue *hyper_rx_queue; // Hyper RX queue - HyperampShmQueue *hyper_tx_queue; + volatile HyperampShmQueue *hyper_rx_queue; // Hyper RX queue + volatile HyperampShmQueue *hyper_tx_queue; struct FrontendEngOps *ops; } FrontendEngine; @@ -141,6 +141,9 @@ void engine_destory_mem_pool_lock(FrontendEngine *eng); int engine_choose_hs_net(FrontendEngine *eng, int *selected_dev_id); +int engine_init_hyperamp_queue(FrontendEngine *eng); + + /** * @brief Get the f2b queue from FrontendEngine's session pool * diff --git a/projects/sel4test/apps/front/include/hardware_config.h b/projects/sel4test/apps/front/include/hardware_config.h new file mode 100644 index 0000000..800bcc0 --- /dev/null +++ b/projects/sel4test/apps/front/include/hardware_config.h @@ -0,0 +1,146 @@ +#ifndef HARDWARE_CONF_H_ +#define HARDWARE_CONF_H_ + +#pragma once + +#define CONFIG_ARM_HIKEY_OUTSTANDING_PREFETCHERS 0 +#define CONFIG_ARM_HIKEY_PREFETCHER_STRIDE 0 +#define CONFIG_ARM_HIKEY_PREFETCHER_NPFSTRM 0 +/* disabled: CONFIG_ARM_HIKEY_PREFETCHER_STBPFDIS */ +/* disabled: CONFIG_ARM_HIKEY_PREFETCHER_STBPFRS */ +/* disabled: CONFIG_PLAT_IMX7 */ +/* disabled: CONFIG_ARCH_AARCH32 */ +#define CONFIG_ARCH_AARCH64 1 +/* disabled: CONFIG_ARCH_ARM_HYP */ +/* disabled: CONFIG_ARCH_RISCV32 */ +/* disabled: CONFIG_ARCH_RISCV64 */ +/* disabled: CONFIG_ARCH_X86_64 */ +/* disabled: CONFIG_ARCH_IA32 */ +#define CONFIG_SEL4_ARCH aarch64 +#define CONFIG_ARCH_ARM 1 +#define CONFIG_ARCH arm +#define CONFIG_WORD_SIZE 64 +#define CONFIG_ARM_PLAT imx8mp-evk +/* disabled: CONFIG_PLAT_BCM2711 */ +/* disabled: CONFIG_PLAT_BCM2837 */ +/* disabled: CONFIG_PLAT_FVP */ +/* disabled: CONFIG_PLAT_HIKEY */ +/* disabled: CONFIG_PLAT_IMX8MQ_EVK */ +/* disabled: CONFIG_PLAT_IMX8MM_EVK */ +#define CONFIG_PLAT_IMX8MP_EVK 1 +/* disabled: CONFIG_PLAT_IMX93 */ +/* disabled: CONFIG_PLAT_MAAXBOARD */ +/* disabled: CONFIG_PLAT_ODROIDC2 */ +/* disabled: CONFIG_PLAT_ODROIDC4 */ +/* disabled: CONFIG_PLAT_PHYTIUM_PI */ +/* disabled: CONFIG_PLAT_QEMU_ARM_VIRT */ +/* disabled: CONFIG_PLAT_QUARTZ64 */ +/* disabled: CONFIG_PLAT_RK3588 */ +/* disabled: CONFIG_PLAT_ROCKPRO64 */ +/* disabled: CONFIG_PLAT_TQMA8XQP1GB */ +/* disabled: CONFIG_PLAT_TX1 */ +/* disabled: CONFIG_PLAT_TX2 */ +/* disabled: CONFIG_PLAT_ZYNQMP */ +#define CONFIG_PLAT imx8mp-evk +/* disabled: CONFIG_ARM_CORTEX_A7 */ +/* disabled: CONFIG_ARM_CORTEX_A8 */ +/* disabled: CONFIG_ARM_CORTEX_A9 */ +/* disabled: CONFIG_ARM_CORTEX_A15 */ +/* disabled: CONFIG_ARM_CORTEX_A35 */ +#define CONFIG_ARM_CORTEX_A53 1 +/* disabled: CONFIG_ARM_CORTEX_A55 */ +/* disabled: CONFIG_ARM_CORTEX_A57 */ +/* disabled: CONFIG_ARM_CORTEX_A72 */ +/* disabled: CONFIG_ARCH_ARM_V7A */ +/* disabled: CONFIG_ARCH_ARM_V7VE */ +#define CONFIG_ARCH_ARM_V8A 1 +/* disabled: CONFIG_AARCH64_SERROR_IGNORE */ +#define CONFIG_ARM_MACH imx +/* disabled: CONFIG_KERNEL_MCS */ +#define CONFIG_ARM_PA_SIZE_BITS_40 1 +/* disabled: CONFIG_ARM_PA_SIZE_BITS_44 */ +#define CONFIG_ARM_ICACHE_VIPT 1 +/* disabled: CONFIG_DEBUG_DISABLE_L2_CACHE */ +/* disabled: CONFIG_DEBUG_DISABLE_L1_ICACHE */ +/* disabled: CONFIG_DEBUG_DISABLE_L1_DCACHE */ +/* disabled: CONFIG_DEBUG_DISABLE_BRANCH_PREDICTION */ +/* disabled: CONFIG_ARM_HYPERVISOR_SUPPORT */ +#define CONFIG_ARM_GIC_V3_SUPPORT 1 +/* disabled: CONFIG_AARCH64_VSPACE_S2_START_L1 */ +/* disabled: CONFIG_ARM_HYP_ENABLE_VCPU_CP14_SAVE_AND_RESTORE */ +/* disabled: CONFIG_ARM_ERRATA_430973 */ +/* disabled: CONFIG_ARM_ERRATA_773022 */ +/* disabled: CONFIG_ARM_SMMU */ +/* disabled: CONFIG_TK1_SMMU */ +/* disabled: CONFIG_ENABLE_A9_PREFETCHER */ +/* disabled: CONFIG_EXPORT_PMU_USER */ +/* disabled: CONFIG_DISABLE_WFI_WFE_TRAPS */ +/* disabled: CONFIG_SMMU_INTERRUPT_ENABLE */ +/* disabled: CONFIG_AARCH32_FPU_ENABLE_CONTEXT_SWITCH */ +#define CONFIG_AARCH64_USER_CACHE_ENABLE 1 +/* disabled: CONFIG_ALLOW_SMC_CALLS */ +#define CONFIG_ARM_TLS_REG_TPIDRU 1 +/* disabled: CONFIG_ARM_TLS_REG_TPIDRURO */ +#define CONFIG_ARM_TLS_REG tpidru +#define CONFIG_L1_CACHE_LINE_SIZE_BITS 6 +/* disabled: CONFIG_ARM_HAS_TLB_LOCK */ +#define CONFIG_HAVE_FPU 1 +#define CONFIG_PADDR_USER_DEVICE_TOP 1099511627776 +#define CONFIG_ROOT_CNODE_SIZE_BITS 13 +#define CONFIG_TIMER_TICK_MS 2 +#define CONFIG_TIME_SLICE 5 +#define CONFIG_RETYPE_FAN_OUT_LIMIT 256 +#define CONFIG_MAX_NUM_WORK_UNITS_PER_PREEMPTION 100 +#define CONFIG_RESET_CHUNK_BITS 8 +#define CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS 230 +#define CONFIG_FASTPATH 1 +/* disabled: CONFIG_EXCEPTION_FASTPATH */ +#define CONFIG_NUM_DOMAINS 1 +/* disabled: CONFIG_SIGNAL_FASTPATH */ +#define CONFIG_NUM_PRIORITIES 256 +#define CONFIG_MAX_NUM_NODES 1 +/* disabled: CONFIG_ENABLE_SMP_SUPPORT */ +#define CONFIG_KERNEL_STACK_BITS 12 +/* disabled: CONFIG_VERIFICATION_BUILD */ +/* disabled: CONFIG_BINARY_VERIFICATION_BUILD */ +#define CONFIG_DEBUG_BUILD 1 +#define CONFIG_HARDWARE_DEBUG_API 1 +#define CONFIG_PRINTING 1 +/* disabled: CONFIG_KERNEL_INVOCATION_REPORT_ERROR_IPC */ +#define CONFIG_NO_BENCHMARKS 1 +/* disabled: CONFIG_BENCHMARK_GENERIC */ +/* disabled: CONFIG_BENCHMARK_TRACK_KERNEL_ENTRIES */ +/* disabled: CONFIG_BENCHMARK_TRACEPOINTS */ +/* disabled: CONFIG_BENCHMARK_TRACK_UTILISATION */ +#define CONFIG_KERNEL_BENCHMARK none +/* disabled: CONFIG_ENABLE_BENCHMARKS */ +/* disabled: CONFIG_KERNEL_LOG_BUFFER */ +#define CONFIG_MAX_NUM_TRACE_POINTS 0 +#define CONFIG_IRQ_REPORTING 1 +#define CONFIG_COLOUR_PRINTING 1 +#define CONFIG_USER_STACK_TRACE_LENGTH 16 +#define CONFIG_KERNEL_OPT_LEVEL_O2 1 +/* disabled: CONFIG_KERNEL_OPT_LEVEL_OS */ +/* disabled: CONFIG_KERNEL_OPT_LEVEL_O0 */ +/* disabled: CONFIG_KERNEL_OPT_LEVEL_O1 */ +/* disabled: CONFIG_KERNEL_OPT_LEVEL_O3 */ +#define CONFIG_KERNEL_OPT_LEVEL -O2 +#define CONFIG_KERNEL_OPTIMISATION_CLONE_FUNCTIONS 1 +/* disabled: CONFIG_KERNEL_FWHOLE_PROGRAM */ +/* disabled: CONFIG_DANGEROUS_CODE_INJECTION */ +/* disabled: CONFIG_DEBUG_DISABLE_PREFETCHERS */ +/* disabled: CONFIG_SET_TLS_BASE_SELF */ +/* disabled: CONFIG_CLZ_32 */ +/* disabled: CONFIG_CLZ_64 */ +/* disabled: CONFIG_CTZ_32 */ +/* disabled: CONFIG_CTZ_64 */ +/* disabled: CONFIG_CLZ_NO_BUILTIN */ +/* disabled: CONFIG_CTZ_NO_BUILTIN */ +/* disabled: CONFIG_EXPORT_PCNT_USER */ +/* disabled: CONFIG_EXPORT_VCNT_USER */ +/* disabled: CONFIG_EXPORT_PTMR_USER */ +/* disabled: CONFIG_EXPORT_VTMR_USER */ +#define CONFIG_VTIMER_UPDATE_VOFFSET 1 + + +#endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index 6362db7..d8c2914 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -10,6 +10,7 @@ #include #include +#include "hardware_config.h" /* ==================== Constant Definitions ==================== */ @@ -34,6 +35,7 @@ typedef enum { /* Queue magic number */ #define HYPERAMP_QUEUE_MAGIC 0x48415150 // "HAQP" + /* ==================== Memory Barriers and Cache Operations ==================== */ #if defined(__aarch64__) || defined(__arm__) @@ -77,6 +79,37 @@ typedef enum { } #endif +#if defined(CONFIG_PLAT_IMX8MP_EVK) || defined(CONFIG_PLAT_RK3588) + // Shared memory configuration for i.MX8MP platform + #define SHM_TX_QUEUE_PADDR 0x7E000000UL + #define SHM_RX_QUEUE_PADDR 0x7E001000UL + #define SHM_DATA_PADDR 0x7E002000UL + +#elif defined(CONFIG_PLAT_PHYTIUM_PI) + // Shared memory configuration for Phytium-Pi platform + #define SHM_TX_QUEUE_PADDR 0xDE000000UL + #define SHM_RX_QUEUE_PADDR 0xDE001000UL + #define SHM_DATA_PADDR 0xDE002000UL + +#else + #error "Unknown Platform! Please define addresses for this board." +#endif + + +// Virtual address: Start of the TX queue in Hyperamp shared memory (seL4 → Linux) +#define SHM_TX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54E000UL) + +// Virtual address: Start of the RX queue in Hyperamp shared memory (Linux → seL4) +#define SHM_RX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54F000UL) + +// Virtual address: Start of the general data region in Hyperamp shared memory +#define SHM_DATA_REGION_VA ((volatile void *)0x550000UL) + +//g_tx_queue = (volatile HyperampShmQueue *)0x54e000; +//g_rx_queue = (volatile HyperampShmQueue *)0x54f000; +//g_data_region = (volatile void *)0x550000; + + #define HYPERAMP_BARRIER() do { HYPERAMP_DMB(); HYPERAMP_DSB(); } while(0) /* ==================== Software Spinlock ==================== */ diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 41ac27b..0e69d25 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -3,6 +3,18 @@ extern FrontendEngine *p_g_fr_eng; extern FrontendEngine g_fr_eng; +static volatile HyperampShmQueue *g_hyper_tx_queue = NULL; // seL4 → Linux (seL4 writes requests, Linux reads) +static volatile HyperampShmQueue *g_hyper_rx_queue = NULL; // Linux → seL4 (seL4 reads responses, Linux writes) + +#if 0 +HyperampQueueConfig hyper_tx_config = { + .map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH, + .capacity = 256, + .block_size = 4096, + .phy_addr = SHM_TX_QUEUE_PADDR, + .virt_addr = (uint64_t)g_hyper_tx_queue, +}; +#endif /** * Configuration structure for the high-speed network receive queue. @@ -36,6 +48,37 @@ SharedMemoryPoolQueueConfig high_speed_net_tx_queue_config = { }; +/** + * Configuration structure for the Hyperamp shared transmit queue (seL4 → Linux). + * + * This global instance defines the parameters required to set up the transmit queue's + * shared memory region, including the mapping mode, physical and virtual addresses, + * queue capacity, and block size. + */ +HyperampQueueConfig hyperamp_tx_config = { + .map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH, + .capacity = 256, + .block_size = 4096, + .phy_addr = SHM_TX_QUEUE_PADDR, + .virt_addr = (uint64_t)SHM_TX_QUEUE_VADDR, +}; + + +/** + * Configuration structure for the Hyperamp shared receive queue (Linux → seL4). + * + * This global instance defines the parameters required to set up the receive queue's + * shared memory region, including the mapping mode, physical and virtual addresses, + * queue capacity, and block size. + */ +HyperampQueueConfig hyperamp_rx_config = { + .map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH, + .capacity = 256, + .block_size = 4096, + .phy_addr = SHM_RX_QUEUE_PADDR, + .virt_addr = (uint64_t)SHM_RX_QUEUE_VADDR, +}; + /** * @brief Get data from the specified RX queue (residing in shared memory) * @param queue Pointer to the SharedMemoryPoolQueue (RX queue) to operate on @@ -338,6 +381,63 @@ int engine_init_hs_net_dev(FrontendEngine *eng){ } +/** + * @brief Initializes the Hyperamp shared transmit and receive queues for the frontend engine. + * + * This function initializes both the transmit (TX) and receive (RX) shared memory queues + * used by the Hyperamp inter-OS communication layer. It uses the global configuration + * structures (e.g., tx_config and rx_config) that specify physical/virtual addresses, + * capacity, block size, and mapping mode. + * + * The initialization includes setting up queue metadata, validating memory mappings, + * and preparing the queues for message passing between seL4 and Linux. + * + * @param[in] eng Pointer to the FrontendEngine instance. Must not be NULL. + * + * @return int Status code indicating the result of the initialization: + * - @c FRONTEND_PROXY_PROCESS_OK on success. + * - @c FRONTEND_PROXY_PROCESS_ERROR if any step fails (e.g., invalid config, + * memory mapping error, or queue setup failure). + * + * @note This function assumes that the underlying shared memory regions are already + * mapped into the virtual address space of the current process. + */ +int engine_init_hyperamp_queue(FrontendEngine *eng){ + int ret; + + if(NULL == eng){ + error_print("engine_init_hyperamp_queue failed: input parameter must not be NULL\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + g_hyper_tx_queue = SHM_TX_QUEUE_VADDR; + g_hyper_rx_queue = SHM_RX_QUEUE_VADDR; + + ret = hyperamp_queue_init(g_hyper_tx_queue, &hyperamp_tx_config, 1); + + if(HYPERAMP_OK != ret){ + error_print("engine_init_hyperamp_queue failed: failed to initialize Hyperamp TX queue!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* Critical: Invalidate cache to ensure the printed data is the latest from physical memory */ + hyperamp_cache_invalidate((volatile void *)g_hyper_tx_queue, 64); + + ret = hyperamp_queue_init(g_hyper_rx_queue, &hyperamp_rx_config, 1); + + if(HYPERAMP_OK != ret){ + error_print("engine_init_hyperamp_queue failed: failed to initialize Hyperamp RX queue!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +/* Critical: Invalidate cache to ensure subsequent reads reflect the latest data from physical memory */ + hyperamp_cache_invalidate((volatile void *)g_hyper_rx_queue, 64); + + eng->hyper_tx_queue = g_hyper_tx_queue; + eng->hyper_rx_queue = g_hyper_rx_queue; + + return FRONTEND_PROXY_PROCESS_OK; +} /** -- Gitee From 88f9409c89a39b4baf31045f840fc61a38dbfa36 Mon Sep 17 00:00:00 2001 From: xj009 Date: Wed, 11 Feb 2026 10:11:05 +0800 Subject: [PATCH 051/133] Integrate Hyperamp shared memory queues into frontend proxy --- projects/sel4test/apps/front/include/dev.h | 2 +- projects/sel4test/apps/front/include/message.h | 3 ++- projects/sel4test/apps/front/src/dev.c | 2 +- projects/sel4test/apps/front/src/engine.c | 9 +++++++++ projects/sel4test/apps/front/src/frontend_proto.c | 2 ++ 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/include/dev.h b/projects/sel4test/apps/front/include/dev.h index bd002e1..8505f0e 100644 --- a/projects/sel4test/apps/front/include/dev.h +++ b/projects/sel4test/apps/front/include/dev.h @@ -52,7 +52,7 @@ typedef struct FrontendHighSpeedNetDeviceSet_ { FrontendDevInfo hs_net_dev[MAX_HS_DEV_NUM]; }FrontendHighSpeedNetDeviceSet; -FrontendDevListCfg *p_global_dev_list_cfg; +extern FrontendDevListCfg *p_global_dev_list_cfg; void frontend_init_dev_list(); diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index 29c9d43..3bb0465 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -595,7 +595,8 @@ struct SharedMemoryPoolQueue; typedef enum { MEMORY_ALLOC_SHARED, // Allocate in shared memory - MEMORY_ALLOC_CALLER // Allocated by caller + MEMORY_ALLOC_CALLER, // Memory is allocated by the caller + MEMORY_ALLOC_AMPQUEUE // Message is placed directly into the HyperAMP queue } MemoryAllocMode; int build_proxy_general_message(struct FrontendEngine_ *engine, GeneralProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, diff --git a/projects/sel4test/apps/front/src/dev.c b/projects/sel4test/apps/front/src/dev.c index ad8c11e..3e8868e 100644 --- a/projects/sel4test/apps/front/src/dev.c +++ b/projects/sel4test/apps/front/src/dev.c @@ -15,7 +15,7 @@ FrontendDevInfo dev_array[] = { } }; - +FrontendDevListCfg *p_global_dev_list_cfg = NULL; FrontendDevListCfg global_dev_list_cfg; diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 0e69d25..758e91c 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -253,6 +253,15 @@ void frontend_engine_init(){ free(p_g_fr_eng->dev_set); abort(); } + + ret = engine_init_hyperamp_queue(p_g_fr_eng); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize hyperamp memory queue!\n!"); + free(p_g_fr_eng->sess_pool); + free(p_g_fr_eng->dev_set); + abort(); + } } diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 73bf418..4907372 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -332,6 +332,8 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h } *result_msg = msg_buf; + }else if(MEMORY_ALLOC_AMPQUEUE == msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; }else{ error_print("build_proxy_general_message failed: unsupported allocte mode!"); return FRONTEND_PROXY_PROCESS_ERROR; -- Gitee From db1a2617710b5c6dae79b95c3a832af8477ce13e Mon Sep 17 00:00:00 2001 From: xj009 Date: Wed, 11 Feb 2026 10:53:00 +0800 Subject: [PATCH 052/133] fix some bugs --- projects/sel4test/apps/front/src/frontend_proto.c | 1 + projects/sel4test/apps/front/src/session_pool.c | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 4907372..8fbbd54 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -333,6 +333,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h *result_msg = msg_buf; }else if(MEMORY_ALLOC_AMPQUEUE == msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; }else{ error_print("build_proxy_general_message failed: unsupported allocte mode!"); diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c index 5d53be7..98557a2 100644 --- a/projects/sel4test/apps/front/src/session_pool.c +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -154,9 +154,10 @@ int frontend_high_speed_init_pool(struct FrontendSessionPool *pool) TAILQ_INIT(&pool->queue_f2b); TAILQ_INIT(&pool->queue_b2f); - pool->htable = NULL; - pool->ops = &fr_high_speed_pool_ops; - pool->engine = frontend_get_global_engine(); + pool->htable = NULL; + pool->ops = &fr_high_speed_pool_ops; + pool->engine = frontend_get_global_engine(); + front_high_speed_pool = pool; return FRONTEND_PROXY_PROCESS_OK; } -- Gitee From 9007e52e653ad6fdb55a556bc4308cdc9afca2ae Mon Sep 17 00:00:00 2001 From: xj009 Date: Wed, 11 Feb 2026 18:18:31 +0800 Subject: [PATCH 053/133] Integrate Hyperamp shared memory queues into frontend proxy --- projects/sel4test/apps/front/include/engine.h | 39 +++++++++++++++ .../apps/front/include/hyperamp_shm_queue.h | 5 ++ projects/sel4test/apps/front/src/engine.c | 47 +++++++++++++++++-- .../sel4test/apps/front/src/frontend_proto.c | 27 +++++++++-- 4 files changed, 109 insertions(+), 9 deletions(-) diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index ff52c1f..a7cdb64 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -11,6 +11,10 @@ #define HS_NET_DEV_CFG "hs_net_dev.ini" + +extern volatile HyperampShmQueue *g_hyper_tx_queue; // seL4 → Linux (seL4 writes requests, Linux reads) +extern volatile HyperampShmQueue *g_hyper_rx_queue; // Linux → seL4 (seL4 reads responses, Linux writes) +extern volatile void *g_hyper_data_region; // Shared data buffer referenced by entries in TX/RX queues /* * Check if the string is a valid IPv4 address * Parameter: ip_str - string to check @@ -276,6 +280,41 @@ int frontend_engine_tx_queue_send(struct SharedMemoryPoolQueue *queue, const voi uint8_t *frontend_engine_rx_queue_get_msg(struct SharedMemoryPoolQueue *queue, size_t max_msg_len, int *ret, size_t *out_len); + +/** + * @brief Retrieves a message from the HyperAMP receive queue managed by the FrontendEngine. + * + * @details This function fetches message data from the HyperAMP RX queue associated with the given + * FrontendEngine instance. It returns a pointer to the message buffer if data is available, + * and outputs the operation status and actual message length through output parameters. + * The caller must check the status code stored in @p ret before using the returned buffer + * and message length. + * + * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL + * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow + * @param[out] ret Pointer to an integer variable that stores the operation status code + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * + * @return Pointer to the uint8_t message data buffer on successful data retrieval; + * NULL if the operation fails, the queue is empty, or a system error occurs + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized), + * the returned pointer is NULL and @p out_len is undefined + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message data is temporarily unavailable (e.g., HyperAMP RX queue is empty), + * the returned pointer is NULL and @p out_len is undefined + * + * @note The underlying message buffer resides in shared memory (e.g., @c g_hyper_data_region). + * The ownership and release semantics are determined by the HyperAMP queue protocol; + * typically, the buffer remains valid until the next receive operation or explicit acknowledgment. + * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. + */ +uint8_t *frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, + int *ret, size_t *out_len); + struct FrontendEngOps *get_hs_frontend_engine_ops(); diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index d8c2914..16e26a7 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -19,6 +19,11 @@ #define HYPERAMP_OK 0 #define HYPERAMP_ERROR (-1) +#define HYPERAMP_AGAIN 1 + +#define HYPERAMP_ZONEID_ROOTLINUX 0 +#define HYPERAMP_ZONEID_SEL4 1 + /* Memory mapping modes */ typedef enum { diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 758e91c..af83d88 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -3,8 +3,9 @@ extern FrontendEngine *p_g_fr_eng; extern FrontendEngine g_fr_eng; -static volatile HyperampShmQueue *g_hyper_tx_queue = NULL; // seL4 → Linux (seL4 writes requests, Linux reads) -static volatile HyperampShmQueue *g_hyper_rx_queue = NULL; // Linux → seL4 (seL4 reads responses, Linux writes) +volatile HyperampShmQueue *g_hyper_tx_queue = NULL; // seL4 → Linux (seL4 writes requests, Linux reads) +volatile HyperampShmQueue *g_hyper_rx_queue = NULL; // Linux → seL4 (seL4 reads responses, Linux writes) +volatile void *g_hyper_data_region = NULL; // Shared data buffer referenced by entries in TX/RX queues #if 0 HyperampQueueConfig hyper_tx_config = { @@ -131,6 +132,43 @@ int frontend_engine_rx_queue_get(struct SharedMemoryPoolQueue *queue, void **buf } +/** + * @brief Retrieves a message from the HyperAMP receive queue managed by the FrontendEngine. + * + * @details This function fetches message data from the HyperAMP RX queue associated with the given + * FrontendEngine instance. It returns a pointer to the message buffer if data is available, + * and outputs the operation status and actual message length through output parameters. + * The caller must check the status code stored in @p ret before using the returned buffer + * and message length. + * + * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL + * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow + * @param[out] ret Pointer to an integer variable that stores the operation status code + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * + * @return Pointer to the uint8_t message data buffer on successful data retrieval; + * NULL if the operation fails, the queue is empty, or a system error occurs + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized), + * the returned pointer is NULL and @p out_len is undefined + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message data is temporarily unavailable (e.g., HyperAMP RX queue is empty), + * the returned pointer is NULL and @p out_len is undefined + * + * @note The underlying message buffer resides in shared memory (e.g., @c g_hyper_data_region). + * The ownership and release semantics are determined by the HyperAMP queue protocol; + * typically, the buffer remains valid until the next receive operation or explicit acknowledgment. + * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. + */ +uint8_t *frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, + int *ret, size_t *out_len){ + return NULL; +} + + /** * @brief Retrieves a message from the shared memory pool queue. * @@ -419,8 +457,9 @@ int engine_init_hyperamp_queue(FrontendEngine *eng){ return FRONTEND_PROXY_PROCESS_ERROR; } - g_hyper_tx_queue = SHM_TX_QUEUE_VADDR; - g_hyper_rx_queue = SHM_RX_QUEUE_VADDR; + g_hyper_tx_queue = SHM_TX_QUEUE_VADDR; + g_hyper_rx_queue = SHM_RX_QUEUE_VADDR; + g_hyper_data_region = SHM_DATA_REGION_VA; ret = hyperamp_queue_init(g_hyper_tx_queue, &hyperamp_tx_config, 1); diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 8fbbd54..08496d0 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -2,6 +2,7 @@ #include "frontend_proto.h" +uint8_t global_amp_tx_buf[HYPERAMP_MSG_HDR_PLUS_MAX_SIZE]; /* * Functions for building sub-type proxy messages. @@ -293,7 +294,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h } outer_msg_type = header->outer_header.proxy_msg_type; - header->inner_header.dev_hdr.payload_len; +// header->inner_header.dev_hdr.payload_len; /* @@ -327,16 +328,21 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h msg_buf = malloc(alloc_size); if(NULL == msg_buf){ - error_print("insufficient memory for allocation!\n"); + error_print("build_proxy_general_message failed: insufficient memory for allocation!\n"); return FRONTEND_PROXY_PROCESS_ERROR; } *result_msg = msg_buf; }else if(MEMORY_ALLOC_AMPQUEUE == msg_buf){ - - return FRONTEND_PROXY_PROCESS_OK; + if(NULL == engine->hyper_tx_queue){ + error_print("build_proxy_general_message failed: HyperAMP TX queue is not initialized!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + memset(global_amp_tx_buf, 0, sizeof(global_amp_tx_buf)); + msg_buf = global_amp_tx_buf; + *result_msg = msg_buf; }else{ - error_print("build_proxy_general_message failed: unsupported allocte mode!"); + error_print("build_proxy_general_message failed: unsupported allocte mode!\n"); return FRONTEND_PROXY_PROCESS_ERROR; } @@ -428,6 +434,17 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h debug_cnt++; } #endif + +/* + * In MEMORY_ALLOC_AMPQUEUE mode, the created message should be pushed into the HyperAMP shared queue. + */ + if(MEMORY_ALLOC_AMPQUEUE == alloc_mode){ + g_hyper_data_region; + msg_buf -= sizeof(ProxyMsgHeader); + ret = hyperamp_queue_enqueue(engine->hyper_tx_queue, HYPERAMP_ZONEID_SEL4, msg_buf, payload_len + proxy_msg_payload_len + sizeof(ProxyMsgHeader), g_hyper_data_region); + return ret; + } + return FRONTEND_PROXY_PROCESS_OK; } #endif -- Gitee From 41be44257b66d5c6e94bc211086b0d1f21c352e7 Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 12 Feb 2026 14:24:40 +0800 Subject: [PATCH 054/133] Integrate Hyperamp shared memory queues into frontend proxy --- projects/sel4test/apps/front/include/engine.h | 42 +++++++----- projects/sel4test/apps/front/src/engine.c | 68 +++++++++++++------ .../sel4test/apps/front/src/frontend_proto.c | 13 +++- 3 files changed, 83 insertions(+), 40 deletions(-) diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index a7cdb64..38db833 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -285,35 +285,41 @@ uint8_t *frontend_engine_rx_queue_get_msg(struct SharedMemoryPoolQueue *queue, s * @brief Retrieves a message from the HyperAMP receive queue managed by the FrontendEngine. * * @details This function fetches message data from the HyperAMP RX queue associated with the given - * FrontendEngine instance. It returns a pointer to the message buffer if data is available, - * and outputs the operation status and actual message length through output parameters. - * The caller must check the status code stored in @p ret before using the returned buffer - * and message length. + * FrontendEngine instance, and copies the message data to the provided buffer pointed to by @p data. + * It returns an integer status code to indicate the operation result, and outputs the actual + * length of the retrieved message through the @p out_len parameter. + * The caller must check the returned status code before using the data in @p data and the value in @p out_len. * * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL - * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow - * @param[out] ret Pointer to an integer variable that stores the operation status code - * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * @param[in] max_msg_len Maximum allowed length of the message to read (i.e., the size of the @p data buffer), + * used to prevent buffer overflow + * @param[out] data Pointer to a uint8_t buffer that stores the retrieved message data; + * valid only when the return value is FRONTEND_PROXY_PROCESS_OK + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message; + * valid only when the return value is FRONTEND_PROXY_PROCESS_OK * - * @return Pointer to the uint8_t message data buffer on successful data retrieval; - * NULL if the operation fails, the queue is empty, or a system error occurs + * @return Integer status code indicating the result of the operation: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: System-level error occurred + * - FRONTEND_PROXY_PROCESS_AGAIN: Message temporarily unavailable * * @retval FRONTEND_PROXY_PROCESS_OK - * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * Message data is retrieved successfully, the @p data buffer contains valid message content + * and @p out_len holds the actual message length * @retval FRONTEND_PROXY_PROCESS_ERROR - * A system-level error occurred (e.g., engine is NULL, internal queue not initialized), - * the returned pointer is NULL and @p out_len is undefined + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized, @p data/@p out_len is NULL), + * the @p data buffer and @p out_len are undefined * @retval FRONTEND_PROXY_PROCESS_AGAIN * Message data is temporarily unavailable (e.g., HyperAMP RX queue is empty), - * the returned pointer is NULL and @p out_len is undefined + * the @p data buffer and @p out_len are undefined * - * @note The underlying message buffer resides in shared memory (e.g., @c g_hyper_data_region). - * The ownership and release semantics are determined by the HyperAMP queue protocol; - * typically, the buffer remains valid until the next receive operation or explicit acknowledgment. + * @note The message data copied to @p data is sourced from shared memory (e.g., @c g_hyper_data_region). + * The ownership of the @p data buffer is held by the caller (who is responsible for allocating/freeing it), + * while the underlying shared memory lifecycle follows the HyperAMP queue protocol. * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. */ -uint8_t *frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, - int *ret, size_t *out_len); +int frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, + uint8_t *data, size_t *out_len); struct FrontendEngOps *get_hs_frontend_engine_ops(); diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index af83d88..12426e3 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -136,36 +136,66 @@ int frontend_engine_rx_queue_get(struct SharedMemoryPoolQueue *queue, void **buf * @brief Retrieves a message from the HyperAMP receive queue managed by the FrontendEngine. * * @details This function fetches message data from the HyperAMP RX queue associated with the given - * FrontendEngine instance. It returns a pointer to the message buffer if data is available, - * and outputs the operation status and actual message length through output parameters. - * The caller must check the status code stored in @p ret before using the returned buffer - * and message length. + * FrontendEngine instance, and copies the message data to the provided buffer pointed to by @p data. + * It returns an integer status code to indicate the operation result, and outputs the actual + * length of the retrieved message through the @p out_len parameter. + * The caller must check the returned status code before using the data in @p data and the value in @p out_len. * * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL - * @param[in] max_msg_len Maximum allowed length of the message to read, used to prevent buffer overflow - * @param[out] ret Pointer to an integer variable that stores the operation status code - * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message + * @param[in] max_msg_len Maximum allowed length of the message to read (i.e., the size of the @p data buffer), + * used to prevent buffer overflow + * @param[out] data Pointer to a uint8_t buffer that stores the retrieved message data; + * valid only when the return value is FRONTEND_PROXY_PROCESS_OK + * @param[out] out_len Pointer to a size_t variable that stores the actual length of the retrieved message; + * valid only when the return value is FRONTEND_PROXY_PROCESS_OK * - * @return Pointer to the uint8_t message data buffer on successful data retrieval; - * NULL if the operation fails, the queue is empty, or a system error occurs + * @return Integer status code indicating the result of the operation: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: System-level error occurred + * - FRONTEND_PROXY_PROCESS_AGAIN: Message temporarily unavailable * * @retval FRONTEND_PROXY_PROCESS_OK - * Message data is retrieved successfully, the returned pointer and @p out_len are valid + * Message data is retrieved successfully, the @p data buffer contains valid message content + * and @p out_len holds the actual message length * @retval FRONTEND_PROXY_PROCESS_ERROR - * A system-level error occurred (e.g., engine is NULL, internal queue not initialized), - * the returned pointer is NULL and @p out_len is undefined + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized, @p data/@p out_len is NULL), + * the @p data buffer and @p out_len are undefined * @retval FRONTEND_PROXY_PROCESS_AGAIN * Message data is temporarily unavailable (e.g., HyperAMP RX queue is empty), - * the returned pointer is NULL and @p out_len is undefined + * the @p data buffer and @p out_len are undefined * - * @note The underlying message buffer resides in shared memory (e.g., @c g_hyper_data_region). - * The ownership and release semantics are determined by the HyperAMP queue protocol; - * typically, the buffer remains valid until the next receive operation or explicit acknowledgment. + * @note The message data copied to @p data is sourced from shared memory (e.g., @c g_hyper_data_region). + * The ownership of the @p data buffer is held by the caller (who is responsible for allocating/freeing it), + * while the underlying shared memory lifecycle follows the HyperAMP queue protocol. * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. */ -uint8_t *frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, - int *ret, size_t *out_len){ - return NULL; +int frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, + uint8_t *data, size_t *out_len){ + int ret; + if(NULL == eng || NULL == eng->hyper_rx_queue || NULL == data || NULL == out_len){ + error_print("frontend_engine_hyperamp_rx_queue_get failed: invalid input parameters (NULL pointers)\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(NULL == g_hyper_data_region){ + error_print("frontend_engine_hyperamp_rx_queue_get failed: The HyperAMP shared memory region is not initialized!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + ret = hyperamp_queue_dequeue(eng->hyper_rx_queue, HYPERAMP_ZONEID_SEL4, data, max_msg_len, out_len, g_hyper_data_region); + + if(HYPERAMP_ERROR == ret){ + error_print("frontend_engine_hyperamp_rx_queue_get failed: hyperamp_queue_dequeue execution failed!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + }else if(HYPERAMP_AGAIN == ret){ + return FRONTEND_PROXY_PROCESS_AGAIN; + }else{ +/* + * hyperamp_queue_dequeue execution succeeded, no action required. + */ + } + + return FRONTEND_PROXY_PROCESS_OK; } diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 08496d0..990eac9 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -391,7 +391,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h } if(FRONTEND_PROXY_PROCESS_OK != ret){ - error_print("build_proxy_general_message failed: failed to build proxy message!"); + error_print("build_proxy_general_message failed: failed to build proxy message!\n"); // free_shared_mem(engine->mem_pool, mem_addr); SHM_POOL_QUEUE_HEAD_ROLLBACK(ring_buf); return FRONTEND_PROXY_PROCESS_ERROR; @@ -442,9 +442,16 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h g_hyper_data_region; msg_buf -= sizeof(ProxyMsgHeader); ret = hyperamp_queue_enqueue(engine->hyper_tx_queue, HYPERAMP_ZONEID_SEL4, msg_buf, payload_len + proxy_msg_payload_len + sizeof(ProxyMsgHeader), g_hyper_data_region); - return ret; - } + if(HYPERAMP_OK == ret){ + return FRONTEND_PROXY_PROCESS_OK; + }else if(HYPERAMP_AGAIN == ret){ + return FRONTEND_PROXY_PROCESS_AGAIN; + }else{ + error_print("build_proxy_general_message failed: faild to push message into the HyperAmp queue!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + } return FRONTEND_PROXY_PROCESS_OK; } #endif -- Gitee From 9f842ad098280990080b857bde602d160d7baeea Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 13 Feb 2026 14:02:33 +0800 Subject: [PATCH 055/133] adding test codes --- .../apps/front/include/senario_test.h | 4 ++ projects/sel4test/apps/front/src/main.c | 6 ++ .../sel4test/apps/front/src/senario_test.c | 56 ++++++++++++++++++- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/include/senario_test.h b/projects/sel4test/apps/front/include/senario_test.h index a64418d..3d94e96 100644 --- a/projects/sel4test/apps/front/include/senario_test.h +++ b/projects/sel4test/apps/front/include/senario_test.h @@ -31,4 +31,8 @@ int data_msg_inject_frontend(FrontendEngine *engine); int test_proxy_scenario_msg_read_from_rx_queue_frontend(FrontendEngine *engine); + +int test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(FrontendEngine *engine); +int device_msg_inject_frontend_hyperamp(FrontendEngine *engine); + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index f468d93..097767b 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -29,11 +29,17 @@ int main(void){ frontend_engine_init(); eng = frontend_get_global_engine(); + +#if 0 sess = frontend_sess_new(eng); ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.100:8080"); test_proxy_scenario_multi_type_msg_build_frontend(eng); test_proxy_scenario_msg_read_from_rx_queue_frontend(eng); +#endif + + sleep(2); + test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); return 0; } \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index 27c7c76..938c747 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -162,7 +162,7 @@ int scenario_msg_inject_frontend(FrontendEngine *engine, } // 7. Check if MemoryAllocMode is valid (covers undefined modes, aligns with document's "shared memory/caller-allocated dual mode") - if (alloc_mode != MEMORY_ALLOC_SHARED && alloc_mode != MEMORY_ALLOC_CALLER) + if (alloc_mode != MEMORY_ALLOC_SHARED && alloc_mode != MEMORY_ALLOC_CALLER && alloc_mode != MEMORY_ALLOC_AMPQUEUE) { error_print("scenario_msg_inject failed: invalid MemoryAllocMode, only MEMORY_ALLOC_SHARED and MEMORY_ALLOC_CALLER are supported"); return FRONTEND_PROXY_PROCESS_ERROR; @@ -442,4 +442,58 @@ int test_proxy_scenario_msg_read_from_rx_queue_frontend(FrontendEngine *engine){ #endif return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Inject a device message into the frontend engine via the HyperAMP queue + * + * @details This function sends a device-related message from the backend to the frontend engine + * by enqueuing it into the HyperAMP TX queue (i.e., the backend-to-frontend communication channel). + * The message is formatted according to the HyperAMP protocol and will be consumed by the + * frontend engine (running in seL4) during its next polling or notification cycle. + * Unlike direct in-process injection, this version relies on cross-environment shared-memory + * messaging through HyperAMP. + * + * @param engine Pointer to the FrontendEngine instance that owns or manages the HyperAMP interface + * @return int Return code indicating the result of the injection attempt: + * - FRONTEND_PROXY_PROCESS_OK: Message successfully enqueued into the HyperAMP TX queue + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to enqueue the message (e.g., queue full, + * uninitialized HyperAMP context, invalid engine state) + * + * @note This function does not wait for the frontend to process the message; it only ensures + * the message is placed in the shared queue. Delivery and handling are asynchronous. + * The actual message content and layout must conform to the agreed-upon HyperAMP device + * message schema between Linux (backend) and seL4 (frontend). + */ +int device_msg_inject_frontend_hyperamp(FrontendEngine *engine){ + GeneralProxyMsgHeader *dev_msg_hdr; + DevMsgReport dev_msg_resp; + int ret, desc_len = 100; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + + dev_msg_hdr = &dev_enable_msg_hdr; + + dev_msg_resp.status = SESS_OP_STATUS_SUCCESS; + dev_msg_resp.error = SESS_OP_CODE_SUCCESS; + dev_msg_resp.data = 0xFF; + + + res_string = res_buf; + desc_string = desc_buf; + + utils_print("In %s, before enter scenario_msg_inject\n", __func__); + ret = scenario_msg_inject_frontend(engine, dev_msg_hdr, &dev_msg_resp, sizeof(dev_msg_resp), MEMORY_ALLOC_AMPQUEUE, res_string, desc_string, desc_len); + + return ret; +} + + +int test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(FrontendEngine *engine){ + int ret; + ret = device_msg_inject_frontend_hyperamp(engine); + return ret; } \ No newline at end of file -- Gitee From bed65e872c7fc310ff1b4c4890b88b42bc52749e Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 13 Feb 2026 16:19:01 +0800 Subject: [PATCH 056/133] fix a bug --- projects/sel4test/apps/front/src/frontend_proto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 990eac9..b95fa35 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -333,7 +333,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h } *result_msg = msg_buf; - }else if(MEMORY_ALLOC_AMPQUEUE == msg_buf){ + }else if(MEMORY_ALLOC_AMPQUEUE == alloc_mode){ if(NULL == engine->hyper_tx_queue){ error_print("build_proxy_general_message failed: HyperAMP TX queue is not initialized!\n"); return FRONTEND_PROXY_PROCESS_ERROR; -- Gitee From a0143c1541f16a4bc5260e633445281de87c4f1a Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 13 Feb 2026 16:37:31 +0800 Subject: [PATCH 057/133] add debug codes --- projects/sel4test/apps/front/src/frontend_proto.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index b95fa35..780a5e4 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -441,6 +441,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h if(MEMORY_ALLOC_AMPQUEUE == alloc_mode){ g_hyper_data_region; msg_buf -= sizeof(ProxyMsgHeader); + utils_print("Before hyperamp_queue_enqueue, the address of the engine->hyper_tx_queue is %p\n", engine->hyper_tx_queue); ret = hyperamp_queue_enqueue(engine->hyper_tx_queue, HYPERAMP_ZONEID_SEL4, msg_buf, payload_len + proxy_msg_payload_len + sizeof(ProxyMsgHeader), g_hyper_data_region); if(HYPERAMP_OK == ret){ @@ -448,7 +449,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h }else if(HYPERAMP_AGAIN == ret){ return FRONTEND_PROXY_PROCESS_AGAIN; }else{ - error_print("build_proxy_general_message failed: faild to push message into the HyperAmp queue!\n"); + error_print("build_proxy_general_message failed: failed to push message into the HyperAmp queue!\n"); return FRONTEND_PROXY_PROCESS_ERROR; } } -- Gitee From 1aec7f9b95805c5ae6c774c97a605f1f1e43d2a9 Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 13 Feb 2026 16:41:34 +0800 Subject: [PATCH 058/133] add test codes --- projects/sel4test/apps/front/src/main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 097767b..c247680 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -38,8 +38,10 @@ int main(void){ test_proxy_scenario_msg_read_from_rx_queue_frontend(eng); #endif - sleep(2); - test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); - + while(1){ + sleep(2); + test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); + } + return 0; } \ No newline at end of file -- Gitee From 46d5bd46f03cb9730a78928c72cc7c0db972ba4f Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 13 Feb 2026 16:56:20 +0800 Subject: [PATCH 059/133] add debug codes --- projects/sel4test/apps/front/src/main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index c247680..cc4dee8 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -38,10 +38,16 @@ int main(void){ test_proxy_scenario_msg_read_from_rx_queue_frontend(eng); #endif + int cnt = 0; while(1){ sleep(2); test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); + + if(cnt > 150){ + break; + } + cnt++; } - + return 0; } \ No newline at end of file -- Gitee From 9500e92c277091796aff9085af31d98c9cdd78ac Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 13 Feb 2026 17:06:24 +0800 Subject: [PATCH 060/133] add debug codes --- projects/sel4test/apps/front/src/hyperamp_shm_queue.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index 01a0306..b824141 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -188,7 +188,10 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, size_t data_len, volatile void *virt_base) { - if (!queue || !data || data_len == 0) return HYPERAMP_ERROR; + if (!queue || !data || data_len == 0) { + printf("hyperamp_queue_enqueue failed: queue =%p, data = %p, data_len = %d\n", queue, data, data_len); + return HYPERAMP_ERROR; + } // Safely read block_size and capacity uint16_t block_size = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, block_size)); @@ -211,6 +214,7 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, // Check if queue is full if (new_header == tail) { + printf("hyperamp_queue_enqueue failed: queue is full!\n"); hyperamp_spinlock_unlock(&queue->queue_lock); return HYPERAMP_ERROR; } @@ -265,7 +269,10 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, size_t *actual_len, volatile void *virt_base) { - if (!queue || !data || max_len == 0) return HYPERAMP_ERROR; + if (!queue || !data || max_len == 0) { + printf("hyperamp_queue_dequeue failed: queue =%p, data = %p, max_len = %d\n", queue, data, max_len); + return HYPERAMP_ERROR; + } /* Invalidate cache before reading to ensure latest data is fetched */ hyperamp_cache_invalidate((volatile void *)queue, 64); -- Gitee From a3cddfe5e0b71d4a60bcb7cdaea6cc50eaa49588 Mon Sep 17 00:00:00 2001 From: xj009 Date: Sat, 14 Feb 2026 11:56:10 +0800 Subject: [PATCH 061/133] debug codes --- .../apps/front/include/hyperamp_shm_queue.h | 1 + .../apps/front/src/hyperamp_shm_queue.c | 67 ++++++++++--------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index 16e26a7..5c70e88 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -10,6 +10,7 @@ #include #include +#include #include "hardware_config.h" /* ==================== Constant Definitions ==================== */ diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index b824141..c02ef24 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -183,10 +183,10 @@ int hyperamp_queue_is_initialized(volatile HyperampShmQueue *queue) * @return HYPERAMP_OK on success, HYPERAMP_ERROR on failure (invalid args, oversized data, or full queue) */ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, - uint32_t zone_id, - const void *data, - size_t data_len, - volatile void *virt_base) + uint32_t zone_id, + const void *data, + size_t data_len, + volatile void *virt_base) { if (!queue || !data || data_len == 0) { printf("hyperamp_queue_enqueue failed: queue =%p, data = %p, data_len = %d\n", queue, data, data_len); @@ -199,39 +199,39 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, if (data_len > block_size) return HYPERAMP_ERROR; - // Acquire lock + // Acquire the lock hyperamp_spinlock_lock(&queue->queue_lock, zone_id); // Safely read header and tail uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); uint16_t tail = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, tail)); - // Compute new header + // Calculate the new header index + printf("In %s, tail = %u, header = %u\n", __func__, tail, header); uint16_t new_header = header + 1; if (new_header >= capacity) { new_header -= capacity; } - // Check if queue is full + // Check if the queue is full if (new_header == tail) { - printf("hyperamp_queue_enqueue failed: queue is full!\n"); hyperamp_spinlock_unlock(&queue->queue_lock); - return HYPERAMP_ERROR; + return HYPERAMP_AGAIN; } - // Compute write address + // Compute the write address uint64_t write_addr = (uint64_t)virt_base + (uint64_t)(header + 1) * block_size; - // Write data + // Write the data hyperamp_safe_memcpy((volatile void *)write_addr, data, data_len); - // Update header (byte-by-byte) + // Update the header (byte-by-byte write for atomicity) volatile uint8_t *p = (volatile uint8_t *)queue; size_t header_offset = offsetof(HyperampShmQueue, header); p[header_offset] = new_header & 0xFF; p[header_offset + 1] = (new_header >> 8) & 0xFF; - // Update enqueue_count (byte-by-byte) + // Update enqueue_count (byte-by-byte write) size_t enqueue_offset = offsetof(HyperampShmQueue, enqueue_count); uint32_t enqueue_count = hyperamp_safe_read_u32(queue, enqueue_offset); enqueue_count++; @@ -243,10 +243,10 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, /* Flush written data to memory */ hyperamp_cache_clean((volatile void *)write_addr, data_len); - /* Flush queue control block to memory */ - hyperamp_cache_clean((volatile void *)queue, 64); /* Only flush first 64 bytes of control fields */ + /* Flush the queue control block to memory */ + hyperamp_cache_clean((volatile void *)queue, 64); /* Only flush the first 64 bytes containing control fields */ - // Release lock + // Release the lock hyperamp_spinlock_unlock(&queue->queue_lock); return HYPERAMP_OK; @@ -263,52 +263,53 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, * @return HYPERAMP_OK on success, HYPERAMP_ERROR on failure (invalid args or empty queue) */ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, - uint32_t zone_id, - void *data, - size_t max_len, - size_t *actual_len, - volatile void *virt_base) + uint32_t zone_id, + void *data, + size_t max_len, + size_t *actual_len, + volatile void *virt_base) { if (!queue || !data || max_len == 0) { printf("hyperamp_queue_dequeue failed: queue =%p, data = %p, max_len = %d\n", queue, data, max_len); return HYPERAMP_ERROR; } - /* Invalidate cache before reading to ensure latest data is fetched */ + /* Invalidate cache before reading to ensure the latest data is fetched */ hyperamp_cache_invalidate((volatile void *)queue, 64); - // Acquire lock + // Acquire the lock hyperamp_spinlock_lock(&queue->queue_lock, zone_id); - // Safely read header, tail, block_size, capacity + // Safely read header, tail, block_size, and capacity uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); uint16_t tail = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, tail)); uint16_t block_size = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, block_size)); uint16_t capacity = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, capacity)); - // Check if queue is empty + // Check if the queue is empty + printf("In %s, tail = %u, header = %u\n", __func__, tail, header); if (tail == header) { hyperamp_spinlock_unlock(&queue->queue_lock); - return HYPERAMP_ERROR; + return HYPERAMP_AGAIN; } - // Compute read address + // Compute the read address uint64_t read_addr = (uint64_t)virt_base + (uint64_t)(tail + 1) * block_size; - /* Invalidate data region cache to ensure fresh data is read */ + /* Invalidate the data region cache to ensure the latest data is read */ hyperamp_cache_invalidate((volatile void *)read_addr, block_size); - // Determine actual read length + // Determine the actual number of bytes to read size_t read_len = (max_len < block_size) ? max_len : block_size; - // Read data + // Read the data hyperamp_safe_memcpy(data, (const volatile void *)read_addr, read_len); if (actual_len) { *actual_len = read_len; } - // Update tail (byte-by-byte) + // Update tail (byte-by-byte write for atomicity) uint16_t new_tail = tail + 1; if (new_tail >= capacity) { new_tail -= capacity; @@ -319,7 +320,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, p[tail_offset] = new_tail & 0xFF; p[tail_offset + 1] = (new_tail >> 8) & 0xFF; - // Update dequeue_count (byte-by-byte) + // Update dequeue_count (byte-by-byte write) size_t dequeue_offset = offsetof(HyperampShmQueue, dequeue_count); uint32_t dequeue_count = hyperamp_safe_read_u32(queue, dequeue_offset); dequeue_count++; @@ -329,7 +330,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, HYPERAMP_BARRIER(); - // Release lock + // Release the lock hyperamp_spinlock_unlock(&queue->queue_lock); return HYPERAMP_OK; -- Gitee From 606a791d77903c72cd821db3ab9f2dd8b43366e0 Mon Sep 17 00:00:00 2001 From: xj009 Date: Sat, 14 Feb 2026 16:05:18 +0800 Subject: [PATCH 062/133] fix imx8mp HyperAMP queue address error --- .../sel4test/apps/front/include/hyperamp_shm_queue.h | 12 +++++++++--- projects/sel4test/apps/front/src/frontend_proto.c | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index 5c70e88..5f8312b 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -103,13 +103,19 @@ typedef enum { // Virtual address: Start of the TX queue in Hyperamp shared memory (seL4 → Linux) -#define SHM_TX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54E000UL) +// #define SHM_TX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54E000UL) +#define SHM_TX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x55F000UL) + // Virtual address: Start of the RX queue in Hyperamp shared memory (Linux → seL4) -#define SHM_RX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54F000UL) +// #define SHM_RX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54F000UL) +#define SHM_RX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x560000UL) + // Virtual address: Start of the general data region in Hyperamp shared memory -#define SHM_DATA_REGION_VA ((volatile void *)0x550000UL) +// #define SHM_DATA_REGION_VA ((volatile void *)0x550000UL) +#define SHM_DATA_REGION_VA ((volatile void *)0x561000UL) + //g_tx_queue = (volatile HyperampShmQueue *)0x54e000; //g_rx_queue = (volatile HyperampShmQueue *)0x54f000; diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 780a5e4..8df0ecd 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -439,14 +439,14 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h * In MEMORY_ALLOC_AMPQUEUE mode, the created message should be pushed into the HyperAMP shared queue. */ if(MEMORY_ALLOC_AMPQUEUE == alloc_mode){ - g_hyper_data_region; msg_buf -= sizeof(ProxyMsgHeader); - utils_print("Before hyperamp_queue_enqueue, the address of the engine->hyper_tx_queue is %p\n", engine->hyper_tx_queue); + utils_print("Before hyperamp_queue_enqueue, the address of the engine->hyper_tx_queue is %p, data region is %p\n", engine->hyper_tx_queue, g_hyper_data_region); ret = hyperamp_queue_enqueue(engine->hyper_tx_queue, HYPERAMP_ZONEID_SEL4, msg_buf, payload_len + proxy_msg_payload_len + sizeof(ProxyMsgHeader), g_hyper_data_region); if(HYPERAMP_OK == ret){ return FRONTEND_PROXY_PROCESS_OK; }else if(HYPERAMP_AGAIN == ret){ + error_print("build_proxy_general_message failed: HyperAMP queue is full!\n"); return FRONTEND_PROXY_PROCESS_AGAIN; }else{ error_print("build_proxy_general_message failed: failed to push message into the HyperAmp queue!\n"); -- Gitee From 466e2c90aeed8e6e781983c6f5f1cb32b66ff7c4 Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 26 Feb 2026 16:32:51 +0800 Subject: [PATCH 063/133] HyperAMP tx queue put operation --- projects/sel4test/apps/front/include/engine.h | 47 +++++ .../apps/front/include/frontend_api.h | 67 +++++++ .../apps/front/include/hyperamp_shm_queue.h | 4 +- .../apps/front/include/senario_test.h | 3 + projects/sel4test/apps/front/src/engine.c | 188 +++++++++++++++++- .../sel4test/apps/front/src/frontend_api.c | 63 ------ .../sel4test/apps/front/src/frontend_proto.c | 4 +- projects/sel4test/apps/front/src/main.c | 3 +- .../sel4test/apps/front/src/senario_test.c | 160 ++++++++++++++- 9 files changed, 466 insertions(+), 73 deletions(-) diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index 38db833..af8cf54 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -94,6 +94,10 @@ typedef struct FrontendEngine_{ struct SharedMemoryPoolQueue *tx_queue; // TX queue volatile HyperampShmQueue *hyper_rx_queue; // Hyper RX queue volatile HyperampShmQueue *hyper_tx_queue; +/* + * Base address of the shared memory region backing HyperAMP queues, mapped into seL4 Linux address space for cross-environment communication. + */ + volatile void *hyper_amp_data_region; struct FrontendEngOps *ops; } FrontendEngine; @@ -122,6 +126,7 @@ extern FrontendEngine *p_g_fr_eng; void frontend_engine_init(); void frontend_engine_run(); +void frontend_engine_run_hyperamp(); void frontend_engine_destory(); FrontendEngine *frontend_get_global_engine(); @@ -321,6 +326,48 @@ uint8_t *frontend_engine_rx_queue_get_msg(struct SharedMemoryPoolQueue *queue, s int frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_len, uint8_t *data, size_t *out_len); + +/** + * @brief Sends a message to the HyperAMP transmit queue managed by the FrontendEngine. + * + * @details This function writes message data from the provided buffer (pointed to by @p data) into the + * HyperAMP TX queue associated with the given FrontendEngine instance. It validates the input + * parameters and message length to ensure compliance with HyperAMP queue constraints, then + * copies the message data to the shared memory region of the TX queue. + * It returns an integer status code to indicate the operation result, and the caller must check + * this code to confirm if the message was successfully enqueued. + * + * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL + * @param[in] msg_len Actual length of the message to send (i.e., the size of valid data in the @p data buffer), + * must be greater than 0 and not exceed the maximum capacity of the HyperAMP TX queue + * @param[in] data Pointer to a uint8_t buffer containing the message data to send; + * cannot be NULL and must hold valid data of length @p msg_len + * + * @return Integer status code indicating the result of the operation: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: System-level error occurred + * - FRONTEND_PROXY_PROCESS_AGAIN: Message temporarily cannot be sent + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is written to the HyperAMP TX queue successfully, and the backend can retrieve it + * via its HyperAMP RX queue + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized, @p data is NULL, + * @p msg_len is 0 or exceeds queue capacity), the message was not enqueued + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message cannot be sent temporarily (e.g., HyperAMP TX queue is full), the caller may retry later, + * no data was written to the queue + * + * @note The message data copied from @p data is stored in shared memory (e.g., @c g_hyper_data_region). + * The ownership of the @p data buffer is held by the caller (who is responsible for allocating/freeing it), + * while the underlying shared memory lifecycle follows the HyperAMP queue protocol. + * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. + * This function is the reverse operation of frontend_engine_hyperamp_rx_queue_get: the former reads from + * the RX queue, while this function writes to the TX queue. + */ +int frontend_engine_hyperamp_tx_queue_put(FrontendEngine *eng, size_t msg_len, + const uint8_t *data); + struct FrontendEngOps *get_hs_frontend_engine_ops(); diff --git a/projects/sel4test/apps/front/include/frontend_api.h b/projects/sel4test/apps/front/include/frontend_api.h index 1f321ef..e725e60 100644 --- a/projects/sel4test/apps/front/include/frontend_api.h +++ b/projects/sel4test/apps/front/include/frontend_api.h @@ -9,6 +9,73 @@ #include "common_utils.h" +/** + * @brief Macro to convert a dotted-decimal IPv4 string to an IPv4Address structure + * @details Parses a string in "xxx.xxx.xxx.xxx" format, validates each octet range (0-255), + * and populates the IPv4Address structure with the binary representation. + * Provides error messages to stderr for invalid formats or out-of-range values. + * + * @param ip_str Input string in dotted-decimal IPv4 format (e.g., "192.168.1.1") + * @param addr_struct Output IPv4Address structure to be populated with parsed values + */ +#define IPV4_STR_TO_ADDR(ip_str, addr_struct) do { \ + unsigned int a, b, c, d; /**< Temporary storage for parsed octet values */ \ + \ + /* Attempt to parse 4 octets from the input string */ \ + if (sscanf(ip_str, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) { \ + \ + /* Validate all octets are within the 0-255 range */ \ + if (a <= 255 && b <= 255 && c <= 255 && d <= 255) { \ + /* Populate the structure with validated octets */ \ + (addr_struct).data[0] = (uint8_t)a; \ + (addr_struct).data[1] = (uint8_t)b; \ + (addr_struct).data[2] = (uint8_t)c; \ + (addr_struct).data[3] = (uint8_t)d; \ + } else { \ + /* Handle octet values outside valid range */ \ + error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 address!"); \ + } \ + } else { \ + /* Handle invalid string format (not matching xxx.xxx.xxx.xxx) */ \ + error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 format!"); \ + } \ +} while(0) + + + + +/** + * @brief Macro to convert an IPv4:port string to an IPv4PortTuple structure + * @details Parses a string in "xxx.xxx.xxx.xxx:port" format, splits it into IP address + * and port components, validates both parts, and populates the IPv4PortTuple. + * Port numbers must be in the range 0-65535. + * + * @param ip_port_str Input string in "xxx.xxx.xxx.xxx:port" format (e.g., "192.168.1.100:8080") + * @param tuple_struct Output IPv4PortTuple structure to be populated with parsed values + */ +#define IPV4_PORT_STR_TO_TUPLE(ip_port_str, tuple_struct) do { \ + char ip_str[16]; /* Buffer to store the IP address part (max IPv4 string length is 15) */ \ + unsigned int port; \ + \ + /* Parse the IP address and port from the input string */ \ + if (sscanf(ip_port_str, "%15[^:]:%u", ip_str, &port) == 2) { \ + \ + /* Convert and validate the IP address part */ \ + IPV4_STR_TO_ADDR(ip_str, (tuple_struct).ipv4_addr); \ + \ + /* Validate the port number (0-65535 range) */ \ + if (port > 65535) { \ + error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid port number: must be 0-65535)!"); \ + } else { \ + (tuple_struct).port = (uint16_t)port; \ + } \ + } else { \ + error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid IP:port format!"); \ + } \ +} while(0) + + + int frontend_eng_command(struct FrontendEngine_ *eng, GeneralProxyMsgHeader *hdr, uint8_t *data, uint32_t size); struct FrontendSession *frontend_sess_new(struct FrontendEngine_ *eng); diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index 5f8312b..4cdbc69 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -22,8 +22,8 @@ #define HYPERAMP_ERROR (-1) #define HYPERAMP_AGAIN 1 -#define HYPERAMP_ZONEID_ROOTLINUX 0 -#define HYPERAMP_ZONEID_SEL4 1 +#define HYPERAMP_ZONE_ID_ROOTLINUX 0 +#define HYPERAMP_ZONE_ID_SEL4 1 /* Memory mapping modes */ diff --git a/projects/sel4test/apps/front/include/senario_test.h b/projects/sel4test/apps/front/include/senario_test.h index 3d94e96..f2eb39a 100644 --- a/projects/sel4test/apps/front/include/senario_test.h +++ b/projects/sel4test/apps/front/include/senario_test.h @@ -34,5 +34,8 @@ int test_proxy_scenario_msg_read_from_rx_queue_frontend(FrontendEngine *engine); int test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(FrontendEngine *engine); int device_msg_inject_frontend_hyperamp(FrontendEngine *engine); +int strategy_msg_inject_frontend_hyperamp(FrontendEngine *engine); +int session_msg_inject_frontend_hyperamp(FrontendEngine *engine); +int data_msg_inject_frontend_hyperamp(FrontendEngine *engine); #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 12426e3..34b66a5 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -182,7 +182,7 @@ int frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_le return FRONTEND_PROXY_PROCESS_ERROR; } - ret = hyperamp_queue_dequeue(eng->hyper_rx_queue, HYPERAMP_ZONEID_SEL4, data, max_msg_len, out_len, g_hyper_data_region); + ret = hyperamp_queue_dequeue(eng->hyper_rx_queue, HYPERAMP_ZONE_ID_SEL4, data, max_msg_len, out_len, g_hyper_data_region); if(HYPERAMP_ERROR == ret){ error_print("frontend_engine_hyperamp_rx_queue_get failed: hyperamp_queue_dequeue execution failed!\n"); @@ -199,6 +199,98 @@ int frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_le } +/** + * @brief Sends a message to the HyperAMP transmit queue managed by the FrontendEngine. + * + * @details This function writes message data from the provided buffer (pointed to by @p data) into the + * HyperAMP TX queue associated with the given FrontendEngine instance. It validates the input + * parameters and message length to ensure compliance with HyperAMP queue constraints, then + * copies the message data to the shared memory region of the TX queue. + * It returns an integer status code to indicate the operation result, and the caller must check + * this code to confirm if the message was successfully enqueued. + * + * @param[in] eng Pointer to the FrontendEngine instance, cannot be NULL + * @param[in] msg_len Actual length of the message to send (i.e., the size of valid data in the @p data buffer), + * must be greater than 0 and not exceed the maximum capacity of the HyperAMP TX queue + * @param[in] data Pointer to a uint8_t buffer containing the message data to send; + * cannot be NULL and must hold valid data of length @p msg_len + * + * @return Integer status code indicating the result of the operation: + * - FRONTEND_PROXY_PROCESS_OK: Operation succeeded + * - FRONTEND_PROXY_PROCESS_ERROR: System-level error occurred + * - FRONTEND_PROXY_PROCESS_AGAIN: Message temporarily cannot be sent + * + * @retval FRONTEND_PROXY_PROCESS_OK + * Message data is written to the HyperAMP TX queue successfully, and the backend can retrieve it + * via its HyperAMP RX queue + * @retval FRONTEND_PROXY_PROCESS_ERROR + * A system-level error occurred (e.g., engine is NULL, internal queue not initialized, @p data is NULL, + * @p msg_len is 0 or exceeds queue capacity), the message was not enqueued + * @retval FRONTEND_PROXY_PROCESS_AGAIN + * Message cannot be sent temporarily (e.g., HyperAMP TX queue is full), the caller may retry later, + * no data was written to the queue + * + * @note The message data copied from @p data is stored in shared memory (e.g., @c g_hyper_data_region). + * The ownership of the @p data buffer is held by the caller (who is responsible for allocating/freeing it), + * while the underlying shared memory lifecycle follows the HyperAMP queue protocol. + * Refer to HyperAMP documentation for detailed memory and lifecycle management rules. + * This function is the reverse operation of frontend_engine_hyperamp_rx_queue_get: the former reads from + * the RX queue, while this function writes to the TX queue. + */ +int frontend_engine_hyperamp_tx_queue_put(FrontendEngine *eng, size_t msg_len, + const uint8_t *data){ + int ret; + + if(NULL == eng){ + error_print("frontend_engine_hyperamp_tx_queue_put failed: eng is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(NULL == eng->hyper_tx_queue){ + error_print("frontend_engine_hyperamp_tx_queue_put failed: eng->hyper_tx_queue is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(msg_len > eng->hyper_tx_queue->block_size){ + error_print("frontend_engine_hyperamp_tx_queue_put failed: message length exceeds queue block size limit!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + if(NULL == eng->hyper_amp_data_region){ + error_print("frontend_engine_hyperamp_tx_queue_put failed: eng->hyper_amp_data_region is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + if(NULL == data){ + error_print("frontend_engine_hyperamp_tx_queue_put failed: data is NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + + utils_print("In %s, the address of hyper_tx_queue is %p\n", __func__, eng->hyper_tx_queue); + + ret = hyperamp_queue_enqueue(eng->hyper_tx_queue, HYPERAMP_ZONE_ID_SEL4, data, msg_len, eng->hyper_amp_data_region); + + if(HYPERAMP_ERROR == ret){ + error_print("frontend_engine_hyperamp_tx_queue_put failed: hyperamp_queue_enqueue execution failed!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + }else if(HYPERAMP_AGAIN == ret){ + error_print("frontend_engine_hyperamp_tx_queue_put failed: queue is empty!\n"); + return FRONTEND_PROXY_PROCESS_AGAIN; + }else{ +/* + * hyperamp_queue_enqueue execution succeeded, no action required. + */ + } + + + return FRONTEND_PROXY_PROCESS_OK; +} + + /** * @brief Retrieves a message from the shared memory pool queue. * @@ -511,8 +603,9 @@ int engine_init_hyperamp_queue(FrontendEngine *eng){ /* Critical: Invalidate cache to ensure subsequent reads reflect the latest data from physical memory */ hyperamp_cache_invalidate((volatile void *)g_hyper_rx_queue, 64); - eng->hyper_tx_queue = g_hyper_tx_queue; - eng->hyper_rx_queue = g_hyper_rx_queue; + eng->hyper_tx_queue = g_hyper_tx_queue; + eng->hyper_rx_queue = g_hyper_rx_queue; + eng->hyper_amp_data_region = g_hyper_data_region; return FRONTEND_PROXY_PROCESS_OK; } @@ -720,4 +813,93 @@ eng_run_step3: } +/** + * @brief Main loop function of the engine (HyperAMP version), handling message processing and data transmission cyclically + * This function executes a continuous loop consisting of four main steps, identical in logic to frontend_engine_run, + * with the key difference being that all inter-environment communication is performed via the HyperAMP queue (instead of + * the standard shared memory queue) to interact with the backend running on Linux. After completing all steps, + * it returns to the first step to implement the core operation of the frontend engine. + * @details The loop process is as follows: + * + * 1. Read data from the RX queue of the HyperAMP queue owned by the FrontendEngine instance, and process + * them sequentially through the frontend proxy protocol stack. + * If any data is read, there are two cases: + * (a) For device messages, strategy messages, and session messages (these are responses from the Linux backend): + * The frontend proxy protocol stack performs corresponding processing for each type, and updates the frontend's + * relevant state based on the message content (the frontend does not generate response packets). + * (b) For sessions that receive data messages: + * These sessions are placed into the backend-to-frontend active queue for processing in step (2). + * If no data message is found, the procedure jumps to step (3). + * + * 2. Access and process session instances in the backend-to-frontend active queue sequentially: + * Sequentially call the application callback function registered by each session in the queue (serving as a data-ready notification). + * After receiving the callback notification, the application will call the dedicated read API to read data from the corresponding session, + * and complete business processing independently based on the read data (the frontend does not involve active data extraction or socket transmission here). + * If the application needs to send data to the Linux backend proxy through the HyperAMP queue, when the data to be sent is added to the send buffer, + * the session will be added to the frontend-to-backend active queue, and these sessions will be processed in STEP (3). + * + * 3. Access and process session instances in the frontend-to-backend active queue sequentially: + * For each session in the queue, read the pending data stored in the session's send buffer (data to be sent to the Linux backend proxy by the application). + * Construct data messages according to the frontend proxy protocol specification, and send the messages to the Linux backend proxy through the HyperAMP's + * TX queue. + * After the data is successfully sent, clear the corresponding pending data in the session's send buffer; if the send buffer becomes empty, remove the session + * from the frontend-to-backend active queue. (This step specifically handles data transmission requests initiated by the application, realizing the frontend- + * -to-backend data proxy through the HyperAMP queue) + * + * After completing all operations in step (3), the loop returns to step (1) to continue the cyclic processing. + */ +void frontend_engine_run_hyperamp(){ + FrontendEngine *eng; + volatile HyperampShmQueue *hyper_rx_queue, hyper_tx_queue; + struct FrontendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *cur_sess, *next_sess; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + uint8_t *proxy_msg; + uint32_t msg_size; + int ret; + + eng = frontend_get_global_engine(); + +/* + * hyper_rx_queue: Local receive queue for HyperAMP communication, which actually maps to the backend's HyperAMP transmit queue (hyper_tx_queue). + * Data sent by the frontend through its hyper_tx_queue will be received by the local side via this hyper_rx_queue. + * + * hyper_tx_queue: Local transmit queue for HyperAMP communication, which is used as the backend's HyperAMP receive queue (hyper_rx_queue). + * Data sent by the local side through this hyper_tx_queue will be received by the backend via its hyper_rx_queue. + */ + if(NULL == eng->hyper_rx_queue || NULL == eng->hyper_tx_queue){ + error_print("frontend_engine_run failed: The global backend engine's RX queue or TX queue has not been initialized!"); + return ; + } + + FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); + FRONTEND_ENGINE_GET_B2F_QUEUE(eng, active_queue_b2f); + + if(NULL == active_queue_f2b || NULL == active_queue_b2f){ + error_print("frontend_engine_run failed: The global frontend engine's f2b session queue or b2f session queue has not been initialized!"); + return ; + } + + if(NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("frontend_engine_run failed: Global frontend engine's session pool (sess_pool) or its operation set (ops) is not initialized!"); + return ; + } + + sess_pool = eng->sess_pool; + sess_pool_ops = sess_pool->ops; + + do{ +/* + * STEP (1). + */ +eng_run_step1: + 1; + + + + }while(1); + +} + void frontend_engine_destory(){} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/frontend_api.c b/projects/sel4test/apps/front/src/frontend_api.c index b2d94d5..28a4089 100644 --- a/projects/sel4test/apps/front/src/frontend_api.c +++ b/projects/sel4test/apps/front/src/frontend_api.c @@ -1,68 +1,5 @@ #include "frontend_api.h" -/** - * @brief Macro to convert a dotted-decimal IPv4 string to an IPv4Address structure - * @details Parses a string in "xxx.xxx.xxx.xxx" format, validates each octet range (0-255), - * and populates the IPv4Address structure with the binary representation. - * Provides error messages to stderr for invalid formats or out-of-range values. - * - * @param ip_str Input string in dotted-decimal IPv4 format (e.g., "192.168.1.1") - * @param addr_struct Output IPv4Address structure to be populated with parsed values - */ -#define IPV4_STR_TO_ADDR(ip_str, addr_struct) do { \ - unsigned int a, b, c, d; /**< Temporary storage for parsed octet values */ \ - \ - /* Attempt to parse 4 octets from the input string */ \ - if (sscanf(ip_str, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) { \ - \ - /* Validate all octets are within the 0-255 range */ \ - if (a <= 255 && b <= 255 && c <= 255 && d <= 255) { \ - /* Populate the structure with validated octets */ \ - (addr_struct).data[0] = (uint8_t)a; \ - (addr_struct).data[1] = (uint8_t)b; \ - (addr_struct).data[2] = (uint8_t)c; \ - (addr_struct).data[3] = (uint8_t)d; \ - } else { \ - /* Handle octet values outside valid range */ \ - error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 address!"); \ - } \ - } else { \ - /* Handle invalid string format (not matching xxx.xxx.xxx.xxx) */ \ - error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 format!"); \ - } \ -} while(0) - - - -/** - * @brief Macro to convert an IPv4:port string to an IPv4PortTuple structure - * @details Parses a string in "xxx.xxx.xxx.xxx:port" format, splits it into IP address - * and port components, validates both parts, and populates the IPv4PortTuple. - * Port numbers must be in the range 0-65535. - * - * @param ip_port_str Input string in "xxx.xxx.xxx.xxx:port" format (e.g., "192.168.1.100:8080") - * @param tuple_struct Output IPv4PortTuple structure to be populated with parsed values - */ -#define IPV4_PORT_STR_TO_TUPLE(ip_port_str, tuple_struct) do { \ - char ip_str[16]; /* Buffer to store the IP address part (max IPv4 string length is 15) */ \ - unsigned int port; \ - \ - /* Parse the IP address and port from the input string */ \ - if (sscanf(ip_port_str, "%15[^:]:%u", ip_str, &port) == 2) { \ - \ - /* Convert and validate the IP address part */ \ - IPV4_STR_TO_ADDR(ip_str, (tuple_struct).ipv4_addr); \ - \ - /* Validate the port number (0-65535 range) */ \ - if (port > 65535) { \ - error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid port number: must be 0-65535)!"); \ - } else { \ - (tuple_struct).port = (uint16_t)port; \ - } \ - } else { \ - error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid IP:port format!"); \ - } \ -} while(0) /** diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 8df0ecd..247e356 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -393,7 +393,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h if(FRONTEND_PROXY_PROCESS_OK != ret){ error_print("build_proxy_general_message failed: failed to build proxy message!\n"); // free_shared_mem(engine->mem_pool, mem_addr); - SHM_POOL_QUEUE_HEAD_ROLLBACK(ring_buf); +// SHM_POOL_QUEUE_HEAD_ROLLBACK(ring_buf); return FRONTEND_PROXY_PROCESS_ERROR; } @@ -441,7 +441,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h if(MEMORY_ALLOC_AMPQUEUE == alloc_mode){ msg_buf -= sizeof(ProxyMsgHeader); utils_print("Before hyperamp_queue_enqueue, the address of the engine->hyper_tx_queue is %p, data region is %p\n", engine->hyper_tx_queue, g_hyper_data_region); - ret = hyperamp_queue_enqueue(engine->hyper_tx_queue, HYPERAMP_ZONEID_SEL4, msg_buf, payload_len + proxy_msg_payload_len + sizeof(ProxyMsgHeader), g_hyper_data_region); + ret = hyperamp_queue_enqueue(engine->hyper_tx_queue, HYPERAMP_ZONE_ID_SEL4, msg_buf, payload_len + proxy_msg_payload_len + sizeof(ProxyMsgHeader), g_hyper_data_region); if(HYPERAMP_OK == ret){ return FRONTEND_PROXY_PROCESS_OK; diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index cc4dee8..64e0390 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -30,6 +30,7 @@ int main(void){ eng = frontend_get_global_engine(); + test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); #if 0 sess = frontend_sess_new(eng); ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.100:8080"); @@ -41,7 +42,7 @@ int main(void){ int cnt = 0; while(1){ sleep(2); - test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); + if(cnt > 150){ break; diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index 938c747..66e97a8 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -1,4 +1,5 @@ #include "senario_test.h" +#include "frontend_api.h" /* * GeneralProxyMsgHeader dev_enable_msg_hdr, strgy_query_msg_hdr, sess_create_msg_hdr, data_msg_hdr; @@ -468,7 +469,8 @@ int test_proxy_scenario_msg_read_from_rx_queue_frontend(FrontendEngine *engine){ */ int device_msg_inject_frontend_hyperamp(FrontendEngine *engine){ GeneralProxyMsgHeader *dev_msg_hdr; - DevMsgReport dev_msg_resp; +// DevMsgReport dev_msg_resp; + DevMsgMask dev_msg_mask; int ret, desc_len = 100; uint8_t **res_string; char *desc_string; @@ -477,23 +479,177 @@ int device_msg_inject_frontend_hyperamp(FrontendEngine *engine){ dev_msg_hdr = &dev_enable_msg_hdr; +#if 0 dev_msg_resp.status = SESS_OP_STATUS_SUCCESS; dev_msg_resp.error = SESS_OP_CODE_SUCCESS; dev_msg_resp.data = 0xFF; +#endif + + dev_msg_mask.data = 0xFF; res_string = res_buf; desc_string = desc_buf; utils_print("In %s, before enter scenario_msg_inject\n", __func__); - ret = scenario_msg_inject_frontend(engine, dev_msg_hdr, &dev_msg_resp, sizeof(dev_msg_resp), MEMORY_ALLOC_AMPQUEUE, res_string, desc_string, desc_len); + ret = scenario_msg_inject_frontend(engine, dev_msg_hdr, &dev_msg_mask, sizeof(dev_msg_mask), MEMORY_ALLOC_AMPQUEUE, res_string, desc_string, desc_len); + + return ret; +} + + +/** + * @brief Inject a strategy message into the frontend engine via the HyperAMP queue + * + * @details This function sends a strategy-related message from the backend to the frontend engine + * by enqueuing it into the HyperAMP TX queue (i.e., the backend-to-frontend communication channel). + * The message is formatted according to the HyperAMP protocol and will be consumed by the + * frontend engine (running in seL4) during its next polling or notification cycle. + * Unlike direct in-process injection, this version relies on cross-environment shared-memory + * messaging through HyperAMP. + * + * @param engine Pointer to the FrontendEngine instance that owns or manages the HyperAMP interface + * @return int Return code indicating the result of the injection attempt: + * - FRONTEND_PROXY_PROCESS_OK: Message successfully enqueued into the HyperAMP TX queue + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to enqueue the message (e.g., queue full, + * uninitialized HyperAMP context, invalid engine state) + * + * @note This function does not wait for the frontend to process the message; it only ensures + * the message is placed in the shared queue. Delivery and handling are asynchronous. + * The actual message content and layout must conform to the agreed-upon HyperAMP strategy + * message schema between Linux (backend) and seL4 (frontend). + */ +int strategy_msg_inject_frontend_hyperamp(FrontendEngine *engine){ + GeneralProxyMsgHeader *strgy_msg_hdr; + StrgyCMDEnableMessage strgy; + int ret, desc_len = 100; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + + strgy_msg_hdr = &strgy_set_msg_hdr; + strgy.strgy_para = 0; + + res_string = res_buf; + desc_string = desc_buf; + + ret = scenario_msg_inject_frontend(engine, strgy_msg_hdr, &strgy, sizeof(strgy), MEMORY_ALLOC_AMPQUEUE, res_string, desc_string, desc_len); + + return ret; +} + + +/** + * @brief Inject a session message into the frontend engine via the HyperAMP queue + * + * @details This function sends a session-related message from the backend to the frontend engine + * by enqueuing it into the HyperAMP TX queue (i.e., the backend-to-frontend communication channel). + * The message is formatted according to the HyperAMP protocol and will be consumed by the + * frontend engine (running in seL4) during its next polling or notification cycle. + * Unlike direct in-process injection, this version relies on cross-environment shared-memory + * messaging through HyperAMP. + * + * @param engine Pointer to the FrontendEngine instance that owns or manages the HyperAMP interface + * @return int Return code indicating the result of the injection attempt: + * - FRONTEND_PROXY_PROCESS_OK: Message successfully enqueued into the HyperAMP TX queue + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to enqueue the message (e.g., queue full, + * uninitialized HyperAMP context, invalid engine state) + * + * @note This function does not wait for the frontend to process the message; it only ensures + * the message is placed in the shared queue. Delivery and handling are asynchronous. + * The actual message content and layout must conform to the agreed-upon HyperAMP session + * message schema between Linux (backend) and seL4 (frontend). + */ +int session_msg_inject_frontend_hyperamp(FrontendEngine *engine){ + GeneralProxyMsgHeader *sess_msg_hdr; + SessIPv4Params sess_ipv4_paras; + int ret, desc_len = 100; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + char *ip_port_string = "192.168.1.101:8888"; + + sess_msg_hdr = &sess_create_msg_hdr; + + sess_ipv4_paras.transport_layer_proto = SESS_UDP_PROTO; + sess_ipv4_paras.device_selection = 0xFF; + IPV4_PORT_STR_TO_TUPLE(ip_port_string, sess_ipv4_paras.dest_endpoint); + + res_string = res_buf; + desc_string = desc_buf; + + utils_print("In %s, version = %d, type = %d\n", __func__, sess_msg_hdr->outer_header.version, sess_msg_hdr->outer_header.proxy_msg_type); + + ret = scenario_msg_inject_frontend(engine, sess_msg_hdr, &sess_ipv4_paras, sizeof(sess_ipv4_paras), MEMORY_ALLOC_AMPQUEUE, res_string, desc_string, desc_len); return ret; } +/** + * @brief Inject a data message into the frontend engine via the HyperAMP queue + * + * @details This function sends a data-related message from the backend to the frontend engine + * by enqueuing it into the HyperAMP TX queue (i.e., the backend-to-frontend communication channel). + * The message is formatted according to the HyperAMP protocol and will be consumed by the + * frontend engine (running in seL4) during its next polling or notification cycle. + * Unlike direct in-process injection, this version relies on cross-environment shared-memory + * messaging through HyperAMP. + * + * @param engine Pointer to the FrontendEngine instance that owns or manages the HyperAMP interface + * @return int Return code indicating the result of the injection attempt: + * - FRONTEND_PROXY_PROCESS_OK: Message successfully enqueued into the HyperAMP TX queue + * - FRONTEND_PROXY_PROCESS_ERROR: Failed to enqueue the message (e.g., queue full, + * uninitialized HyperAMP context, invalid engine state) + * + * @note This function does not wait for the frontend to process the message; it only ensures + * the message is placed in the shared queue. Delivery and handling are asynchronous. + * The actual message content and layout must conform to the agreed-upon HyperAMP data + * message schema between Linux (backend) and seL4 (frontend). + */ +int data_msg_inject_frontend_hyperamp(FrontendEngine *engine){ + GeneralProxyMsgHeader *data_msg_hdr; + uint8_t **res_string; + char *desc_string; + uint8_t *res_buf[100] = {NULL}; + char desc_buf[100] = {0}; + char data_buf[100]; + int ret, desc_len = 100; + + + + memset(data_buf, 0, sizeof(data_buf)); + snprintf(data_buf, sizeof(data_buf), "test msg"); + + utils_print("strlen(test msg) = %d\n", strlen("test msg")); + utils_print("content of data_buf = %s\n", data_buf); + + data_msg_hdr = &proxy_data_msg_hdr; + res_string = res_buf; + desc_string = desc_buf; + + data_msg_hdr->outer_header.payload_len = strlen("test msg"); + + utils_print("outer_header.payload_len = %d\n", data_msg_hdr->outer_header.payload_len); + + utils_print("In %s, version = %d, type = %d\n", __func__, data_msg_hdr->outer_header.version, data_msg_hdr->outer_header.proxy_msg_type); + + DUMP_BUFFER_CONTENT(data_buf, 8, "%c"); + + ret = scenario_msg_inject_frontend(engine, data_msg_hdr, &data_buf, strlen(data_buf), MEMORY_ALLOC_AMPQUEUE, res_string, desc_string, desc_len); + + return FRONTEND_PROXY_PROCESS_OK; +} + + int test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(FrontendEngine *engine){ int ret; ret = device_msg_inject_frontend_hyperamp(engine); + ret = strategy_msg_inject_frontend_hyperamp(engine); + ret = session_msg_inject_frontend_hyperamp(engine); + ret = data_msg_inject_frontend_hyperamp(engine); + return ret; } \ No newline at end of file -- Gitee From 5752adceabbb7625533ce404b3d871a0b9a5dc9d Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 26 Feb 2026 17:52:23 +0800 Subject: [PATCH 064/133] renew frontend engine run (HyperAMP version) --- projects/sel4test/apps/front/src/engine.c | 104 ++++++++++++++++++++-- projects/sel4test/apps/front/src/main.c | 14 ++- 2 files changed, 111 insertions(+), 7 deletions(-) diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 34b66a5..6caa527 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -857,17 +857,24 @@ void frontend_engine_run_hyperamp(){ struct FrontendSessionPoolOps *sess_pool_ops; uint8_t *proxy_msg; uint32_t msg_size; - int ret; + int ret, block_size; + uint8_t msg_buf[HYPERAMP_MSG_HDR_PLUS_MAX_SIZE]; eng = frontend_get_global_engine(); /* - * hyper_rx_queue: Local receive queue for HyperAMP communication, which actually maps to the backend's HyperAMP transmit queue (hyper_tx_queue). - * Data sent by the frontend through its hyper_tx_queue will be received by the local side via this hyper_rx_queue. + * hyper_rx_queue: HyperAMP receive queue instance for cross-OS shared memory communication. + * This local receive queue maps to the front-end's HyperAMP transmit queue (hyper_tx_queue). + * Data sent by the front-end through its hyper_tx_queue is received locally via this hyper_rx_queue. + * + * hyper_tx_queue: HyperAMP transmit queue instance for cross-OS shared memory communication. + * This local transmit queue serves as the front-end's HyperAMP receive queue (hyper_rx_queue). + * Data sent locally through this hyper_tx_queue is received by the front-end via its hyper_rx_queue. * - * hyper_tx_queue: Local transmit queue for HyperAMP communication, which is used as the backend's HyperAMP receive queue (hyper_rx_queue). - * Data sent by the local side through this hyper_tx_queue will be received by the backend via its hyper_rx_queue. + * hyper_amp_data_region: The memory region where cross-OS shared memory data is stored, + * which is the underlying storage for data transmitted via HyperAMP queues. */ + if(NULL == eng->hyper_rx_queue || NULL == eng->hyper_tx_queue){ error_print("frontend_engine_run failed: The global backend engine's RX queue or TX queue has not been initialized!"); return ; @@ -894,9 +901,94 @@ void frontend_engine_run_hyperamp(){ * STEP (1). */ eng_run_step1: - 1; + do{ + ret = frontend_engine_hyperamp_rx_queue_get(eng, HYPERAMP_MSG_HDR_PLUS_MAX_SIZE, msg_buf, &block_size); + + /* + * If returning FRONTEND_PROXY_PROCESS_ERROR, it indicates a system-level error (e.g., invalid queue handle, shared memory access exception, etc.) + * Processing cannot continue; print error message and return directly. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_engine_run failed: failed to get data from RX queue!"); + return; + } + + /* + * If returning FRONTEND_PROXY_PROCESS_AGAIN, it indicates temporary inability to retrieve data (e.g., empty queue, resource temporarily occupied, etc., non-error state) + * No error reporting needed; jump to eng_run_step2 to execute the next process. + */ + if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + goto eng_run_step2; + } + + /* + * Process the proxy message. + */ + frontend_proxy_msg_process(proxy_msg); + }while(FRONTEND_PROXY_PROCESS_OK == ret); +eng_run_step2: +/* + * Recall the FRONTEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (1) procedure may renew the back-to-front queue (queue_b2f) of the session pool. + */ + FRONTEND_ENGINE_GET_B2F_QUEUE(eng, active_queue_b2f); + + TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess){ + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_RECVDATA); +/* + * queue_b2f: Session queue for sessions with pending data to be received by the frontend from the backend. + * (Pending data refers to data sent from the backend to the frontend, which the frontend needs to read.) + * If the receive queue (msg_b2f) is empty, the session instance should be removed from the queue_b2f list. + */ + if(TAILQ_EMPTY(&cur_sess->msg_b2f)){ + TAILQ_REMOVE(active_queue_b2f, cur_sess, entries_b2f); + cur_sess->state_b2f &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } + } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess) + + + +eng_run_step3: +/* + * Recall the BACKEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (2) procedure may renew the front-to-back queue (queue_f2b) of the session pool. + */ + FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); + + TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess){ + +/* + * Call the corresponding data_process_f2b function pointer in the session pool's operation set (sess_pool_ops), which attempts to send the frontend-to-backend data maintained by the + * current session (cur_sess) via the shared memory's TX queue. + * The return value corresponds to three scenarios: + * Returns FRONTEND_PROXY_PROCESS_OK: All data has been sent successfully. + * Returns FRONTEND_PROXY_PROCESS_AGAIN: Not all data has been sent, and no errors occurred. + * Returns FRONTEND_PROXY_PROCESS_ERROR: An error occurred during the sending process. + */ + ret = sess_pool_ops->data_process_f2b(cur_sess); +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_OK, this indicates all message segments in the front-to-back (F2B) message queue have been sent via the shared memory's TX queue. + * Such sessions should be detached from the F2B active queue, and their "linked to queue" state flag should be cleared. + */ + if(FRONTEND_PROXY_PROCESS_OK == ret){ + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->state_f2b &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_ERROR, it indicates an error occurred when attempting to send data via the shared memory's TX queue. + * For such sessions, they should be removed from the frontend-to-backend active queue (active_queue_f2b), and the session's abnormal event callback should be triggered. + * + * Note that the recovery of resources occupied by the session needs to be completed during the processing of the FRONTEND_SESS_EVENT_ABNOMAL event, and this responsibility + * lies with the application. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNOMAL); + } +/* + * When not all data has been sent and no errors have occurred, no action is required. + */ + } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess) }while(1); diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 64e0390..3d253bc 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -16,7 +16,9 @@ int main(void){ printf("Running tests\n"); FrontendEngine *eng; struct FrontendSession *sess; - int ret; + ProxyMsgHeader *proxy_msg_hdr; + int ret, block_size; + uint8_t msg_buf[HYPERAMP_MSG_HDR_PLUS_MAX_SIZE]; #if 0 seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); unsigned long long *vaddrs=(unsigned long long *)*Ptr2ShmemCommBuff; @@ -43,6 +45,16 @@ int main(void){ while(1){ sleep(2); + ret = frontend_engine_hyperamp_rx_queue_get(eng, HYPERAMP_MSG_HDR_PLUS_MAX_SIZE, msg_buf, &block_size); + + if(FRONTEND_PROXY_PROCESS_OK == ret){ + proxy_msg_hdr = (ProxyMsgHeader *)msg_buf; + utils_print("version = %d, msg type = %d, frontend ID = %d, backend ID = %d, msg len = %d\n", + proxy_msg_hdr->version, proxy_msg_hdr->proxy_msg_type, proxy_msg_hdr->frontend_sess_id, proxy_msg_hdr->backend_sess_id, proxy_msg_hdr->payload_len); + + frontend_proxy_msg_process(msg_buf); + + } if(cnt > 150){ break; -- Gitee From 47a321aba549e4e6e8eb9c3363e08eb013042495 Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 27 Feb 2026 10:22:46 +0800 Subject: [PATCH 065/133] add test codes --- projects/sel4test/apps/front/src/main.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 3d253bc..8b6d06c 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "frontend_api.h" #include "senario_test.h" @@ -43,7 +44,8 @@ int main(void){ int cnt = 0; while(1){ - sleep(2); +// sleep(2); + ps_sdelay(2); ret = frontend_engine_hyperamp_rx_queue_get(eng, HYPERAMP_MSG_HDR_PLUS_MAX_SIZE, msg_buf, &block_size); @@ -54,9 +56,13 @@ int main(void){ frontend_proxy_msg_process(msg_buf); + }else if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + utils_print("In frontend, HyperAMP RX queue is empty!\n"); + }else{ + utils_print("Failed to get message in HyperAMP RX queue!\n"); } - if(cnt > 150){ + if(cnt > 100){ break; } cnt++; -- Gitee From 792f48770c2dd3a5f42b46daaccadf1731921815 Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 27 Feb 2026 16:00:26 +0800 Subject: [PATCH 066/133] fix some bugs --- projects/sel4test/apps/front/include/message.h | 4 ++-- .../sel4test/apps/front/src/frontend_proto.c | 2 ++ .../sel4test/apps/front/src/senario_test.c | 18 +++++++++--------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index 3bb0465..1e1da6d 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -319,7 +319,7 @@ typedef enum { ( \ /* Determine length for SET message based on action type */ \ (action_type == ACTION_TYPE_COMMAND) ? 2 : /* SET command → 2 bytes */ \ - (action_type == ACTION_TYPE_RESPONSE) ? 2 : /* SET response → 2 bytes */ \ + (action_type == ACTION_TYPE_RESPONSE) ? 4 : /* SET response → 4 bytes */ \ PROXY_MSG_INVALID_LEN /* Invalid action type for SET message */ \ ) : \ (strgy_msg_type == STRGY_MSG_QUERY) ? \ @@ -338,7 +338,7 @@ typedef enum { * euses STRGY_MSG_PAYLOAD_LEN to avoid duplicate logic */ #define STRGY_MSG_HEADER_PAYLOAD_LEN(header) \ - DEV_MSG_PAYLOAD_LEN((header)->msg_type, (header)->action_type) + STRGY_MSG_PAYLOAD_LEN((header)->msg_type, (header)->action_type) /** diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 247e356..b91fe6a 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -144,6 +144,8 @@ int build_proxy_sess_message(SessMsgHeader *sess_hdr, const uint8_t *payload, si corr_len = SESS_MSG_HEADER_PAYLOAD_LEN(sess_hdr); + utils_print("In func %s, corr_len = %d, payload_len - %d\n", __func__, corr_len, payload_len); + if(payload_len != corr_len){ error_print("build_proxy_sess_message failed: payload length does not match expected value based on message type, action type and IP version!"); return FRONTEND_PROXY_PROCESS_ERROR; diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index 66e97a8..ff49f63 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -10,14 +10,14 @@ GeneralProxyMsgHeader dev_enable_msg_hdr = { .proxy_msg_type = PROXY_MSG_TYPE_DEV, // Proxy message type: Device message (0) .frontend_sess_id = FRONTEND_ADMIN_SESSION_ID, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy .backend_sess_id = BACKEND_ADMIN_SESSION_ID, // Backend admin session ID, used to match backend-backend sessions in frontend proxy - .payload_len = sizeof(DevMsgHeader) + sizeof(DevMsgReport) // Payload length, set according to actual payload + .payload_len = sizeof(DevMsgHeader) + sizeof(DevMsgMask) // Payload length, set according to actual payload }, .inner_header.dev_hdr = { // Use device message inner header .version = PROXY_PROTO_DEV_VERSION_1, // Protocol version, fixed to 1 .msg_type = DEV_MSG_ENABLE, // Message type: Enable (1) .msg_id = 0, // Message ID, used for command-response matching - .action_type = ACTION_TYPE_RESPONSE, // Signaling type: Response (1) - .payload_len = sizeof(DevMsgReport) // Payload length, set according to actual payload + .action_type = ACTION_TYPE_COMMAND, // Signaling type: Response (1) + .payload_len = sizeof(DevMsgMask) // Payload length, set according to actual payload } }; @@ -29,14 +29,14 @@ GeneralProxyMsgHeader strgy_set_msg_hdr = { .proxy_msg_type = PROXY_MSG_TYPE_STRGY, // Proxy message type: Strategy message (1) .frontend_sess_id = FRONTEND_ADMIN_SESSION_ID, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy .backend_sess_id = BACKEND_ADMIN_SESSION_ID, // Backend admin session ID, used to match backend-backend sessions in frontend proxy - .payload_len = sizeof(StrgyMsgHeader) + sizeof(StrgyMsgReport) // Payload length, set according to actual payload + .payload_len = sizeof(StrgyMsgHeader) + sizeof(StrgyCMDEnableMessage) // Payload length, set according to actual payload }, .inner_header.strgy_hdr = { // Use strategy message inner header .version = PROXY_PROTO_STRGY_VERSION_1, // Protocol version, fixed to 1 .msg_type = STRGY_MSG_SET, // Message type: Set (0) .msg_id = 0, // Message ID, used for command-response matching - .action_type = ACTION_TYPE_RESPONSE, // Signaling type: Response (0) - .payload_len = sizeof(StrgyMsgReport) // Payload length, set according to actual payload + .action_type = ACTION_TYPE_COMMAND, // Signaling type: Response (0) + .payload_len = sizeof(StrgyCMDEnableMessage) // Payload length, set according to actual payload } }; @@ -48,14 +48,14 @@ GeneralProxyMsgHeader sess_create_msg_hdr = { .proxy_msg_type = PROXY_MSG_TYPE_SESS, // Proxy message type: Session message (2) .frontend_sess_id = 1, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy .backend_sess_id = 1, // Backend admin session ID, used to match backend-backend sessions in backend proxy - .payload_len = sizeof(SessMsgHeader) + sizeof(SessOpRespData) // Payload length, set according to actual payload + .payload_len = sizeof(SessMsgHeader) + sizeof(SessIPv4Params) // Payload length, set according to actual payload }, .inner_header.sess_hdr = { // Use session message inner header .version = PROXY_PROTO_SESS_VERSION_1, // Protocol version, fixed to 1 .msg_type = SESS_MSG_CREATE, // Message type: Create (1) - .action_type = ACTION_TYPE_RESPONSE, // Signaling type: RESPONSE (1) + .action_type = ACTION_TYPE_COMMAND, // Signaling type: RESPONSE (1) .ip_version = SESS_IPV4_PROTO, // IP version: IPv4 (4), can be changed to IPv4 (6) if needed - .payload_len = sizeof(SessOpRespData) // Payload length, set according to actual payload + .payload_len = sizeof(SessIPv4Params) // Payload length, set according to actual payload } }; -- Gitee From 026510311de492431d39a91603b880b2d6bc70b4 Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 27 Feb 2026 16:12:38 +0800 Subject: [PATCH 067/133] adding test codes --- projects/sel4test/apps/front/src/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 8b6d06c..3e0a50b 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -31,7 +31,7 @@ int main(void){ #endif frontend_engine_init(); - eng = frontend_get_global_engine(); + eng = frontend_get_global_engine(); test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); #if 0 @@ -53,7 +53,6 @@ int main(void){ proxy_msg_hdr = (ProxyMsgHeader *)msg_buf; utils_print("version = %d, msg type = %d, frontend ID = %d, backend ID = %d, msg len = %d\n", proxy_msg_hdr->version, proxy_msg_hdr->proxy_msg_type, proxy_msg_hdr->frontend_sess_id, proxy_msg_hdr->backend_sess_id, proxy_msg_hdr->payload_len); - frontend_proxy_msg_process(msg_buf); }else if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ @@ -65,6 +64,9 @@ int main(void){ if(cnt > 100){ break; } + + data_msg_inject_frontend_hyperamp(eng); + cnt++; } -- Gitee From c5e2a70c2c4a8a4c0f89d6e0a3584b34942bf4c4 Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 27 Feb 2026 17:26:37 +0800 Subject: [PATCH 068/133] fix a bug --- projects/sel4test/apps/front/src/frontend_proto.c | 2 +- projects/sel4test/apps/front/src/senario_test.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index b91fe6a..d2ceab7 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -144,7 +144,7 @@ int build_proxy_sess_message(SessMsgHeader *sess_hdr, const uint8_t *payload, si corr_len = SESS_MSG_HEADER_PAYLOAD_LEN(sess_hdr); - utils_print("In func %s, corr_len = %d, payload_len - %d\n", __func__, corr_len, payload_len); + utils_print("In func %s, corr_len = %d, payload_len = %d\n", __func__, corr_len, payload_len); if(payload_len != corr_len){ error_print("build_proxy_sess_message failed: payload length does not match expected value based on message type, action type and IP version!"); diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index ff49f63..c85e646 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -47,7 +47,7 @@ GeneralProxyMsgHeader sess_create_msg_hdr = { .version = PROXY_PROTO_VERSION_1, // Protocol version, fixed to 1 as specified .proxy_msg_type = PROXY_MSG_TYPE_SESS, // Proxy message type: Session message (2) .frontend_sess_id = 1, // Frontend admin session ID, used to match frontend-backend sessions in frontend proxy - .backend_sess_id = 1, // Backend admin session ID, used to match backend-backend sessions in backend proxy + .backend_sess_id = BACKEND_HANDOVER_SESSION_ID, // Backend admin session ID, used to match backend-backend sessions in backend proxy .payload_len = sizeof(SessMsgHeader) + sizeof(SessIPv4Params) // Payload length, set according to actual payload }, .inner_header.sess_hdr = { // Use session message inner header -- Gitee From f6f053610830743c3a6721b67e04dc96599854ef Mon Sep 17 00:00:00 2001 From: xj009 Date: Sat, 28 Feb 2026 16:07:00 +0800 Subject: [PATCH 069/133] add test codes --- projects/sel4test/apps/front/src/frontend_proto.c | 7 ++++--- projects/sel4test/apps/front/src/main.c | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index d2ceab7..b435aad 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -795,6 +795,7 @@ int frontend_proxy_sess_msg_process(uint16_t frontend_sess_id, uint16_t backend_ sess_msg_hdr = (SessMsgHeader *)msg; + utils_print("In %s\n", __func__); if(NULL == sess_msg_hdr){ error_print("backend_proxy_sess_msg_process() failed: the msg pointer is NULL!\n"); @@ -808,9 +809,8 @@ int frontend_proxy_sess_msg_process(uint16_t frontend_sess_id, uint16_t backend_ payload_len = sess_msg_hdr->payload_len; msg_data = msg + sizeof(SessMsgHeader); - utils_print("In %s\n", __func__); - utils_print("In %s, version = %d, msg_type = %d, action type = %d, ip version = %d, payload len = %d, address = %p\n", - __func__, version, msg_type, action_type, ip_version, payload_len, &sess_msg_hdr); + utils_print("version = %d, msg_type = %d, action type = %d, ip version = %d, payload len = %d, address = %p\n", + version, msg_type, action_type, ip_version, payload_len, &sess_msg_hdr); #if 0 @@ -937,6 +937,7 @@ int __frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_ bool ip_ver_valid = true; int ret; + utils_print("In %s\n", __func__); /* * The main body of the session creation procedure lies in the function which the create_sess_step2 pointer points to. * In __frontend_proxy_sess_msg_process_active_create_ver1, this function parses the session parameters and calls the function pointed to by the create_sess_step2 pointer to establish a new session. diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 3e0a50b..d0e09f3 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -53,6 +53,9 @@ int main(void){ proxy_msg_hdr = (ProxyMsgHeader *)msg_buf; utils_print("version = %d, msg type = %d, frontend ID = %d, backend ID = %d, msg len = %d\n", proxy_msg_hdr->version, proxy_msg_hdr->proxy_msg_type, proxy_msg_hdr->frontend_sess_id, proxy_msg_hdr->backend_sess_id, proxy_msg_hdr->payload_len); + + utils_print("Before enter frontend_proxy_msg_process, the content of the proxy message is:\n"); + DUMP_BUFFER_CONTENT(msg_buf, proxy_msg_hdr->payload_len + sizeof(ProxyMsgHeader), "%02x "); frontend_proxy_msg_process(msg_buf); }else if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ @@ -61,7 +64,7 @@ int main(void){ utils_print("Failed to get message in HyperAMP RX queue!\n"); } - if(cnt > 100){ + if(cnt > 50){ break; } -- Gitee From c99b5d4ab21110c9b05696f9551f1273367254fb Mon Sep 17 00:00:00 2001 From: xj009 Date: Sat, 28 Feb 2026 18:13:33 +0800 Subject: [PATCH 070/133] add debug codes --- .../apps/front/include/common_utils.h | 49 +++++++++++++++++++ .../apps/front/src/hyperamp_shm_queue.c | 9 +++- projects/sel4test/apps/front/src/main.c | 2 + 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/include/common_utils.h b/projects/sel4test/apps/front/include/common_utils.h index f833833..45b54ac 100644 --- a/projects/sel4test/apps/front/include/common_utils.h +++ b/projects/sel4test/apps/front/include/common_utils.h @@ -4,6 +4,7 @@ #include #include #include +#include "message.h" #define UTILS_ENABLE_DEBUG 1 @@ -48,4 +49,52 @@ int debug_print(const char *format, ...); printf("\n"); \ } while (0) + +/** + * @def DUMP_PROXY_MSG_HEADER(BUF_PTR) + * @brief Parse and print ProxyMsgHeader from raw uint8_t buffer + * + * This macro converts a uint8_t* raw memory pointer to ProxyMsgHeader structure, + * then prints all fields with human-readable format (decimal + hexadecimal), + * including semantic explanation for message type and range check for payload length. + * + * @param BUF_PTR Input buffer pointer (uint8_t*), points to the start of ProxyMsgHeader in raw memory + * + * @note 1. The buffer must have at least sizeof(ProxyMsgHeader) valid bytes + * @note 2. uint16_t fields are converted from network byte order to host byte order via ntohs() + * @note 3. Null pointer check is included to avoid segmentation fault + * @warning The input buffer must use packed memory layout (match __attribute__((packed))) + */ +#define DUMP_PROXY_MSG_HEADER(BUF_PTR) do { \ + /* 1. Null pointer check to avoid segmentation fault */ \ + if ((BUF_PTR) == NULL) { \ + printf("[ProxyMsgHeader] Error: Input buffer pointer is NULL!\n"); \ + break; \ + } \ + /* 2. Cast uint8_t* to ProxyMsgHeader* to parse bytes as structured data */ \ + const ProxyMsgHeader* header = (const ProxyMsgHeader*)(BUF_PTR); \ + /* 3. Print all fields (convert uint16_t from network byte order to host byte order) */ \ + printf("============= ProxyMsgHeader =============\n"); \ + printf("version: %u (0x%02X)\n", header->version, header->version); \ + /* Semantic explanation for proxy_msg_type for better readability */ \ + printf("proxy_msg_type: %u (0x%02X) -> ", header->proxy_msg_type, header->proxy_msg_type); \ + switch (header->proxy_msg_type) { \ + case 0: printf("device message\n"); break; \ + case 1: printf("strategy message\n"); break; \ + case 2: printf("session message\n"); break; \ + case 3: printf("data message\n"); break; \ + default: printf("unknown message type\n"); break; \ + } \ + /* Convert uint16_t fields with ntohs() to handle endianness issues */ \ + printf("frontend_sess_id: %u (0x%04X)\n", ntohs(header->frontend_sess_id), ntohs(header->frontend_sess_id)); \ + printf("backend_sess_id: %u (0x%04X)\n", ntohs(header->backend_sess_id), ntohs(header->backend_sess_id)); \ + printf("payload_len: %u (0x%04X) bytes\n", ntohs(header->payload_len), ntohs(header->payload_len)); \ + /* Validate payload_len against valid range (1~4088) */ \ + uint16_t payload_len = ntohs(header->payload_len); \ + if (payload_len < 1 || payload_len > 4088) { \ + printf("⚠️ Warning: payload_len (%u) is out of valid range (1~4088)!\n", payload_len); \ + } \ + printf("===========================================\n"); \ +} while (0) + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index c02ef24..8f6ca25 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -1,4 +1,5 @@ #include "hyperamp_shm_queue.h" +#include "common_utils.h" // Trusted public key (user-generated ECDSA-P256 public key in DER format) static const unsigned char TRUSTED_PUBKEY_DER[] = { @@ -304,7 +305,13 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, // Read the data hyperamp_safe_memcpy(data, (const volatile void *)read_addr, read_len); - + +#if 1 + printf("In %s, before DUMP_BUFFER_CONTENT\n", __func__); +// DUMP_BUFFER_CONTENT(data, 20, "%0x2 "); + DUMP_PROXY_MSG_HEADER(data); +#endif + if (actual_len) { *actual_len = read_len; } diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index d0e09f3..c491c51 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -56,6 +56,8 @@ int main(void){ utils_print("Before enter frontend_proxy_msg_process, the content of the proxy message is:\n"); DUMP_BUFFER_CONTENT(msg_buf, proxy_msg_hdr->payload_len + sizeof(ProxyMsgHeader), "%02x "); + + DUMP_PROXY_MSG_HEADER(msg_buf); frontend_proxy_msg_process(msg_buf); }else if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ -- Gitee From 641cca81c05b69d56d4d20407f20d15d608d489d Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 2 Mar 2026 09:48:14 +0800 Subject: [PATCH 071/133] add debug codes --- projects/sel4test/apps/front/src/hyperamp_shm_queue.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index 8f6ca25..1c0e5a5 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -307,8 +307,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, hyperamp_safe_memcpy(data, (const volatile void *)read_addr, read_len); #if 1 - printf("In %s, before DUMP_BUFFER_CONTENT\n", __func__); -// DUMP_BUFFER_CONTENT(data, 20, "%0x2 "); + printf("In %s, before update the tail pointer and the dequeue count, the content of message header:\n", __func__); DUMP_PROXY_MSG_HEADER(data); #endif @@ -334,7 +333,12 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, for (int i = 0; i < 4; i++) { p[dequeue_offset + i] = (dequeue_count >> (i * 8)) & 0xFF; } - + + #if 1 + printf("In %s, after update the tail pointer and the dequeue count, the content of message header:\n", __func__); + DUMP_PROXY_MSG_HEADER(data); +#endif + HYPERAMP_BARRIER(); // Release the lock -- Gitee From 533658b83589895cbb3e0de88bdd4e35adb5dcd0 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 2 Mar 2026 10:41:27 +0800 Subject: [PATCH 072/133] add debug codes --- projects/sel4test/apps/front/src/hyperamp_shm_queue.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index 1c0e5a5..d70ffd1 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -277,6 +277,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, /* Invalidate cache before reading to ensure the latest data is fetched */ hyperamp_cache_invalidate((volatile void *)queue, 64); + HYPERAMP_BARRIER(); // Acquire the lock hyperamp_spinlock_lock(&queue->queue_lock, zone_id); @@ -299,6 +300,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, /* Invalidate the data region cache to ensure the latest data is read */ hyperamp_cache_invalidate((volatile void *)read_addr, block_size); + HYPERAMP_BARRIER(); // Determine the actual number of bytes to read size_t read_len = (max_len < block_size) ? max_len : block_size; @@ -311,6 +313,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, DUMP_PROXY_MSG_HEADER(data); #endif + if (actual_len) { *actual_len = read_len; } -- Gitee From e0fd8fd3433772b5b180c9a939c9fc225205629d Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 2 Mar 2026 16:39:27 +0800 Subject: [PATCH 073/133] test codes --- .../apps/front/include/common_utils.h | 20 +++++++++---------- projects/sel4test/apps/front/src/engine.c | 2 ++ .../sel4test/apps/front/src/senario_test.c | 3 ++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/projects/sel4test/apps/front/include/common_utils.h b/projects/sel4test/apps/front/include/common_utils.h index 45b54ac..8750c01 100644 --- a/projects/sel4test/apps/front/include/common_utils.h +++ b/projects/sel4test/apps/front/include/common_utils.h @@ -66,17 +66,17 @@ int debug_print(const char *format, ...); * @warning The input buffer must use packed memory layout (match __attribute__((packed))) */ #define DUMP_PROXY_MSG_HEADER(BUF_PTR) do { \ - /* 1. Null pointer check to avoid segmentation fault */ \ + /* 1. Null pointer check to prevent segmentation fault */ \ if ((BUF_PTR) == NULL) { \ printf("[ProxyMsgHeader] Error: Input buffer pointer is NULL!\n"); \ break; \ } \ - /* 2. Cast uint8_t* to ProxyMsgHeader* to parse bytes as structured data */ \ + /* 2. Cast uint8_t* to ProxyMsgHeader* to parse byte stream as structured data */ \ const ProxyMsgHeader* header = (const ProxyMsgHeader*)(BUF_PTR); \ - /* 3. Print all fields (convert uint16_t from network byte order to host byte order) */ \ + /* 3. Print all header fields (no endianness conversion required) */ \ printf("============= ProxyMsgHeader =============\n"); \ printf("version: %u (0x%02X)\n", header->version, header->version); \ - /* Semantic explanation for proxy_msg_type for better readability */ \ + /* Print proxy message type with semantic explanation for readability */ \ printf("proxy_msg_type: %u (0x%02X) -> ", header->proxy_msg_type, header->proxy_msg_type); \ switch (header->proxy_msg_type) { \ case 0: printf("device message\n"); break; \ @@ -85,12 +85,12 @@ int debug_print(const char *format, ...); case 3: printf("data message\n"); break; \ default: printf("unknown message type\n"); break; \ } \ - /* Convert uint16_t fields with ntohs() to handle endianness issues */ \ - printf("frontend_sess_id: %u (0x%04X)\n", ntohs(header->frontend_sess_id), ntohs(header->frontend_sess_id)); \ - printf("backend_sess_id: %u (0x%04X)\n", ntohs(header->backend_sess_id), ntohs(header->backend_sess_id)); \ - printf("payload_len: %u (0x%04X) bytes\n", ntohs(header->payload_len), ntohs(header->payload_len)); \ - /* Validate payload_len against valid range (1~4088) */ \ - uint16_t payload_len = ntohs(header->payload_len); \ + /* Directly print uint16_t fields (no ntohs() for endianness conversion) */ \ + printf("frontend_sess_id: %u (0x%04X)\n", header->frontend_sess_id, header->frontend_sess_id); \ + printf("backend_sess_id: %u (0x%04X)\n", header->backend_sess_id, header->backend_sess_id); \ + printf("payload_len: %u (0x%04X) bytes\n", header->payload_len, header->payload_len); \ + /* Validate payload length against valid range (1~4088) */ \ + uint16_t payload_len = header->payload_len; \ if (payload_len < 1 || payload_len > 4088) { \ printf("⚠️ Warning: payload_len (%u) is out of valid range (1~4088)!\n", payload_len); \ } \ diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 6caa527..e8a8a3b 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -405,6 +405,7 @@ void frontend_engine_init(){ abort(); } +#if 0 ret = engine_init_shared_mem_queue(p_g_fr_eng); if(FRONTEND_PROXY_PROCESS_OK != ret){ @@ -413,6 +414,7 @@ void frontend_engine_init(){ free(p_g_fr_eng->dev_set); abort(); } +#endif ret = engine_init_hyperamp_queue(p_g_fr_eng); diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index c85e646..89487dc 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -126,13 +126,14 @@ int scenario_msg_inject_frontend(FrontendEngine *engine, error_print("scenario_msg_inject_frontend failed: BackendEngine pointer is NULL, engine must be initialized!"); return FRONTEND_PROXY_PROCESS_ERROR; } - +#if 0 // 2. Check if engine->rx_queue (shared memory RX queue) is NULL (queue initialization is required for message injection, per document) if (engine->rx_queue == NULL) { error_print("scenario_msg_inject_frontend failed: engine->rx_queue (SharedMemoryPoolQueue) is NULL, RX queue must be initialized via engine_init_shared_mem_queue"); return FRONTEND_PROXY_PROCESS_ERROR; } +#endif // 3. Check if GeneralProxyMsgHeader pointer is NULL (valid header is required for message parsing, per document's message handling rules) if (msg_header == NULL) -- Gitee From 5f71588c1455b71be5e5fad22393da214e90657c Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 5 Mar 2026 14:58:27 +0800 Subject: [PATCH 074/133] fix type mismatch issue causing runtime error --- .../sel4test/apps/front/src/hyperamp_shm_queue.c | 14 +++++++++++++- projects/sel4test/apps/front/src/main.c | 3 ++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index d70ffd1..0982916 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -318,12 +318,18 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, *actual_len = read_len; } + printf("After set actual_len\n"); + DUMP_PROXY_MSG_HEADER(data); // Update tail (byte-by-byte write for atomicity) uint16_t new_tail = tail + 1; if (new_tail >= capacity) { new_tail -= capacity; } - + + printf("After set new_tail\n"); + DUMP_PROXY_MSG_HEADER(data); + +#if 0 volatile uint8_t *p = (volatile uint8_t *)queue; size_t tail_offset = offsetof(HyperampShmQueue, tail); p[tail_offset] = new_tail & 0xFF; @@ -336,6 +342,9 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, for (int i = 0; i < 4; i++) { p[dequeue_offset + i] = (dequeue_count >> (i * 8)) & 0xFF; } +#endif + queue->tail = new_tail; + queue->dequeue_count++; #if 1 printf("In %s, after update the tail pointer and the dequeue count, the content of message header:\n", __func__); @@ -346,6 +355,9 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, // Release the lock hyperamp_spinlock_unlock(&queue->queue_lock); + + printf("After unlock spinlock\n"); + DUMP_PROXY_MSG_HEADER(data); return HYPERAMP_OK; } diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index c491c51..c016efd 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -18,7 +18,8 @@ int main(void){ FrontendEngine *eng; struct FrontendSession *sess; ProxyMsgHeader *proxy_msg_hdr; - int ret, block_size; + int ret; + size_t block_size; uint8_t msg_buf[HYPERAMP_MSG_HDR_PLUS_MAX_SIZE]; #if 0 seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); -- Gitee From 8f9c847082e7c6bb2f3d8a3c224eef36c30056af Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 5 Mar 2026 15:09:50 +0800 Subject: [PATCH 075/133] fix type mismatch issue causing runtime error --- projects/sel4test/apps/front/src/hyperamp_shm_queue.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index 0982916..e9abaed 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -329,7 +329,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, printf("After set new_tail\n"); DUMP_PROXY_MSG_HEADER(data); -#if 0 +#if 1 volatile uint8_t *p = (volatile uint8_t *)queue; size_t tail_offset = offsetof(HyperampShmQueue, tail); p[tail_offset] = new_tail & 0xFF; @@ -343,8 +343,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, p[dequeue_offset + i] = (dequeue_count >> (i * 8)) & 0xFF; } #endif - queue->tail = new_tail; - queue->dequeue_count++; + #if 1 printf("In %s, after update the tail pointer and the dequeue count, the content of message header:\n", __func__); -- Gitee From 79425bf26e635a6d0e6bfc3ae16f4837c7256715 Mon Sep 17 00:00:00 2001 From: lr Date: Thu, 5 Mar 2026 15:33:29 +0800 Subject: [PATCH 076/133] feat: add help code --- .../apps/front/include/hyperamp_shm_queue.h | 6 ++-- projects/sel4test/apps/front/src/engine.c | 15 +++++++-- .../apps/front/src/hyperamp_shm_queue.c | 33 ++++++++++++++++++- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index 4cdbc69..b75c612 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -104,17 +104,17 @@ typedef enum { // Virtual address: Start of the TX queue in Hyperamp shared memory (seL4 → Linux) // #define SHM_TX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54E000UL) -#define SHM_TX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x55F000UL) +#define SHM_TX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x55E000UL) // Virtual address: Start of the RX queue in Hyperamp shared memory (Linux → seL4) // #define SHM_RX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x54F000UL) -#define SHM_RX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x560000UL) +#define SHM_RX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x55F000UL) // Virtual address: Start of the general data region in Hyperamp shared memory // #define SHM_DATA_REGION_VA ((volatile void *)0x550000UL) -#define SHM_DATA_REGION_VA ((volatile void *)0x561000UL) +#define SHM_DATA_REGION_VA ((volatile void *)0x560000UL) //g_tx_queue = (volatile HyperampShmQueue *)0x54e000; diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index e8a8a3b..b87ca8a 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -182,8 +182,19 @@ int frontend_engine_hyperamp_rx_queue_get(FrontendEngine *eng, size_t max_msg_le return FRONTEND_PROXY_PROCESS_ERROR; } - ret = hyperamp_queue_dequeue(eng->hyper_rx_queue, HYPERAMP_ZONE_ID_SEL4, data, max_msg_len, out_len, g_hyper_data_region); - + /* 关键:在读取队列状态前失效缓存,确保读取到 Linux 写入的最新数据 */ + hyperamp_cache_invalidate(eng->hyper_rx_queue, 64); + + // 检查 RX Queue 是否有来自 Linux 后端的响应 + uint16_t rx_header = hyperamp_safe_read_u16(eng->hyper_rx_queue, + offsetof(HyperampShmQueue, header)); + uint16_t rx_tail = hyperamp_safe_read_u16(eng->hyper_rx_queue, + offsetof(HyperampShmQueue, tail)); + + volatile void *rx_data_base = g_hyper_data_region; // 共享数据区 + printf("debug: rx_header=%u, rx_tail=%u, rx_data_base=%p, msg_buf=%p\n", rx_header, rx_tail, rx_data_base, data); + ret = hyperamp_queue_dequeue(eng->hyper_rx_queue, HYPERAMP_ZONE_ID_SEL4, data, max_msg_len, out_len, rx_data_base); + if(HYPERAMP_ERROR == ret){ error_print("frontend_engine_hyperamp_rx_queue_get failed: hyperamp_queue_dequeue execution failed!\n"); return FRONTEND_PROXY_PROCESS_ERROR; diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index e9abaed..fcf7151 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -113,7 +113,7 @@ static inline void hyperamp_safe_memcpy(volatile void *dst, const volatile void HYPERAMP_BARRIER(); } -static inline uint16_t hyperamp_safe_read_u16(const volatile void *addr, size_t offset) +uint16_t hyperamp_safe_read_u16(const volatile void *addr, size_t offset) { const volatile uint8_t *p = (const volatile uint8_t *)addr; uint16_t val = 0; @@ -253,6 +253,37 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, return HYPERAMP_OK; } + +//helper function +static void print_hex(const uint8_t *data, size_t len, size_t max_display) +{ + printf(" [HEX] "); + for (size_t i = 0; i < len && i < max_display; i++) { + printf("%02x", data[i]); + if ((i + 1) % 16 == 0) printf("\n "); + } + if (len > max_display) printf("... (%zu bytes total)", len); + printf("\n"); +} + +static void print_string(const char *data, size_t len, size_t max_display) +{ + printf(" [STR] \""); + for (size_t i = 0; i < len && i < max_display; i++) { + char c = data[i]; + if (c >= 32 && c <= 126) { + printf("%c", c); + } else if (c == '\0') { + break; + } else { + printf("\\x%02x", (unsigned char)c); + } + } + if (len > max_display) printf("..."); + printf("\"\n"); +} + + /** * @brief Dequeue data from the shared memory queue * @param queue [in/out] Pointer to the shared memory queue control structure (tail and dequeue_count will be updated internally) -- Gitee From 71b5e86a231a4d107bd2039679f6e8c73a533317 Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 5 Mar 2026 16:01:12 +0800 Subject: [PATCH 077/133] fix type mismatch issue causing runtime error --- projects/sel4test/apps/front/src/engine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index b87ca8a..1afc191 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -870,7 +870,8 @@ void frontend_engine_run_hyperamp(){ struct FrontendSessionPoolOps *sess_pool_ops; uint8_t *proxy_msg; uint32_t msg_size; - int ret, block_size; + int ret; + size_t block_size; uint8_t msg_buf[HYPERAMP_MSG_HDR_PLUS_MAX_SIZE]; eng = frontend_get_global_engine(); -- Gitee From eaa0b6ced0c9922b15586d23759a13a6fe7dff2b Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 6 Mar 2026 15:09:41 +0800 Subject: [PATCH 078/133] add test codes --- .../apps/front/include/senario_test.h | 5 + .../sel4test/apps/front/include/session.h | 2 +- projects/sel4test/apps/front/src/engine.c | 9 +- .../sel4test/apps/front/src/frontend_proto.c | 4 +- .../sel4test/apps/front/src/senario_test.c | 107 ++++++++++++++++++ .../sel4test/apps/front/src/session_pool.c | 16 +-- 6 files changed, 128 insertions(+), 15 deletions(-) diff --git a/projects/sel4test/apps/front/include/senario_test.h b/projects/sel4test/apps/front/include/senario_test.h index f2eb39a..8a6257b 100644 --- a/projects/sel4test/apps/front/include/senario_test.h +++ b/projects/sel4test/apps/front/include/senario_test.h @@ -38,4 +38,9 @@ int strategy_msg_inject_frontend_hyperamp(FrontendEngine *engine); int session_msg_inject_frontend_hyperamp(FrontendEngine *engine); int data_msg_inject_frontend_hyperamp(FrontendEngine *engine); + +int test_frontend_proxy_scenario_process_active_f2b_sess_queue(FrontendEngine *engine); + +int test_frontend_proxy_scenario_process_active_b2f_sess_queue(FrontendEngine *engine); + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index 47f9394..40ecf7f 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -29,7 +29,7 @@ typedef enum { FRONTEND_SESS_EVENT_RECVDATA, /**< Frontend session data received event (triggered when the session receives data from the peer/backend, and the data is ready for processing) */ FRONTEND_SESS_EVENT_CLOSE, /**< Frontend session close event (triggered when the session needs to be closed actively or passively, e.g., after receiving close command or peer disconnect) */ FRONTEND_SESS_EVENT_TIMEOUT, /**< Frontend session timeout event (triggered when the session has no data interaction or response within the predefined timeout period) */ - FRONTEND_SESS_EVENT_ABNOMAL, /**< Frontend session abnormal event (triggered when unexpected exceptions occur during session operation, such as resource allocation failure, protocol parsing error, or unexpected connection interruption) */ + FRONTEND_SESS_EVENT_ABNORMAL, /**< Frontend session abnormal event (triggered when unexpected exceptions occur during session operation, such as resource allocation failure, protocol parsing error, or unexpected connection interruption) */ FRONTEND_SESS_EVENT_MAX /**< Total number of frontend session event types (for boundary checking and iteration, not a valid event type) */ } FrontendSessionEvent; diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 1afc191..46ece29 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -808,12 +808,12 @@ eng_run_step3: * If data_process_f2b returns FRONTEND_PROXY_PROCESS_ERROR, it indicates an error occurred when attempting to send data via the shared memory's TX queue. * For such sessions, they should be removed from the frontend-to-backend active queue (active_queue_f2b), and the session's abnormal event callback should be triggered. * - * Note that the recovery of resources occupied by the session needs to be completed during the processing of the FRONTEND_SESS_EVENT_ABNOMAL event, and this responsibility + * Note that the recovery of resources occupied by the session needs to be completed during the processing of the FRONTEND_SESS_EVENT_ABNORMAL event, and this responsibility * lies with the application. */ if(FRONTEND_PROXY_PROCESS_ERROR == ret){ TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); - cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNOMAL); + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNORMAL); } /* * When not all data has been sent and no errors have occurred, no action is required. @@ -992,18 +992,17 @@ eng_run_step3: * If data_process_f2b returns FRONTEND_PROXY_PROCESS_ERROR, it indicates an error occurred when attempting to send data via the shared memory's TX queue. * For such sessions, they should be removed from the frontend-to-backend active queue (active_queue_f2b), and the session's abnormal event callback should be triggered. * - * Note that the recovery of resources occupied by the session needs to be completed during the processing of the FRONTEND_SESS_EVENT_ABNOMAL event, and this responsibility + * Note that the recovery of resources occupied by the session needs to be completed during the processing of the FRONTEND_SESS_EVENT_ABNORMAL event, and this responsibility * lies with the application. */ if(FRONTEND_PROXY_PROCESS_ERROR == ret){ TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); - cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNOMAL); + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNORMAL); } /* * When not all data has been sent and no errors have occurred, no action is required. */ } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess) - }while(1); } diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index b435aad..1144d44 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -967,7 +967,7 @@ int __frontend_proxy_sess_msg_process_active_create_ver1(uint16_t frontend_sess_ if(FRONTEND_PROXY_PROCESS_OK == ret){ sess->event_callback(sess, FRONTEND_SESS_EVENT_CONN); }else{ - sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNOMAL); + sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNORMAL); } @@ -1170,7 +1170,7 @@ int __frontend_proxy_sess_msg_process_close_ver1(uint16_t frontend_sess_id, uint if(SESS_OP_STATUS_SUCCESS != status){ utils_print("In %s, the session creating procedurec failed, the error code is %d\n", __func__, code); - sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNOMAL); + sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNORMAL); }else{ sess->event_callback(sess, FRONTEND_SESS_EVENT_CLOSE); } diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index 89487dc..57af1ba 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -653,4 +653,111 @@ int test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(FrontendEngine *e ret = data_msg_inject_frontend_hyperamp(engine); return ret; +} + + + +/** + * @brief Process the active Frontend-to-Backend session queue in the frontend proxy scenario + * @details This function is designed to be executed after the frontend engine reads messages from the receive queue. + * It accesses all active Frontend-to-Backend (f2b) sessions managed by the frontend engine, + * retrieves the to-be-sent data from each active session in sequence, and send the data to + * the corresponding Backend side through the HyperAMP TX queue by calling sess_pool->data_process_f2b. + * + * @param[in] engine Pointer to a FrontendEngine structure that manages the active f2b sessions and related frontend resources + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful processing of all active sessions; + * FRONTEND_PROXY_PROCESS_ERROR if any error occurs during processing (e.g., socket write failure, invalid session) + */ +int test_frontend_proxy_scenario_process_active_f2b_sess_queue(FrontendEngine *engine){ + struct FrontendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *cur_sess, *next_sess; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + uint8_t *proxy_msg; + uint32_t msg_size; + int ret; + + FRONTEND_ENGINE_GET_F2B_QUEUE(engine, active_queue_f2b); + + TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess){ +/* + * Call the corresponding data_process_f2b function pointer in the session pool's operation set (sess_pool_ops), which attempts to send the frontend-to-backend data maintained by the + * current session (cur_sess) via the shared memory's TX queue. + * The return value corresponds to three scenarios: + * Returns FRONTEND_PROXY_PROCESS_OK: All data has been sent successfully. + * Returns FRONTEND_PROXY_PROCESS_AGAIN: Not all data has been sent, and no errors occurred. + * Returns FRONTEND_PROXY_PROCESS_ERROR: An error occurred during the sending process. + */ + ret = sess_pool_ops->data_process_f2b(cur_sess); +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_OK, this indicates all message segments in the front-to-back (F2B) message queue have been sent via the shared memory's TX queue. + * Such sessions should be detached from the F2B active queue, and their "linked to queue" state flag should be cleared. + */ + if(FRONTEND_PROXY_PROCESS_OK == ret){ + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->state_f2b &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_ERROR, it indicates an error occurred when attempting to send data via the shared memory's TX queue. + * For such sessions, they should be removed from the frontend-to-backend active queue (active_queue_f2b), and the session's abnormal event callback should be triggered. + * + * Note that the recovery of resources occupied by the session needs to be completed during the processing of the FRONTEND_SESS_EVENT_ABNORMAL event, and this responsibility + * lies with the application. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNORMAL); + } +/* + * When not all data has been sent and no errors have occurred, no action is required. + */ + } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess) + + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Process the active Backend-to-Frontend session queue in the frontend proxy scenario + * @details This function is designed for processing Backend-to-Frontend (b2f) traffic in the frontend proxy layer. + * It accesses all active b2f sessions managed by the frontend engine, + * calls the sess->event_callback function in sequence for each session, + * and read the packets in the session's b2f message queue from the frontend-side shared memory RX queue. + * + * @param[in] engine Pointer to a FrontendEngine structure that manages the active b2f sessions and related frontend resources + * (including b2f message queues, HyperAMP TX queue, etc.) + * @return int Returns FRONTEND_PROXY_PROCESS_OK on successful processing of all active sessions; + * FRONTEND_PROXY_PROCESS_ERROR if any error occurs during processing + */ +int test_frontend_proxy_scenario_process_active_b2f_sess_queue(FrontendEngine *engine){ + struct FrontendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *cur_sess, *next_sess; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + uint8_t *proxy_msg; + uint32_t msg_size; + int ret; + + FRONTEND_ENGINE_GET_B2F_QUEUE(engine, active_queue_b2f); + + sess_pool = engine->sess_pool; + sess_pool_ops = sess_pool->ops; + + utils_print("In %s\n", __func__); + + TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess){ + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_RECVDATA); + +/* + * queue_b2f: Session queue for sessions with pending data to be received by the frontend from the backend. + * (Pending data refers to data sent from the backend to the frontend, which the frontend needs to read.) + * If the receive queue (msg_b2f) is empty, the session instance should be removed from the queue_b2f list. + */ + if(TAILQ_EMPTY(&cur_sess->msg_b2f)){ + TAILQ_REMOVE(active_queue_b2f, cur_sess, entries_b2f); + cur_sess->state_b2f &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } + } + + return FRONTEND_PROXY_PROCESS_OK; } \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c index 98557a2..09e7a6b 100644 --- a/projects/sel4test/apps/front/src/session_pool.c +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -641,7 +641,7 @@ int frontend_high_speed_close_sess_step2(struct FrontendSessionPool *s_pool, str if(SESS_OP_STATUS_SUCCESS != status){ utils_print("In %s, the session creating procedurec failed, the error code is %d\n", __func__, code); - sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNOMAL); + sess->event_callback(sess, FRONTEND_SESS_EVENT_ABNORMAL); ret = FRONTEND_PROXY_PROCESS_ERROR; }else{ sess->event_callback(sess, FRONTEND_SESS_EVENT_CLOSE); @@ -729,7 +729,9 @@ int frontend_high_speed_data_process_f2b(struct FrontendSession *sess){ * Parameters: Engine instance, message header pointer, data segment pointer, data length, * result storage pointer, memory allocation mode (shared memory), additional parameters */ - ret = build_proxy_general_message(sess->eng, &data_msg_hdr, cur_seg->data, cur_seg->len, res_pointer, MEMORY_ALLOC_SHARED, sess->eng->tx_queue); +// ret = build_proxy_general_message(sess->eng, &data_msg_hdr, cur_seg->data, cur_seg->len, res_pointer, MEMORY_ALLOC_SHARED, sess->eng->tx_queue); + + ret = build_proxy_general_message(sess->eng, &data_msg_hdr, cur_seg->data, cur_seg->len, res_pointer, MEMORY_ALLOC_AMPQUEUE, NULL); if(FRONTEND_PROXY_PROCESS_OK == ret){ TAILQ_REMOVE(&sess->msg_f2b, cur_seg, entry); @@ -789,13 +791,13 @@ void frontend_high_speed_destroy_pool(struct FrontendSessionPool *s_pool) */ void default_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event){ if(FRONTEND_SESS_EVENT_CONN == event){ - + utils_print("In %s, FRONTEND_SESS_EVENT_CONN happens!\n", __func__); }else if(FRONTEND_SESS_EVENT_RECVDATA == event){ - + utils_print("In %s, FRONTEND_SESS_EVENT_RECVDATA happens!\n", __func__); }else if(FRONTEND_SESS_EVENT_CLOSE == event){ - - }else if(FRONTEND_SESS_EVENT_ABNOMAL == event){ - + utils_print("In %s, FRONTEND_SESS_EVENT_CLOSE happens!\n", __func__); + }else if(FRONTEND_SESS_EVENT_ABNORMAL == event){ + utils_print("In %s, FRONTEND_SESS_EVENT_ABNORMAL happens!\n", __func__); } return; -- Gitee From dde09e88201da643db3d45c5340447a0e7e5bb6d Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 6 Mar 2026 17:38:15 +0800 Subject: [PATCH 079/133] add test codes, modify API functions --- .../sel4test/apps/front/include/session.h | 107 ++++++-- .../sel4test/apps/front/src/frontend_api.c | 242 +++++++++++++++++- .../sel4test/apps/front/src/senario_test.c | 2 +- projects/sel4test/apps/front/src/session.c | 5 +- 4 files changed, 328 insertions(+), 28 deletions(-) diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index 40ecf7f..c0d8cb8 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -59,27 +59,64 @@ typedef enum { * dynamically allocated memory or shared memory (via a shared memory pool). */ struct SessMsgSeg { -/*< Length of the data buffer (in bytes). Indicates the valid data size in the buffer pointed to by @ref data. */ + /** + * @brief Length of the currently valid (unread) data in bytes. + * + * This value decreases as data is read from the segment. + * Initially set to the total size of the received data block. + * When frontend_sess_recv reads part of this segment, 'len' is reduced + * and 'data' pointer is advanced accordingly. + */ uint16_t len; -/* - * < Memory source type of the data buffer, corresponding to SessMsgSegType. - * Valid values: SESS_MSG_SEG_DYNAMIC_ALLOC (dynamic memory) or SESS_MSG_SEG_SHARED_MEM (shared memory). - */ + + /** + * @brief Offset of the current data pointer relative to the original buffer start. + * + * Indicates how many bytes have been consumed from the original allocation/shared memory block. + * - Initially 0 when the segment is created. + * - Increases as 'data' pointer advances during partial reads (e.g., in frontend_sess_recv). + * - Useful for debugging, tracing data progress, or calculating absolute positions in shared memory. + * + * Relationship: Original_Data_Start + offset == Current data pointer (if base address is known). + */ + uint16_t offset; + + /** + * @brief Memory source type of the data buffer, corresponding to SessMsgSegType. + * + * Valid values: + * - SESS_MSG_SEG_DYNAMIC_ALLOC: Data points to malloc()'ed memory. 'mem_pool' should be NULL. + * - SESS_MSG_SEG_SHARED_MEM: Data points to a region within a shared memory pool. 'mem_pool' must be valid. + */ uint16_t type; -/* - * < Pointer to the associated shared memory pool. - * Valid and non-NULL only when @ref type is SESS_MSG_SEG_SHARED_MEM, used for managing shared memory ownership and validation. - * NULL when @ref type is SESS_MSG_SEG_DYNAMIC_ALLOC (no shared memory pool associated). - */ + + /** + * @brief Pointer to the associated shared memory pool. + * + * - Valid (non-NULL) ONLY when @ref type is SESS_MSG_SEG_SHARED_MEM. + * Used to manage ownership, reference counting, and proper release of shared memory. + * - NULL when @ref type is SESS_MSG_SEG_DYNAMIC_ALLOC. + */ struct SharedMemoryPool *mem_pool; -/*< Pointer to the actual data buffer. - * For SESS_MSG_SEG_DYNAMIC_ALLOC: Points to memory allocated via malloc(). - * For SESS_MSG_SEG_SHARED_MEM: Points to a valid data segment within the shared memory managed by @ref mem_pool. - */ + + /** + * @brief Pointer to the current start of the unread data buffer. + * + * - For SESS_MSG_SEG_DYNAMIC_ALLOC: Points to the current position in dynamically allocated memory. + * Initially points to malloc() return value; advances as data is consumed. + * - For SESS_MSG_SEG_SHARED_MEM: Points to the current valid data segment within the shared memory. + * + * @note This pointer is MUTABLE. Functions like frontend_sess_recv will increment this pointer + * and decrease @ref len when only part of the segment is read, preserving the remaining data. + */ uint8_t *data; -/* - * < TAILQ queue entry. Used to link multiple SessMsgSeg structures into a doubly linked list for sequential access. - */ + + /** + * @brief TAILQ queue entry. + * + * Links this segment into the session's receive queue (msg_b2f), allowing sequential traversal + * and aggregation of multiple message fragments. + */ TAILQ_ENTRY(SessMsgSeg) entry; }; @@ -194,6 +231,42 @@ struct FrontendSession { ) +/** + * @brief Get the block size of the front-end session's hyper queue in the specified direction (with null-pointer safe check, branchless concatenation implementation) + * + * Dynamically select the hyper queue member using identifier concatenation without conditional judgment; + * perform step-by-step null-pointer checks along the pointer chain to avoid illegal access. + * Pointer chain: sess -> eng -> [hyper_tx_queue/hyper_rx_queue] -> block_size (target hyper queue member generated by concatenating hyper_, dir and _queue via ##) + * + * @param sess Pointer to the front-end session instance (type: struct FrontendSession *), which can be NULL + * @param dir Queue direction identifier, only two valid values are supported: tx (corresponds to hyper_tx_queue) and rx (corresponds to hyper_rx_queue) + * + * @return uint16_t Block size of the hyper queue (block_size): + * - Non-0: All dependent pointers are valid, returns the actual configured block size (unit: bytes) + * - 0: Any dependent pointer is NULL (sess/eng/target direction hyper queue) or no corresponding member after dir concatenation (may report an error in advance during compilation) + * + * @note 1. Branchless design: Directly generate hyper_tx_queue/hyper_rx_queue member names by concatenating hyper_, dir and _queue via ##, + * completely replacing the original conditional judgment with higher execution efficiency and simpler syntax; + * 2. Compile-time validity check: If an invalid value other than tx/rx is passed to dir (e.g., txx), + * concatenation will generate a non-existent member like hyper_txx_queue, which directly reports an error during compilation, + * avoiding issues earlier than runtime judgment; + * 3. Null-pointer safety retained: Maintains the original three-level pointer check (sess→eng→hyper queue), + * and directly returns 0 when any link is NULL through the && short-circuit feature, without sacrificing null-pointer safety; + * 4. Type compatibility: The return type is consistent with block_size (uint16_t), and 0U is an unsigned integer 0, + * compatible with numerical comparison scenarios; + * 5. Concatenation safety: All parameters and member accesses in the macro are enclosed in parentheses to avoid + * syntax errors caused by operator precedence conflicts. + */ +#define SESS_GET_HYPER_QUEUE_BLOCK_SIZE(sess, dir) \ +( \ + (sess) != NULL && \ + (sess)->eng != NULL && \ + (sess)->eng->hyper_##dir##_queue != NULL \ + ? (sess)->eng->hyper_##dir##_queue->block_size \ + : 0U \ +) + + /** * @brief Link Frontend session to the specified queue (only if not linked yet) and set state bit * diff --git a/projects/sel4test/apps/front/src/frontend_api.c b/projects/sel4test/apps/front/src/frontend_api.c index 28a4089..934488f 100644 --- a/projects/sel4test/apps/front/src/frontend_api.c +++ b/projects/sel4test/apps/front/src/frontend_api.c @@ -180,27 +180,41 @@ int frontend_sess_connect(struct FrontendSession *sess, struct SessMsgPara *para /** * @brief Calculate the number of message segments required for sending a message (includes header + data) * - * Each segment can hold up to `bsize` bytes (fixed header + message data). Uses ceil division to ensure full data coverage. + * Each segment can hold up to `bsize` bytes (fixed header + message data). + * Uses safe ceil division to ensure full data coverage without integer overflow. * * @param dsize Total size of message data to send (bytes, non-negative) * @param bsize Max capacity of a single message segment (bytes, must be > hsize) * @param hsize Fixed size of the segment header (bytes, non-negative) * * @return size_t Required segment count: - * - 0: Invalid param (bsize <= hsize, no space for data) + * - 0: Invalid param (bsize <= hsize) OR No data to send (dsize == 0) * - >=1: Valid count (all data + headers fit in segments) * - * @note 1. Effective data per segment: bsize - hsize (header occupies fixed space in each segment); - * 2. Ceil division: (dsize + effective_per_seg - 1U) / effective_per_seg to avoid truncation; - * 3. Edge cases: dsize=0 returns 1 (header-only segment if bsize>hsize); dsize <= effective_per_seg returns 1. + * @note 1. Effective data per segment: effective = bsize - hsize. + * 2. Safe Ceil Division: Uses formula ((dsize - 1) / effective) + 1 to prevent + * overflow that could occur with (dsize + effective - 1). + * 3. Edge case handling: + * - If dsize == 0, returns 0 (no segments needed for empty payload). + * - If bsize <= hsize, returns 0 immediately as no data can be transmitted. + * 4. Type Safety: All intermediate calculations are performed in size_t (unsigned). */ #define CALC_MSG_SEG_NUM(dsize, bsize, hsize) \ ( \ - (bsize) > (hsize) ? \ - ((((dsize) + (bsize) - (hsize) - 1U) / ((bsize) - (hsize)))) : 0U \ + /* Check validity: segment capacity must be strictly greater than header size */ \ + ((bsize) <= (hsize)) ? 0U : \ + ( \ + /* Edge case: Empty message requires 0 segments */ \ + ((dsize) == 0U) ? 0U : \ + /* Safe ceiling division: (dsize - 1) / effective_capacity + 1 */ \ + /* This avoids overflow risks associated with (dsize + effective_capacity - 1) */ \ + (((dsize) - 1U) / ((bsize) - (hsize)) + 1U) \ + ) \ ) + +#if 0 /** * @brief Write data to the send buffer of a frontend session (for frontend session data transmission) * @@ -228,7 +242,7 @@ int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t siz uint8_t *payload, **res_pointer; uint8_t *res_buf[100] = {NULL}; - block_size = SESS_GET_QUEUE_BLOCK_SIZE(sess, tx); + block_size = SESS_GET_HYPER_QUEUE_BLOCK_SIZE(sess, tx); if(0 == block_size){ error_print("frontend_sess_send failed: the TX queue is not initialized correctly!"); @@ -240,12 +254,14 @@ int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t siz seg_num = CALC_MSG_SEG_NUM(data_size, block_size, header_size); eff_data_per_seg = block_size - header_size; +#if 0 memset(&data_msg_hdr, 0, sizeof(data_msg_hdr)); data_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; data_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_DATA; data_msg_hdr.outer_header.backend_sess_id = sess->backend_sess_id; data_msg_hdr.outer_header.frontend_sess_id = sess->frontend_sess_id; +#endif /* * Copy data into message segments. Each message segment carries a PROXY-DATA message whose payload does not exceed block_size - header_size. */ @@ -311,8 +327,114 @@ seg_frag_error: FRONTEND_SESS_UNLINK_FROM_QUEUE(sess, f2b); return -1; } +#endif +/** + * @brief Fragment user data and enqueue pure payloads into the frontend-to-backend queue (msg_f2b). + * + * This function splits input data into chunks, reserving space for protocol headers + * (which will be added by the subsequent sending logic). + * + * Key Features: + * 1. Robust Looping: Driven by remaining data size, not pre-calculated segment counts. + * 2. Partial Success: If memory allocation fails mid-stream, already enqueued segments + * are kept and marked for sending. Returns the number of bytes successfully enqueued. + * 3. No Dead Code: Removed unreachable error handling paths. + * + * @param[in] sess Pointer to the frontend session instance. + * @param[in] data Pointer to the user payload data. + * @param[in] size Total length of the user payload data. + * + * @return int + * - >0: Number of bytes successfully enqueued. + * - 0: No data enqueued (e.g., first allocation failed). + * - -1: Critical parameter error. + */ +int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t size) { + struct SessMsgSeg *msg_seg; + uint32_t block_size; + uint32_t header_reserved_size; + uint32_t max_payload_per_seg; + uint32_t total_enqueued; + uint32_t cur_chunk_size; + uint32_t remaining_data; + uint8_t *src_ptr; + + /* 1. Parameter Validation */ + if (NULL == sess || NULL == data || 0 == size) { + error_print("frontend_sess_send failed: invalid parameters!\n"); + return -1; /* Use -1 for invalid args */ + } + + /* 2. Get Queue Block Size */ + block_size = SESS_GET_HYPER_QUEUE_BLOCK_SIZE(sess, tx); + if (0 == block_size) { + error_print("frontend_sess_send failed: TX queue not initialized!\n"); + return -1; + } + + /* Calculate limits */ + header_reserved_size = PROXY_MSG_HDR_SIZE; + + if (block_size <= header_reserved_size) { + error_print("frontend_sess_send failed: Block size too small for header reservation!\n"); + return -1; + } + + max_payload_per_seg = block_size - header_reserved_size; + + total_enqueued = 0; + remaining_data = size; + src_ptr = data; + + /* 3. Fragmentation Loop (Driven by remaining data, NOT pre-calculated seg_num) */ + while (remaining_data > 0) { + /* Determine chunk size: min(remaining, max_per_seg) */ + cur_chunk_size = (remaining_data > max_payload_per_seg) ? max_payload_per_seg : remaining_data; + + /* Allocate Segment (Pure Payload only) */ + msg_seg = sess_msg_seg_alloc(cur_chunk_size, SESS_MSG_SEG_DYNAMIC_ALLOC, NULL, NULL); + + if (NULL == msg_seg) { + error_print("frontend_sess_send failed: Memory allocation failed!\n"); + /* + * PARTIAL SUCCESS: + * Stop processing. Already enqueued segments are valid. + * Do NOT free them. Do NOT unlink. Just return what we have. + */ + goto done_partial; + } + + /* Copy Pure Payload */ + memcpy(msg_seg->data, src_ptr, cur_chunk_size); + + /* Set Metadata */ + msg_seg->len = cur_chunk_size; + msg_seg->offset = 0; + msg_seg->type = SESS_MSG_SEG_DYNAMIC_ALLOC; + msg_seg->mem_pool = NULL; + + /* Enqueue */ + SESS_MSG_SEG_INSERT_QUEUE(sess, msg_seg, f2b); + + /* Update Counters */ + total_enqueued += cur_chunk_size; + remaining_data -= cur_chunk_size; + src_ptr += cur_chunk_size; + } + +done_partial: + /* 4. Finalize: Link Session ONLY if we successfully enqueued something */ + if (total_enqueued > 0) { + FRONTEND_SESS_LINK_TO_QUEUE(sess, f2b); + } + + return (int)total_enqueued; +} + + +#if 0 /** * @brief Read data from the receive buffer of a frontend session (for frontend session data reception) * This function is used to read specified data from the receive buffer of a frontend session (FrontendSession). @@ -368,6 +490,110 @@ int frontend_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t siz #endif return read_size; } +#endif + + +/** + * @brief Read data from the receive buffer of a frontend session (supports multi-block aggregation) + * + * This function reads data from the receive buffer (FrontendSession) into the user-provided buffer. + * It aggregates multiple small data blocks (SessMsgSeg) from the TAILQ into a single contiguous + * memory copy, as long as the requested 'size' allows. + * + * Key Behaviors: + * 1. Aggregation: Continuously reads from the queue head until the user buffer is full or queue is empty. + * 2. Partial Read Handling (Zero Data Loss): + * - If a segment fits entirely: It is copied, removed from the queue, and freed. + * - If a segment is larger than remaining space: Only the fitting portion is copied. + * The segment is KEPT in the queue with updated metadata: + * - 'data' pointer is advanced. + * - 'len' is decreased. + * - 'offset' is increased (to track progress from original base). + * 3. Non-blocking: Returns immediately based on current availability. + * + * @param[in] sess Pointer to the frontend session instance; must not be NULL. + * @param[inout] data Pointer to the destination buffer; must not be NULL. + * @param[in] size Maximum bytes to read; must be > 0. + * + * @return int + * - >0: Total number of bytes successfully read. + * - 0: No data available in the queue. + * - -1: Error (invalid parameters). + * + * @note + * - Thread Safety: Assumes exclusive access to sess->msg_b2f. External locking is required if + * accessed by multiple threads. + * - Offset Tracking: The 'offset' field in SessMsgSeg is updated to reflect the number of + * bytes consumed from the original allocation. + */ +int frontend_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t size) { + struct SessMsgSeg *cur_seg; + uint32_t total_read = 0; + uint32_t copy_len; + uint8_t *dst_ptr = data; + + /* 1. Parameter Validation */ + if (NULL == sess || NULL == data || 0 == size) { + error_print("frontend_sess_recv failed: invalid parameters!\n"); + return -1; + } + + /* 2. Loop to aggregate multiple segments */ + while (total_read < size) { + cur_seg = TAILQ_FIRST(&sess->msg_b2f); + + /* Queue empty: stop reading */ + if (NULL == cur_seg) { + break; + } + + /* Calculate remaining space in user buffer */ + uint32_t remaining_space = size - total_read; + + if (cur_seg->len <= remaining_space) { + /* Case A: Segment fits entirely in the remaining buffer */ + copy_len = cur_seg->len; + + /* Copy all data from this segment */ + memcpy(dst_ptr, cur_seg->data, copy_len); + + /* Update destination pointer and total count */ + dst_ptr += copy_len; + total_read += copy_len; + + /* Remove and free the fully consumed segment */ + TAILQ_REMOVE(&sess->msg_b2f, cur_seg, entry); + + /* Optional: Clear pointers before free for safety/debugging */ + cur_seg->data = NULL; + cur_seg->mem_pool = NULL; + + free(cur_seg); + + /* Continue to next segment */ + } else { + /* Case B: Segment is larger than remaining buffer space */ + copy_len = remaining_space; + + /* Copy only the fitting part */ + memcpy(dst_ptr, cur_seg->data, copy_len); + + /* Update destination pointer and total count */ + dst_ptr += copy_len; + total_read += copy_len; + + /* CRITICAL UPDATE: Adjust segment metadata to preserve remaining data */ + cur_seg->data += copy_len; /* Advance data pointer */ + cur_seg->len -= copy_len; /* Decrease remaining length */ + cur_seg->offset += (uint16_t)copy_len; /* Increase offset to track consumption */ + + /* Stop reading because user buffer is now full */ + break; + } + } + + return (int)total_read; +} /** diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index 57af1ba..0a831a6 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -682,7 +682,7 @@ int test_frontend_proxy_scenario_process_active_f2b_sess_queue(FrontendEngine *e TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess){ /* * Call the corresponding data_process_f2b function pointer in the session pool's operation set (sess_pool_ops), which attempts to send the frontend-to-backend data maintained by the - * current session (cur_sess) via the shared memory's TX queue. + * current session (cur_sess) via the HyperAMP TX queue. * The return value corresponds to three scenarios: * Returns FRONTEND_PROXY_PROCESS_OK: All data has been sent successfully. * Returns FRONTEND_PROXY_PROCESS_AGAIN: Not all data has been sent, and no errors occurred. diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 52ef346..1ceff9c 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -56,8 +56,9 @@ struct SessMsgSeg *sess_msg_seg_alloc(size_t len, SessMsgSegType type, uint8_t * goto msg_alloc_error; } - msg_seg->type = type; - msg_seg->len = len; + msg_seg->type = type; + msg_seg->len = len; + msg_seg->offset = 0; return msg_seg; msg_alloc_error: -- Gitee From 96dc337de02072e6ec27cd444401df9f0835737c Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 11:41:05 +0800 Subject: [PATCH 080/133] fix bugs, add test codes --- .../sel4test/apps/front/src/frontend_api.c | 11 ++- .../sel4test/apps/front/src/frontend_proto.c | 1 + projects/sel4test/apps/front/src/main.c | 72 +++++++++++++++++-- .../sel4test/apps/front/src/session_pool.c | 18 +++-- 4 files changed, 92 insertions(+), 10 deletions(-) diff --git a/projects/sel4test/apps/front/src/frontend_api.c b/projects/sel4test/apps/front/src/frontend_api.c index 934488f..0bd9a61 100644 --- a/projects/sel4test/apps/front/src/frontend_api.c +++ b/projects/sel4test/apps/front/src/frontend_api.c @@ -412,7 +412,7 @@ int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t siz /* Set Metadata */ msg_seg->len = cur_chunk_size; msg_seg->offset = 0; - msg_seg->type = SESS_MSG_SEG_DYNAMIC_ALLOC; +// msg_seg->type = SESS_MSG_SEG_DYNAMIC_ALLOC; msg_seg->mem_pool = NULL; /* Enqueue */ @@ -568,6 +568,15 @@ int frontend_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t siz cur_seg->data = NULL; cur_seg->mem_pool = NULL; +/** + * Check if the current session message segment is marked as dynamically allocated + * (SESS_MSG_SEG_DYNAMIC_ALLOC indicates the segment's data buffer was allocated via malloc/calloc/realloc). + * If true, release the associated memory to avoid memory leaks – this is critical for + * maintaining memory integrity in session message processing. + */ if(SESS_MSG_SEG_DYNAMIC_ALLOC == cur_seg->type){ + free(cur_seg->data); + } + free(cur_seg); /* Continue to next segment */ diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 1144d44..ed56792 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -1256,6 +1256,7 @@ int frontend_proxy_data_msg_prosess(uint16_t frontend_sess_id, uint16_t backend_ } utils_print("In %s, after sess_msg_seg_alloc\n", __func__); + memcpy(msg_seg->data, msg, data_len); /* * Insert the message segment into the back-to-front message queue. */ diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index c016efd..695e547 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -11,8 +11,69 @@ #include "frontend_api.h" #include "senario_test.h" + +void test_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event); + extern void tailq_test(); extern void uthash_test(); + + + +/** + * @brief Test event callback function for frontend sessions + * + * This is the test-oriented callback implementation for handling frontend session events, + * which is invoked by the session framework when specific events (defined in + * FrontendSessionEvent enumeration) occur during the lifecycle of a FrontendSession. + * It is designed exclusively for testing and validation purposes (e.g., unit testing, + * integration testing of the session framework), and provides test-specific event processing + * logic instead of production-grade business handling. + * + * @param[in] sess Pointer to the FrontendSession instance that triggered the event; + * must be a valid non-NULL pointer pointing to an active session + * @param[in] event The specific event type that occurred, one of the values in + * FrontendSessionEvent enumeration (FRONTEND_SESS_EVENT_CONN, + * FRONTEND_SESS_EVENT_RECVDATA, FRONTEND_SESS_EVENT_TIMEOUT, FRONTEND_SESS_EVENT_CLOSE) + * + * @note 1. This callback is called automatically by the session management framework, + * and should not be invoked manually by the user; + * 2. The test implementation typically includes operations such as + * event logging for test verification, capturing event occurrence status, + * simulating edge-case processing (e.g., intentionally triggering error paths on FRONTEND_SESS_EVENT_TIMEOUT, + * validating data integrity on FRONTEND_SESS_EVENT_RECVDATA, or checking resource cleanup on FRONTEND_SESS_EVENT_CLOSE); + * 3. This callback is NOT intended for production use — it should only be used in + * test environments to validate the correctness of session event triggering and handling; + * 4. Ensure the 'sess' pointer is valid when the callback is triggered (the framework + * guarantees this under normal circumstances), invalid pointers may lead to undefined behavior; + * 5. The processing logic in this callback should be non-blocking to avoid blocking the + * session framework's event loop, even in test scenarios. + */ +void test_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event){ + uint8_t snd_buf[4096], rcv_buf[4096]; + int ret; + + if(FRONTEND_SESS_EVENT_CONN == event){ + utils_print("In %s: FRONTEND_SESS_EVENT_CONN triggered!\n", __func__); + utils_print("%s: Starting to send test data...\n", __func__); + memset(snd_buf, 0, sizeof(snd_buf)); + snprintf((char*)snd_buf, sizeof(snd_buf), "test msg"); + ret = frontend_sess_send(sess, snd_buf, strlen((char*)snd_buf)); + utils_print("%s: frontend_sess_send returned %d bytes (sent data: \"test msg\")\n", __func__, ret); + }else if(FRONTEND_SESS_EVENT_RECVDATA == event){ + utils_print("In %s: FRONTEND_SESS_EVENT_RECVDATA triggered!\n", __func__); + ret = frontend_sess_recv(sess, rcv_buf, sizeof(rcv_buf)); + utils_print("%s: frontend_sess_recv received %d bytes of data\n", __func__, ret); + frontend_sess_send(sess, rcv_buf, ret); + }else if(FRONTEND_SESS_EVENT_CLOSE == event){ + utils_print("In %s: FRONTEND_SESS_EVENT_CLOSE triggered!\n", __func__); + }else if(FRONTEND_SESS_EVENT_ABNORMAL == event){ + utils_print("In %s: FRONTEND_SESS_EVENT_ABNORMAL triggered!\n", __func__); + } + + return; +} + + int main(void){ printf("Running tests\n"); FrontendEngine *eng; @@ -34,13 +95,14 @@ int main(void){ eng = frontend_get_global_engine(); - test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); -#if 0 +// test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); +#if 1 sess = frontend_sess_new(eng); ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.100:8080"); + frontend_sess_bind_callback(sess, test_session_event_callback); - test_proxy_scenario_multi_type_msg_build_frontend(eng); - test_proxy_scenario_msg_read_from_rx_queue_frontend(eng); +// test_proxy_scenario_multi_type_msg_build_frontend(eng); +// test_proxy_scenario_msg_read_from_rx_queue_frontend(eng); #endif int cnt = 0; @@ -77,4 +139,4 @@ int main(void){ } return 0; -} \ No newline at end of file +} diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c index 09e7a6b..a906270 100644 --- a/projects/sel4test/apps/front/src/session_pool.c +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -292,7 +292,8 @@ void frontend_high_speed_delete_all_sess(struct FrontendSessionPool *s_pool) ipv4_para.transport_layer_proto = para->trans_proto; memcpy(&ipv4_para.dest_endpoint, ¶->ip_port_tuple.ipv4_port_tuple, sizeof(ipv4_para.dest_endpoint)); - ret = build_proxy_general_message(engine, &proxy_msg_hdr, &ipv4_para, sizeof(ipv4_para), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); +// ret = build_proxy_general_message(engine, &proxy_msg_hdr, &ipv4_para, sizeof(ipv4_para), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &ipv4_para, sizeof(ipv4_para), res_msg, MEMORY_ALLOC_AMPQUEUE, engine->tx_queue); if(ret != FRONTEND_PROXY_PROCESS_OK){ error_print("frontend_high_speed_create_sess_step1 failed: failed to build proxy general message or send to shared memory TX queue!"); @@ -464,7 +465,8 @@ int frontend_high_speed_create_sess_passive(struct FrontendSessionPool *s_pool, resp_data.status = SESS_OP_STATUS_SUCCESS; resp_data.code = SESS_OP_CODE_SUCCESS; - ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); +// ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_AMPQUEUE, NULL); if(FRONTEND_PROXY_PROCESS_OK != ret){ error_print("frontend_high_speed_create_sess_passive: failed to build proxy response message for passive session creation!\n"); @@ -473,7 +475,9 @@ int frontend_high_speed_create_sess_passive(struct FrontendSessionPool *s_pool, return ret; create_sess_passive_error: - ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); +// ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &resp_data, sizeof(resp_data), res_msg, MEMORY_ALLOC_AMPQUEUE, engine->tx_queue); + if(FRONTEND_PROXY_PROCESS_OK != ret){ error_print("frontend_high_speed_create_sess_passive: failed to build error notification message!\n"); @@ -604,7 +608,8 @@ int frontend_high_speed_close_sess_step1(struct FrontendSessionPool *s_pool, str proxy_msg_hdr.inner_header.sess_hdr.ip_version = sess->ip_version; proxy_msg_hdr.inner_header.sess_hdr.payload_len = 0; - ret = build_proxy_general_message(engine, &proxy_msg_hdr, NULL, 0, res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); +// ret = build_proxy_general_message(engine, &proxy_msg_hdr, NULL, 0, res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); + ret = build_proxy_general_message(engine, &proxy_msg_hdr, NULL, 0, res_msg, MEMORY_ALLOC_AMPQUEUE, NULL); if(ret != FRONTEND_PROXY_PROCESS_OK){ error_print("frontend_high_speed_close_sess_step1 failed: failed to build proxy general message or send to shared memory TX queue!"); @@ -790,8 +795,13 @@ void frontend_high_speed_destroy_pool(struct FrontendSessionPool *s_pool) * session framework's event loop. */ void default_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event){ + uint8_t snd_buf[4096], rcv_buf[4096]; if(FRONTEND_SESS_EVENT_CONN == event){ utils_print("In %s, FRONTEND_SESS_EVENT_CONN happens!\n", __func__); + utils_print("Now begin to send data.\n"); + memset(snd_buf, 0, sizeof(snd_buf)); + snprintf(snd_buf, sizeof(snd_buf), "test msg"); + frontend_sess_send(sess, snd_buf, strlen(snd_buf)); }else if(FRONTEND_SESS_EVENT_RECVDATA == event){ utils_print("In %s, FRONTEND_SESS_EVENT_RECVDATA happens!\n", __func__); }else if(FRONTEND_SESS_EVENT_CLOSE == event){ -- Gitee From 953bffcb133897b6312d238b52605e149867bf2f Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 13:49:07 +0800 Subject: [PATCH 081/133] add commits --- .../sel4test/apps/front/src/senario_test.c | 14 +++--- .../sel4test/apps/front/src/session_pool.c | 46 ++++++++++++++++++- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index 0a831a6..4f4d6ef 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -681,12 +681,14 @@ int test_frontend_proxy_scenario_process_active_f2b_sess_queue(FrontendEngine *e TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess){ /* - * Call the corresponding data_process_f2b function pointer in the session pool's operation set (sess_pool_ops), which attempts to send the frontend-to-backend data maintained by the - * current session (cur_sess) via the HyperAMP TX queue. - * The return value corresponds to three scenarios: - * Returns FRONTEND_PROXY_PROCESS_OK: All data has been sent successfully. - * Returns FRONTEND_PROXY_PROCESS_AGAIN: Not all data has been sent, and no errors occurred. - * Returns FRONTEND_PROXY_PROCESS_ERROR: An error occurred during the sending process. + * Call the corresponding data_process_f2b function pointer in the session pool's operation set (sess_pool_ops), + * which attempts to send the frontend-to-backend (F2B) data maintained by the current session (cur_sess) + * through the HyperAMP TX queue. The data_process_f2b pointer points to the frontend_high_speed_data_process_f2b function. + * + * The return value indicates three possible scenarios: + * - Returns FRONTEND_PROXY_PROCESS_OK: All data was sent successfully. + * - Returns FRONTEND_PROXY_PROCESS_AGAIN: Not all data was sent, and no errors occurred (retry is needed). + * - Returns FRONTEND_PROXY_PROCESS_ERROR: An error occurred during the data transmission process. */ ret = sess_pool_ops->data_process_f2b(cur_sess); /* diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c index a906270..37c9dbb 100644 --- a/projects/sel4test/apps/front/src/session_pool.c +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -701,6 +701,50 @@ int frontend_high_speed_data_process_b2f(struct FrontendSession *sess){ } + +/** + * @brief High-speed processing function for frontend-to-backend (F2B) data messages via HyperAMP queue + * + * @details This function traverses the frontend-to-backend message queue (msg_f2b) of a given FrontendSession, + * constructs general proxy data messages for each message segment, and sends them through the HyperAMP TX queue. + * It uses TAILQ_FOREACH_SAFE to safely iterate over the doubly linked list (TAILQ) to avoid iteration exceptions + * caused by node deletion during traversal. For each message segment: + * 1. Populates the general proxy message header with session and protocol metadata (version, message type, session IDs) + * 2. Calls build_proxy_general_message to assemble the data message for HyperAMP queue transmission + * 3. On successful construction (FRONTEND_PROXY_PROCESS_OK): removes the segment from the queue and frees associated resources + * 4. On temporary failure (FRONTEND_PROXY_PROCESS_AGAIN): terminates processing (HyperAMP TX queue full) and requests retry + * 5. On fatal failure (FRONTEND_PROXY_PROCESS_ERROR): terminates processing immediately (device failure detected), + * resource cleanup is handled by the external caller (global release of all session resources) + * + * @param[in] sess Pointer to the FrontendSession instance containing the F2B message queue to process; + * must be a valid non-NULL pointer to an active session + * + * @return Integer status code indicating the processing result: + * - FRONTEND_PROXY_PROCESS_OK: All message segments in the msg_f2b queue were processed and sent successfully + * - FRONTEND_PROXY_PROCESS_AGAIN: Processing stopped prematurely (HyperAMP TX queue is full), + * unprocessed segments remain in the queue (retry is required, no resource release) + * - FRONTEND_PROXY_PROCESS_ERROR: Fatal device failure detected, processing aborted immediately; + * the external caller MUST perform a "full cleanup" (release all session resources, + * including all pending message segments and the session instance itself) + * + * @retval FRONTEND_PROXY_PROCESS_OK All segments processed successfully, no pending data left in the queue + * @retval FRONTEND_PROXY_PROCESS_AGAIN Temporary queue full error, recoverable via retry (no resource release needed) + * @retval FRONTEND_PROXY_PROCESS_ERROR Unrecoverable device failure, requires global session resource release (all pending messages are discarded) + * + * @note 1. TAILQ_FOREACH_SAFE ensures safe traversal even when nodes are removed during iteration (next_seg caches the next node) + * 2. Successfully processed segments are immediately removed from the queue and freed to prevent memory leaks + * 3. FRONTEND_PROXY_PROCESS_AGAIN (queue full) is a recoverable error — the caller should retry this function after a short delay + * 4. FRONTEND_PROXY_PROCESS_ERROR indicates a fatal device failure (not a single message error), so the caller MUST: + * - Release all resources associated with the session (msg_f2b queue, session instance, HyperAMP handles, etc.) + * - Discard all unprocessed message segments (no retry is meaningful for device failure scenarios) + * - Mark the session as invalid to avoid accidental reuse + * 5. This function does NOT clean up failed segments on FRONTEND_PROXY_PROCESS_ERROR — global cleanup is delegated to the caller + * 6. The function terminates immediately on non-OK return codes, leaving remaining segments unprocessed (expected behavior for fatal errors) + * + * @see build_proxy_general_message + * @see sess_msg_seg_free + * @see TAILQ_FOREACH_SAFE + */ int frontend_high_speed_data_process_f2b(struct FrontendSession *sess){ struct SessMsgSeg *cur_seg, *next_seg; GeneralProxyMsgHeader data_msg_hdr; @@ -742,7 +786,7 @@ int frontend_high_speed_data_process_f2b(struct FrontendSession *sess){ TAILQ_REMOVE(&sess->msg_f2b, cur_seg, entry); sess_msg_seg_free(cur_seg); }else if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ - error_print("frontend_high_speed_data_process_f2b stops: the shared-memory TX queue is full!\n"); + error_print("frontend_high_speed_data_process_f2b stops: the HyperAMP TX queue is full!\n"); return FRONTEND_PROXY_PROCESS_AGAIN; }else{ error_print("frontend_high_speed_data_process_f2b failed: fail to build data message!\n"); -- Gitee From 31708ce09194d51de3d0a624af82ed5e55a3185e Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 14:40:42 +0800 Subject: [PATCH 082/133] change port --- projects/sel4test/apps/front/src/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 695e547..df88a97 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -98,7 +98,7 @@ int main(void){ // test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); #if 1 sess = frontend_sess_new(eng); - ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.100:8080"); + ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.101:8888"); frontend_sess_bind_callback(sess, test_session_event_callback); // test_proxy_scenario_multi_type_msg_build_frontend(eng); @@ -129,6 +129,9 @@ int main(void){ utils_print("Failed to get message in HyperAMP RX queue!\n"); } + test_frontend_proxy_scenario_process_active_b2f_sess_queue(eng); + test_frontend_proxy_scenario_process_active_f2b_sess_queue(eng); + if(cnt > 50){ break; } -- Gitee From ff61e04e6739e72f68bda5829d2c71fec78bebda Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 15:22:31 +0800 Subject: [PATCH 083/133] add debug codes --- projects/sel4test/apps/front/src/frontend_api.c | 7 +++++++ projects/sel4test/apps/front/src/session_pool.c | 12 +++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/frontend_api.c b/projects/sel4test/apps/front/src/frontend_api.c index 0bd9a61..b24a764 100644 --- a/projects/sel4test/apps/front/src/frontend_api.c +++ b/projects/sel4test/apps/front/src/frontend_api.c @@ -108,6 +108,10 @@ int frontend_sess_connect_by_addrstr(struct FrontendSession *sess, int proto, co para.trans_proto = proto; IPV4_PORT_STR_TO_TUPLE(addr_str, para.ip_port_tuple.ipv4_port_tuple); + utils_print("In %s:\n", __func__); + utils_print("frontend_sess_id = %d, backend_sess_id = %d, ip_version = %d, dev_id = %d, trans_proto = %d\n", + para.frontend_sess_id, para.backend_sess_id, para.ip_version, para.dev_id, para.trans_proto); + return frontend_sess_connect(sess, ¶); } @@ -170,6 +174,9 @@ int frontend_sess_connect(struct FrontendSession *sess, struct SessMsgPara *para para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[2], para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[3], para->ip_port_tuple.ipv4_port_tuple.port); + + utils_print("frontend_sess_id = %d, backend_sess_id = %d, ip_version = %d, dev_id = %d, trans_proto = %d\n", + para->frontend_sess_id, para->backend_sess_id, para->ip_version, para->dev_id, para->trans_proto); ret = ops->create_sess_step1(sess_pool, sess, para); diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c index 37c9dbb..62966bb 100644 --- a/projects/sel4test/apps/front/src/session_pool.c +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -221,6 +221,16 @@ void frontend_high_speed_delete_all_sess(struct FrontendSessionPool *s_pool) uint8_t **res_msg, *res_buf[100] = {NULL}; + utils_print("In %s, addr is %d.%d.%d.%d port = %d\n", __func__, + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[0], + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[1], + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[2], + para->ip_port_tuple.ipv4_port_tuple.ipv4_addr.data[3], + para->ip_port_tuple.ipv4_port_tuple.port); + + utils_print("frontend_sess_id = %d, backend_sess_id = %d, ip_version = %d, dev_id = %d, trans_proto = %d\n", + para->frontend_sess_id, para->backend_sess_id, para->ip_version, para->dev_id, para->trans_proto); + engine = s_pool->engine; sess_pool_ops = s_pool->ops; res_msg = res_buf; @@ -293,7 +303,7 @@ void frontend_high_speed_delete_all_sess(struct FrontendSessionPool *s_pool) memcpy(&ipv4_para.dest_endpoint, ¶->ip_port_tuple.ipv4_port_tuple, sizeof(ipv4_para.dest_endpoint)); // ret = build_proxy_general_message(engine, &proxy_msg_hdr, &ipv4_para, sizeof(ipv4_para), res_msg, MEMORY_ALLOC_SHARED, engine->tx_queue); - ret = build_proxy_general_message(engine, &proxy_msg_hdr, &ipv4_para, sizeof(ipv4_para), res_msg, MEMORY_ALLOC_AMPQUEUE, engine->tx_queue); + ret = build_proxy_general_message(engine, &proxy_msg_hdr, &ipv4_para, sizeof(ipv4_para), res_msg, MEMORY_ALLOC_AMPQUEUE, NULL); if(ret != FRONTEND_PROXY_PROCESS_OK){ error_print("frontend_high_speed_create_sess_step1 failed: failed to build proxy general message or send to shared memory TX queue!"); -- Gitee From 745b14eea71dcc1264eaec28d72aded935700d51 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 15:24:29 +0800 Subject: [PATCH 084/133] add debug codes --- projects/sel4test/apps/front/src/main.c | 2 +- projects/sel4test/apps/front/src/senario_test.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index df88a97..db7c613 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -95,7 +95,7 @@ int main(void){ eng = frontend_get_global_engine(); -// test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); + test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); #if 1 sess = frontend_sess_new(eng); ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.101:8888"); diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index 4f4d6ef..f2cf869 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -647,10 +647,10 @@ int data_msg_inject_frontend_hyperamp(FrontendEngine *engine){ int test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(FrontendEngine *engine){ int ret; - ret = device_msg_inject_frontend_hyperamp(engine); - ret = strategy_msg_inject_frontend_hyperamp(engine); +// ret = device_msg_inject_frontend_hyperamp(engine); +// ret = strategy_msg_inject_frontend_hyperamp(engine); ret = session_msg_inject_frontend_hyperamp(engine); - ret = data_msg_inject_frontend_hyperamp(engine); +// ret = data_msg_inject_frontend_hyperamp(engine); return ret; } -- Gitee From 535683c0ae4168030111b46bf386f2994ee753a2 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 16:26:20 +0800 Subject: [PATCH 085/133] modify a macro --- projects/sel4test/apps/front/include/queue.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/projects/sel4test/apps/front/include/queue.h b/projects/sel4test/apps/front/include/queue.h index 58e667e..f669e70 100644 --- a/projects/sel4test/apps/front/include/queue.h +++ b/projects/sel4test/apps/front/include/queue.h @@ -586,11 +586,21 @@ struct { \ (var) = (next_var), (next_var) = TAILQ_NEXT((var), field)) #endif + + +#if 0 #define TAILQ_FOREACH_SAFE(var, head, field, next_var) \ for (var = TAILQ_FIRST(head); \ var != TAILQ_END(head) && (next_var = TAILQ_NEXT(var, field), 1); \ var = next_var) #endif +#endif + +#define TAILQ_FOREACH_SAFE(var, head, field, next_var) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) != NULL && ((next_var) = TAILQ_NEXT((var), field), 1); \ + (var) = (next_var)) + #endif /* sys/queue.h */ -- Gitee From b6208b5d178e8f0f2379ac317978da8d92a244f4 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 16:40:44 +0800 Subject: [PATCH 086/133] fix a bug --- projects/sel4test/apps/front/src/frontend_api.c | 4 ++++ projects/sel4test/apps/front/src/senario_test.c | 3 +++ projects/sel4test/apps/front/src/session_pool.c | 3 +++ 3 files changed, 10 insertions(+) diff --git a/projects/sel4test/apps/front/src/frontend_api.c b/projects/sel4test/apps/front/src/frontend_api.c index b24a764..81b3d50 100644 --- a/projects/sel4test/apps/front/src/frontend_api.c +++ b/projects/sel4test/apps/front/src/frontend_api.c @@ -368,6 +368,7 @@ int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t siz uint32_t remaining_data; uint8_t *src_ptr; + utils_print("In %s\n", __func__); /* 1. Parameter Validation */ if (NULL == sess || NULL == data || 0 == size) { error_print("frontend_sess_send failed: invalid parameters!\n"); @@ -381,6 +382,7 @@ int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t siz return -1; } + /* Calculate limits */ header_reserved_size = PROXY_MSG_HDR_SIZE; @@ -391,6 +393,8 @@ int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t siz max_payload_per_seg = block_size - header_reserved_size; + utils_print("block_size = %d, header_reserved_size = %d, max_payload_per_seg = %d\n", block_size, header_reserved_size, max_payload_per_seg); + total_enqueued = 0; remaining_data = size; src_ptr = data; diff --git a/projects/sel4test/apps/front/src/senario_test.c b/projects/sel4test/apps/front/src/senario_test.c index f2cf869..be34e30 100644 --- a/projects/sel4test/apps/front/src/senario_test.c +++ b/projects/sel4test/apps/front/src/senario_test.c @@ -677,6 +677,9 @@ int test_frontend_proxy_scenario_process_active_f2b_sess_queue(FrontendEngine *e uint32_t msg_size; int ret; + sess_pool = engine->sess_pool; + sess_pool_ops = sess_pool->ops; + FRONTEND_ENGINE_GET_F2B_QUEUE(engine, active_queue_f2b); TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess){ diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c index 62966bb..8ce86a1 100644 --- a/projects/sel4test/apps/front/src/session_pool.c +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -756,6 +756,7 @@ int frontend_high_speed_data_process_b2f(struct FrontendSession *sess){ * @see TAILQ_FOREACH_SAFE */ int frontend_high_speed_data_process_f2b(struct FrontendSession *sess){ + utils_print("In %s\n", __func__); struct SessMsgSeg *cur_seg, *next_seg; GeneralProxyMsgHeader data_msg_hdr; uint8_t *payload, **res_pointer; @@ -781,7 +782,9 @@ int frontend_high_speed_data_process_f2b(struct FrontendSession *sess){ * entry: Member used for linking nodes in the list (standard field of TAILQ linked list nodes) * next_seg: Temporarily stores the next node to ensure traversal can continue after deleting the current node */ + utils_print("Before contribute DATA message\n"); TAILQ_FOREACH_SAFE(cur_seg, &sess->msg_f2b, entry, next_seg) { + utils_print("data seg length = %d\n", cur_seg->len); data_msg_hdr.outer_header.payload_len = cur_seg->len; /* * Call the proxy general message construction function to assemble the data-type message -- Gitee From 9701f24740c06ebf2bf48c902251a97be3b22327 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 17:07:13 +0800 Subject: [PATCH 087/133] Open the infinite operation loop --- projects/sel4test/apps/front/src/main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index db7c613..79380cb 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -95,16 +95,17 @@ int main(void){ eng = frontend_get_global_engine(); - test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); +// test_proxy_scenario_multi_type_msg_build_frontend_hyperamp(eng); #if 1 sess = frontend_sess_new(eng); ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.101:8888"); frontend_sess_bind_callback(sess, test_session_event_callback); -// test_proxy_scenario_multi_type_msg_build_frontend(eng); -// test_proxy_scenario_msg_read_from_rx_queue_frontend(eng); #endif + + +#if 0 int cnt = 0; while(1){ // sleep(2); @@ -140,6 +141,9 @@ int main(void){ cnt++; } +#endif + + frontend_engine_run_hyperamp(); return 0; } -- Gitee From 505a3898fd15089c1e2d87c8b85d9baa55b78c28 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 17:35:26 +0800 Subject: [PATCH 088/133] add test codes --- projects/sel4test/apps/front/src/engine.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 46ece29..229d2f9 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -1,4 +1,5 @@ #include "engine.h" +#include extern FrontendEngine *p_g_fr_eng; extern FrontendEngine g_fr_eng; @@ -916,6 +917,7 @@ void frontend_engine_run_hyperamp(){ */ eng_run_step1: do{ + ps_sdelay(1); ret = frontend_engine_hyperamp_rx_queue_get(eng, HYPERAMP_MSG_HDR_PLUS_MAX_SIZE, msg_buf, &block_size); /* -- Gitee From 60ebc762eae10b0dde031fae26e9c20b9724d791 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 17:43:04 +0800 Subject: [PATCH 089/133] add test code --- projects/sel4test/apps/front/src/frontend_proto.c | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index ed56792..7bb27bc 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -501,6 +501,7 @@ int frontend_proxy_msg_process(uint8_t *msg){ msg_type = proxy_msg_hdr->proxy_msg_type; msg_len = proxy_msg_hdr->payload_len; + utils_print("In %s, the address of msg = %p\n", msg); utils_print("In %s, version = %d, frontend sess id = %d, backend sess id = %d, msg_type = %d, msg_len = %d\n", __func__, proxy_proto_ver, frontend_sess_id, backend_sess_id, msg_type, msg_len); /* -- Gitee From dc9a17f7a98d9cceac60c2e69b2e81e71491f1dd Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 9 Mar 2026 17:46:32 +0800 Subject: [PATCH 090/133] add test code --- projects/sel4test/apps/front/src/hyperamp_shm_queue.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index fcf7151..74f1763 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -339,6 +339,9 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, // Read the data hyperamp_safe_memcpy(data, (const volatile void *)read_addr, read_len); + print_hex(data, read_len, 64); + print_hex(read_addr, read_len, 64); + #if 1 printf("In %s, before update the tail pointer and the dequeue count, the content of message header:\n", __func__); DUMP_PROXY_MSG_HEADER(data); -- Gitee From 31574b6b6a37ef0a91d9a3a5be8457def30e5801 Mon Sep 17 00:00:00 2001 From: lr Date: Mon, 9 Mar 2026 18:39:07 +0800 Subject: [PATCH 091/133] add debug code --- .../apps/front/include/common_utils.h | 4 +-- projects/sel4test/apps/front/src/engine.c | 27 ++++++++++++------- .../sel4test/apps/front/src/frontend_proto.c | 7 ++--- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/projects/sel4test/apps/front/include/common_utils.h b/projects/sel4test/apps/front/include/common_utils.h index 8750c01..fa7097e 100644 --- a/projects/sel4test/apps/front/include/common_utils.h +++ b/projects/sel4test/apps/front/include/common_utils.h @@ -6,7 +6,7 @@ #include #include "message.h" -#define UTILS_ENABLE_DEBUG 1 +#define UTILS_ENABLE_DEBUG 0 void error_print(char *error); @@ -15,7 +15,7 @@ int debug_print(const char *format, ...); #if UTILS_ENABLE_DEBUG #define utils_print(...) printf(__VA_ARGS__) #else -#define utils_print(...) do {} while(0) +#define utils_print(...) (void)(0) #endif diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 229d2f9..cd3f90a 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -918,6 +918,7 @@ void frontend_engine_run_hyperamp(){ eng_run_step1: do{ ps_sdelay(1); + printf("In %s-1\n", __func__); ret = frontend_engine_hyperamp_rx_queue_get(eng, HYPERAMP_MSG_HDR_PLUS_MAX_SIZE, msg_buf, &block_size); /* @@ -932,24 +933,26 @@ eng_run_step1: /* * If returning FRONTEND_PROXY_PROCESS_AGAIN, it indicates temporary inability to retrieve data (e.g., empty queue, resource temporarily occupied, etc., non-error state) * No error reporting needed; jump to eng_run_step2 to execute the next process. - */ + */ printf("In %s-2\n", __func__); if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ goto eng_run_step2; } - + printf("In %s-3\n", __func__); /* * Process the proxy message. */ - frontend_proxy_msg_process(proxy_msg); + frontend_proxy_msg_process(msg_buf); + printf("In %s-4\n", __func__); }while(FRONTEND_PROXY_PROCESS_OK == ret); eng_run_step2: /* * Recall the FRONTEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (1) procedure may renew the back-to-front queue (queue_b2f) of the session pool. - */ + */ printf("In %s-5\n", __func__); FRONTEND_ENGINE_GET_B2F_QUEUE(eng, active_queue_b2f); - + printf("In %s-6\n", __func__); TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess){ + printf("In %s-7\n", __func__); cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_RECVDATA); /* @@ -961,6 +964,7 @@ eng_run_step2: TAILQ_REMOVE(active_queue_b2f, cur_sess, entries_b2f); cur_sess->state_b2f &= ~FRONTEND_SESS_LINKED_TO_QUEUE; } + printf("In %s-8\n", __func__); } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess) @@ -968,9 +972,9 @@ eng_run_step2: eng_run_step3: /* * Recall the BACKEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (2) procedure may renew the front-to-back queue (queue_f2b) of the session pool. - */ + */ printf("In %s-9\n", __func__); FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); - + printf("In %s-10\n", __func__); TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess){ /* @@ -980,13 +984,14 @@ eng_run_step3: * Returns FRONTEND_PROXY_PROCESS_OK: All data has been sent successfully. * Returns FRONTEND_PROXY_PROCESS_AGAIN: Not all data has been sent, and no errors occurred. * Returns FRONTEND_PROXY_PROCESS_ERROR: An error occurred during the sending process. - */ + */ printf("In %s-11\n", __func__); ret = sess_pool_ops->data_process_f2b(cur_sess); /* * If data_process_f2b returns FRONTEND_PROXY_PROCESS_OK, this indicates all message segments in the front-to-back (F2B) message queue have been sent via the shared memory's TX queue. * Such sessions should be detached from the F2B active queue, and their "linked to queue" state flag should be cleared. - */ + */ printf("In %s-12\n", __func__); if(FRONTEND_PROXY_PROCESS_OK == ret){ + printf("In %s-13\n", __func__); TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); cur_sess->state_f2b &= ~FRONTEND_SESS_LINKED_TO_QUEUE; } @@ -998,10 +1003,12 @@ eng_run_step3: * lies with the application. */ if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + printf("In %s-14\n", __func__); TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNORMAL); } -/* + printf("In %s-15\n", __func__); +/* * When not all data has been sent and no errors have occurred, no action is required. */ } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index ed56792..c6e575f 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -481,7 +481,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h * 3. The return result of the specialized processing function does not affect the return status of the current function; only whether the distribution process is successful is fed back */ int frontend_proxy_msg_process(uint8_t *msg){ - utils_print("In %s\n", __func__); + printf("In %s-1\n", __func__); ProxyMsgHeader *proxy_msg_hdr; int proxy_proto_ver, msg_len, ret; ProxyMsgType msg_type; @@ -500,8 +500,8 @@ int frontend_proxy_msg_process(uint8_t *msg){ backend_sess_id = proxy_msg_hdr->backend_sess_id; msg_type = proxy_msg_hdr->proxy_msg_type; msg_len = proxy_msg_hdr->payload_len; - - utils_print("In %s, version = %d, frontend sess id = %d, backend sess id = %d, msg_type = %d, msg_len = %d\n", + printf("In %s-2\n", __func__); + printf("In %s, version = %d, frontend sess id = %d, backend sess id = %d, msg_type = %d, msg_len = %d\n", __func__, proxy_proto_ver, frontend_sess_id, backend_sess_id, msg_type, msg_len); /* * Check the validity of the message type. @@ -541,6 +541,7 @@ int frontend_proxy_msg_process(uint8_t *msg){ } ret = frontend_proxy_strgy_msg_process(msg_ptr); }else if(PROXY_MSG_TYPE_SESS == msg_type){ + printf("In %s-3\n", __func__); ret = frontend_proxy_sess_msg_process(frontend_sess_id, backend_sess_id, msg_ptr); }else{ /* -- Gitee From 922c80024d1a5f73e8cba90570776d0100fa5758 Mon Sep 17 00:00:00 2001 From: xj009 Date: Tue, 10 Mar 2026 09:20:56 +0800 Subject: [PATCH 092/133] add test code --- projects/sel4test/apps/front/src/frontend_api.c | 8 ++++++-- projects/sel4test/apps/front/src/session_pool.c | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/front/src/frontend_api.c b/projects/sel4test/apps/front/src/frontend_api.c index 81b3d50..39c2f42 100644 --- a/projects/sel4test/apps/front/src/frontend_api.c +++ b/projects/sel4test/apps/front/src/frontend_api.c @@ -368,7 +368,8 @@ int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t siz uint32_t remaining_data; uint8_t *src_ptr; - utils_print("In %s\n", __func__); + utils_print("In %s\n, preparing to send data:", __func__); + DUMP_BUFFER_CONTENT(data, size, "%c"); /* 1. Parameter Validation */ if (NULL == sess || NULL == data || 0 == size) { error_print("frontend_sess_send failed: invalid parameters!\n"); @@ -393,7 +394,7 @@ int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t siz max_payload_per_seg = block_size - header_reserved_size; - utils_print("block_size = %d, header_reserved_size = %d, max_payload_per_seg = %d\n", block_size, header_reserved_size, max_payload_per_seg); + utils_print("data size = %d, block_size = %d, header_reserved_size = %d, max_payload_per_seg = %d\n", size, block_size, header_reserved_size, max_payload_per_seg); total_enqueued = 0; remaining_data = size; @@ -433,13 +434,16 @@ int frontend_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t siz total_enqueued += cur_chunk_size; remaining_data -= cur_chunk_size; src_ptr += cur_chunk_size; + utils_print("After loop, remaining_data = %d\n", remaining_data); } done_partial: /* 4. Finalize: Link Session ONLY if we successfully enqueued something */ + utils_print("[STATE-TAG-1] f2b_queue: state_f2b = %d, total_enqueued = %d\n", sess->state_f2b, total_enqueued); if (total_enqueued > 0) { FRONTEND_SESS_LINK_TO_QUEUE(sess, f2b); } + utils_print("[STATE-TAG-2] f2b_queue: state_f2b = %d\n", sess->state_f2b); return (int)total_enqueued; } diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c index 8ce86a1..f8e130a 100644 --- a/projects/sel4test/apps/front/src/session_pool.c +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -783,6 +783,7 @@ int frontend_high_speed_data_process_f2b(struct FrontendSession *sess){ * next_seg: Temporarily stores the next node to ensure traversal can continue after deleting the current node */ utils_print("Before contribute DATA message\n"); + utils_print("The address of the sess->eng = %p\n", sess->eng); TAILQ_FOREACH_SAFE(cur_seg, &sess->msg_f2b, entry, next_seg) { utils_print("data seg length = %d\n", cur_seg->len); data_msg_hdr.outer_header.payload_len = cur_seg->len; -- Gitee From 22b497bcbb4b62a66b6fa430ae4028a2f1dc8b26 Mon Sep 17 00:00:00 2001 From: lr Date: Tue, 10 Mar 2026 10:43:56 +0800 Subject: [PATCH 093/133] fix: argc type error --- projects/sel4test/apps/front/include/session.h | 2 +- projects/sel4test/apps/front/src/session.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index c0d8cb8..1580f4c 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -127,7 +127,7 @@ TAILQ_HEAD(SessMsgQueue, SessMsgSeg); struct SessMsgSeg *sess_msg_seg_alloc(size_t len, SessMsgSegType type, uint8_t *shared_data, struct SharedMemoryPool *mem_pool); struct SessMsgSeg* sess_msg_seg_alloc_lite(SessMsgSegType type); -void sess_msg_seg_free(struct SessMsgSeg **seg_ptr); +void sess_msg_seg_free(struct SessMsgSeg *seg_ptr); void sess_msg_queue_free_all(struct SessMsgQueue *queue); diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 1ceff9c..fee001f 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -117,15 +117,15 @@ struct SessMsgSeg* sess_msg_seg_alloc_lite(SessMsgSegType type){ * - If type is SESS_MSG_SEG_SHARED_MEM: does not free data (managed by shared memory system) * - Safely handles NULL input (no operation performed) */ -void sess_msg_seg_free(struct SessMsgSeg **seg_ptr){ +void sess_msg_seg_free(struct SessMsgSeg *seg_ptr){ struct SessMsgSeg *msg_seg; - if(NULL == seg_ptr || NULL == *seg_ptr){ + if(NULL == seg_ptr){ error_print("sess_msg_seg_free failed: input pointer is invalid!"); return; } - msg_seg = *seg_ptr; + msg_seg = seg_ptr; switch(msg_seg->type) { -- Gitee From c1b3b461273ee97039e80bbc7b62874dab82a583 Mon Sep 17 00:00:00 2001 From: lr Date: Tue, 10 Mar 2026 16:38:52 +0800 Subject: [PATCH 094/133] modify debug enable --- projects/sel4test/apps/front/include/common_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/include/common_utils.h b/projects/sel4test/apps/front/include/common_utils.h index fa7097e..4b122e6 100644 --- a/projects/sel4test/apps/front/include/common_utils.h +++ b/projects/sel4test/apps/front/include/common_utils.h @@ -6,7 +6,7 @@ #include #include "message.h" -#define UTILS_ENABLE_DEBUG 0 +#define UTILS_ENABLE_DEBUG 1 void error_print(char *error); -- Gitee From 2a7bbb50920e7426fd5aeed32d06f67fb60a61d3 Mon Sep 17 00:00:00 2001 From: lr Date: Mon, 16 Mar 2026 09:59:35 +0800 Subject: [PATCH 095/133] =?UTF-8?q?feat:=E6=9B=B4=E6=96=B0=E5=85=B1?= =?UTF-8?q?=E4=BA=AB=E5=86=85=E5=AD=98=E5=9C=B0=E5=9D=80=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=EF=BC=8C=E9=80=9A=E8=BF=87=20IPC=20buffer=20?= =?UTF-8?q?msg=20=E5=AD=97=E6=AE=B5=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/arm/kernel/boot.c | 30 ++++++++++------ projects/sel4test/apps/front/src/engine.c | 19 ++++++++--- projects/sel4test/apps/front/src/main.c | 12 +------ .../sel4test/apps/hyperamp-server/src/main.c | 34 +++++++------------ 4 files changed, 47 insertions(+), 48 deletions(-) diff --git a/kernel/src/arch/arm/kernel/boot.c b/kernel/src/arch/arm/kernel/boot.c index 936245f..9bac988 100755 --- a/kernel/src/arch/arm/kernel/boot.c +++ b/kernel/src/arch/arm/kernel/boot.c @@ -639,17 +639,6 @@ static BOOT_CODE bool_t try_init_kernel( // 内核直接映射区使用 cacheable 属性,会导致缓存不一致问题 // 应该由用户空间程序通过 uncached 映射进行初始化 - // 将共享内存虚拟地址传递给用户空间(与之前实现相同) - // store the first available vaddr in ipc buffer - seL4_Word *ipcBuf = (seL4_Word*)rootserver.ipc_buf; - *ipcBuf = (seL4_Word)(extra_bi_frame_vptr + extra_bi_size); - - // store shm: TX_Q RX_Q DATA vaddrs in first available addr - unsigned long long *addrMsg = (unsigned long long *)(rootserver.extra_bi + extra_bi_size); - const unsigned long long shmemComm_frame_vaddrs[] = - {shm_tx_queue_vaddr, shm_rx_queue_vaddr, shm_data_vaddr}; - for (unsigned i = 0; i < 3; ++i) - addrMsg[i] = shmemComm_frame_vaddrs[i]; /* create/initialise the initial thread's ASID pool */ it_ap_cap = create_it_asid_pool(root_cnode_cap); if (cap_get_capType(it_ap_cap) == cap_null_cap) { @@ -700,6 +689,25 @@ static BOOT_CODE bool_t try_init_kernel( /* finalise the bootinfo frame */ bi_finalise(); + // 将共享内存虚拟地址写入 IPC buffer 的 msg[2..4] 字段 + // msg[0..1] 会被 sel4runtime 的 seL4_DebugNameThread("rootserver") 覆盖 + // 用户空间通过 seL4_GetMR(2/3/4) 读取 + // word_t *ipcBuf下标 对应结构体字段 seL4_GetMR(i) + // ipcBuf[0] tag — + // ipcBuf[1] msg[0] seL4_GetMR(0) + // ipcBuf[2] msg[1] seL4_GetMR(1) + // ipcBuf[3] msg[2] seL4_GetMR(2) + // ipcBuf[4] msg[3] seL4_GetMR(3) + // ipcBuf[5] msg[4] seL4_GetMR(4) + { + word_t *ipcBuf = (word_t *)rootserver.ipc_buf; + ipcBuf[3] = shm_tx_queue_vaddr; // msg[2] + ipcBuf[4] = shm_rx_queue_vaddr; // msg[3] + ipcBuf[5] = shm_data_vaddr; // msg[4] + printf("kernel: IPC buffer msg[2..4] = 0x%lx, 0x%lx, 0x%lx\n", + ipcBuf[3], ipcBuf[4], ipcBuf[5]); + } + /* Flushing the L1 cache and invalidating the TLB is good enough here to * make sure everything written by the kernel is visible to userland. There * are no uncached userland frames at this stage that require enforcing diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index cd3f90a..5d06bdf 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -1,6 +1,6 @@ #include "engine.h" #include - +#include extern FrontendEngine *p_g_fr_eng; extern FrontendEngine g_fr_eng; @@ -593,9 +593,20 @@ int engine_init_hyperamp_queue(FrontendEngine *eng){ return FRONTEND_PROXY_PROCESS_ERROR; } - g_hyper_tx_queue = SHM_TX_QUEUE_VADDR; - g_hyper_rx_queue = SHM_RX_QUEUE_VADDR; - g_hyper_data_region = SHM_DATA_REGION_VA; + // g_hyper_tx_queue = SHM_TX_QUEUE_VADDR; + // g_hyper_rx_queue = SHM_RX_QUEUE_VADDR; + // g_hyper_data_region = SHM_DATA_REGION_VA; + // 从 IPC buffer msg[] 字段读取共享内存虚拟地址 + // boot.c 写入 msg[2..4], 因为 sel4runtime 的 seL4_DebugNameThread + // 会将 "rootserver" 写入 msg[0..1] + //**不要在seL4_GetMR() 之前插入任何 seL4 系统调用,否则地址会被覆盖掉!!! + g_hyper_tx_queue = (volatile HyperampShmQueue *)seL4_GetMR(2); // TX: seL4 → Linux + g_hyper_tx_queue = (volatile HyperampShmQueue *)seL4_GetMR(3); // RX: Linux → seL4 + g_hyper_data_region = (volatile void *) seL4_GetMR(4); // Data Region: 4MB + printf("[seL4] Shared Memory Addresses (from IPC msg[2..4]):\n"); + printf(" TX Queue (seL4->Linux): %p\n", (void *)g_hyper_tx_queue); + printf(" RX Queue (Linux->seL4): %p\n", (void *)g_hyper_tx_queue); + printf(" Data Region : %p\n", (void *)g_hyper_data_region); ret = hyperamp_queue_init(g_hyper_tx_queue, &hyperamp_tx_config, 1); diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 79380cb..1c177d9 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -75,22 +74,13 @@ void test_session_event_callback(struct FrontendSession* sess, FrontendSessionEv int main(void){ - printf("Running tests\n"); FrontendEngine *eng; struct FrontendSession *sess; ProxyMsgHeader *proxy_msg_hdr; int ret; size_t block_size; uint8_t msg_buf[HYPERAMP_MSG_HDR_PLUS_MAX_SIZE]; -#if 0 - seL4_Word* Ptr2ShmemCommBuff= (seL4_Word*)seL4_GetIPCBuffer(); - unsigned long long *vaddrs=(unsigned long long *)*Ptr2ShmemCommBuff; - int* a = malloc(10*sizeof(int)); - *a = 1; - printf("a = %d, addr of a = %p\n", *a, a); - tailq_test(); - uthash_test(); -#endif + frontend_engine_init(); eng = frontend_get_global_engine(); diff --git a/projects/sel4test/apps/hyperamp-server/src/main.c b/projects/sel4test/apps/hyperamp-server/src/main.c index 92fadf2..e83f585 100644 --- a/projects/sel4test/apps/hyperamp-server/src/main.c +++ b/projects/sel4test/apps/hyperamp-server/src/main.c @@ -956,28 +956,18 @@ int main(void) printf(" Compatible with HighSpeedCProxy\n"); printf("================================================\n\n"); - // 从 IPC buffer 获取共享内存地址 - // boot.c 存储方式: IPC buffer 第一个 word 存储指向地址数组的指针 - // 该数组包含: [TX Queue vaddr, RX Queue vaddr, Data Region vaddr] - seL4_Word *ipc_buf = (seL4_Word *)seL4_GetIPCBuffer(); - unsigned long long *vaddrs = (unsigned long long *)*ipc_buf; - - // HyperAMP 4KB 队列布局 (与 Linux 端和内核配置匹配) - // 正确的架构: - // - TX Queue (0xDE000000): seL4 → Linux (seL4 前端发送请求,Linux 后端接收) - // - RX Queue (0xDE001000): Linux → seL4 (Linux 后端发送响应,seL4 前端接收) - - // g_tx_queue = (volatile HyperampShmQueue *)vaddrs[0]; // TX: seL4 → Linux (seL4 写请求) - // g_rx_queue = (volatile HyperampShmQueue *)vaddrs[1]; // RX: Linux → seL4 (seL4 读响应) - // g_data_region = (volatile void *)vaddrs[2]; // Data Region: 4MB - g_tx_queue = (volatile HyperampShmQueue *)0x54e000; - g_rx_queue = (volatile HyperampShmQueue *)0x54f000; - g_data_region = (volatile void *)0x550000; - - printf("[seL4] Shared Memory Addresses:\n"); - printf(" TX Queue[0xde000000] (seL4->Linux): %p\n", (void *)g_tx_queue); - printf(" RX Queue[0xde001000] (Linux->seL4): %p\n", (void *)g_rx_queue); - printf(" Data Region: %p (shared 4MB)\n", (void *)g_data_region); + // 从 IPC buffer msg[] 字段读取共享内存虚拟地址 + // boot.c 写入 msg[2..4], 因为 sel4runtime 的 seL4_DebugNameThread + // 会将 "rootserver" 写入 msg[0..1] + //**不要在seL4_GetMR() 之前插入任何 seL4 系统调用,否则地址会被覆盖掉!!! + g_tx_queue = (volatile HyperampShmQueue *)seL4_GetMR(2); // TX: seL4 → Linux + g_rx_queue = (volatile HyperampShmQueue *)seL4_GetMR(3); // RX: Linux → seL4 + g_data_region = (volatile void *) seL4_GetMR(4); // Data Region: 4MB + + printf("[seL4] Shared Memory Addresses (from IPC msg[2..4]):\n"); + printf(" TX Queue (seL4->Linux): %p\n", (void *)g_tx_queue); + printf(" RX Queue (Linux->seL4): %p\n", (void *)g_rx_queue); + printf(" Data Region : %p\n", (void *)g_data_region); // 验证地址有效性 if (!g_tx_queue || !g_rx_queue || !g_data_region) { -- Gitee From 9280e400af5b798388955ae46e7ec6fe6ce80ba8 Mon Sep 17 00:00:00 2001 From: lr Date: Mon, 16 Mar 2026 16:19:40 +0800 Subject: [PATCH 096/133] =?UTF-8?q?feat:=E4=BC=98=E5=8C=96=20HyperAMP=20?= =?UTF-8?q?=E8=87=AA=E6=97=8B=E9=94=81=E5=AE=9E=E7=8E=B0=EF=BC=8C=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E5=8E=9F=E5=AD=90=E6=8C=87=E4=BB=A4=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=87=AA=E6=97=8B=E9=94=81=EF=BC=8C=E6=8E=A7=E5=88=B6=E9=98=9F?= =?UTF-8?q?=E5=88=97=E4=B8=BANORMAL=EF=BC=8C=E6=95=B0=E6=8D=AE=E5=8C=BA?= =?UTF-8?q?=E4=B8=BADEVICE=5FnGnRnE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/arm/64/kernel/vspace.c | 29 +++-- .../apps/front/include/hyperamp_shm_queue.h | 13 +- projects/sel4test/apps/front/src/engine.c | 4 +- .../apps/front/src/hyperamp_shm_queue.c | 98 ++++++++------- .../include/shm/hyperamp_shm_queue.h | 112 +++++++++--------- 5 files changed, 130 insertions(+), 126 deletions(-) diff --git a/kernel/src/arch/arm/64/kernel/vspace.c b/kernel/src/arch/arm/64/kernel/vspace.c index 4bdad9d..ca710bc 100755 --- a/kernel/src/arch/arm/64/kernel/vspace.c +++ b/kernel/src/arch/arm/64/kernel/vspace.c @@ -337,16 +337,16 @@ static BOOT_CODE void map_4MB_phys_to_vaddr(vspace_root_t *vspaceRoot, /* 在 PT 中写入 4KB 页表项 */ pt += GET_UPT_INDEX(vptr, ULVL_FRM_ARM_PT_LVL(3)); - /* HyperAMP: 对共享内存区域使用 uncached 映射 (Stage-1 for EL1 guest) */ + /* HyperAMP: 4MB 数据区始终 DEVICE_nGnRnE (uncached) */ word_t mem_attr; - if (cur_paddr >= SHM_TX_QUEUE_PADDR && cur_paddr < (SHM_DATA_PADDR + SHM_DATA_SIZE)) { - mem_attr = DEVICE_nGnRnE; /* Stage-1: Uncached device memory for shared memory */ - if (i == 0) { /* 只打印第一页 */ - printf("[HyperAMP] map_4MB: PA 0x%lx -> DEVICE_nGnRnE (uncached)\n", + if (cur_paddr >= SHM_DATA_PADDR && cur_paddr < (SHM_DATA_PADDR + SHM_DATA_SIZE)) { + mem_attr = DEVICE_nGnRnE; + if (i == 0) { + printf("[HyperAMP] map_4MB: PA 0x%lx -> DEVICE_nGnRnE (uncached)\n", (unsigned long)cur_paddr); } } else { - mem_attr = NORMAL; /* Stage-1: Normal cacheable memory */ + mem_attr = NORMAL; } *(pt) = pte_pte_4k_page_new( @@ -395,15 +395,22 @@ static BOOT_CODE void map_it_frame_cap(cap_t vspace_cap, cap_t frame_cap, bool_t assert(pte_pte_table_ptr_get_present(pd)); pt = paddr_to_pptr(pte_pte_table_ptr_get_pt_base_address(pd)); - /* HyperAMP: 对共享内存区域使用 uncached 映射 (Stage-1 for EL1 guest) */ + /* HyperAMP: 队列控制块用 NORMAL (支持 LDAXR/STLXR 原子指令) + * 数据区用 DEVICE_nGnRnE (uncached) */ paddr_t frame_paddr = pptr_to_paddr(pptr); word_t mem_attr; - if (frame_paddr >= SHM_TX_QUEUE_PADDR && frame_paddr < (SHM_DATA_PADDR + SHM_DATA_SIZE)) { - mem_attr = DEVICE_nGnRnE; /* Stage-1: Uncached device memory for shared memory */ - printf("[HyperAMP] map_it_frame_cap: PA 0x%lx -> DEVICE_nGnRnE (uncached)\n", + if (frame_paddr >= SHM_TX_QUEUE_PADDR && frame_paddr < SHM_DATA_PADDR) { + /* TX/RX Queue 页: NORMAL WB (LDAXR/STLXR 要求) */ + mem_attr = NORMAL; + printf("[HyperAMP] map_it_frame_cap: PA 0x%lx -> NORMAL (for spinlock atomics)\n", + (unsigned long)frame_paddr); + } else if (frame_paddr >= SHM_DATA_PADDR && frame_paddr < (SHM_DATA_PADDR + SHM_DATA_SIZE)) { + /* 数据区: DEVICE_nGnRnE (uncached) */ + mem_attr = DEVICE_nGnRnE; + printf("[HyperAMP] map_it_frame_cap: PA 0x%lx -> DEVICE_nGnRnE (uncached)\n", (unsigned long)frame_paddr); } else { - mem_attr = NORMAL; /* Stage-1: Normal cacheable memory */ + mem_attr = NORMAL; } *(pt + GET_UPT_INDEX(vptr, ULVL_FRM_ARM_PT_LVL(3))) = pte_pte_4k_page_new( diff --git a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h index b75c612..3c19229 100644 --- a/projects/sel4test/apps/front/include/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/front/include/hyperamp_shm_queue.h @@ -46,10 +46,9 @@ typedef enum { #if defined(__aarch64__) || defined(__arm__) #define HYPERAMP_DMB() __asm__ volatile("dmb sy" ::: "memory") - #define HYPERAMP_DSB() __asm__ volatile("dsb sy" ::: "memory") #define HYPERAMP_ISB() __asm__ volatile("isb" ::: "memory") - /* Data cache clean – flush cache lines to main memory (used for shared memory writes) */ + /* 缓存操作 - 仅用于数据区 (DEVICE_nGnRnE uncached),队列控制块由 LDAXR/STLXR 处理一致性 */ static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { volatile char *p = (volatile char *)addr; volatile char *end = p + size; @@ -71,7 +70,6 @@ typedef enum { } #else #define HYPERAMP_DMB() __asm__ volatile("mfence" ::: "memory") - #define HYPERAMP_DSB() __asm__ volatile("mfence" ::: "memory") #define HYPERAMP_ISB() __asm__ volatile("" ::: "memory") static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { @@ -122,7 +120,8 @@ typedef enum { //g_data_region = (volatile void *)0x550000; -#define HYPERAMP_BARRIER() do { HYPERAMP_DMB(); HYPERAMP_DSB(); } while(0) +/* HYPERAMP_BARRIER 仅使用 DMB:DSB 已由 LDXR/STLXR 内置语义取代 */ +#define HYPERAMP_BARRIER() HYPERAMP_DMB() /* ==================== Software Spinlock ==================== */ @@ -131,8 +130,10 @@ typedef struct { volatile uint32_t owner_zone_id; volatile uint32_t lock_count; volatile uint32_t contention_count; -} __attribute__((packed)) HyperampSpinlock; - +} __attribute__((aligned(4))) HyperampSpinlock; +/* 注:移除 __packed__,保留 __aligned(4)。 + * lock_value 必须 4 字节对齐以支持 LDAXR/STXR。4 个 uint32_t 自然对齐,sizeof = 16B。 + */ /* ==================== Address Mapping Table Entry ==================== */ typedef struct { diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 5d06bdf..2c4b695 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -601,11 +601,11 @@ int engine_init_hyperamp_queue(FrontendEngine *eng){ // 会将 "rootserver" 写入 msg[0..1] //**不要在seL4_GetMR() 之前插入任何 seL4 系统调用,否则地址会被覆盖掉!!! g_hyper_tx_queue = (volatile HyperampShmQueue *)seL4_GetMR(2); // TX: seL4 → Linux - g_hyper_tx_queue = (volatile HyperampShmQueue *)seL4_GetMR(3); // RX: Linux → seL4 + g_hyper_rx_queue = (volatile HyperampShmQueue *)seL4_GetMR(3); // RX: Linux → seL4 g_hyper_data_region = (volatile void *) seL4_GetMR(4); // Data Region: 4MB printf("[seL4] Shared Memory Addresses (from IPC msg[2..4]):\n"); printf(" TX Queue (seL4->Linux): %p\n", (void *)g_hyper_tx_queue); - printf(" RX Queue (Linux->seL4): %p\n", (void *)g_hyper_tx_queue); + printf(" RX Queue (Linux->seL4): %p\n", (void *)g_hyper_rx_queue); printf(" Data Region : %p\n", (void *)g_hyper_data_region); ret = hyperamp_queue_init(g_hyper_tx_queue, &hyperamp_tx_config, 1); diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index 74f1763..5c4780d 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -19,79 +19,75 @@ static const unsigned int TRUSTED_PUBKEY_DER_LEN = 91; static inline void hyperamp_spinlock_init(volatile HyperampSpinlock *lock) { if (!lock) return; - - volatile uint8_t *p = (volatile uint8_t *)lock; - for (size_t i = 0; i < sizeof(HyperampSpinlock); i++) { - p[i] = 0; - } + lock->lock_value = 0; + lock->owner_zone_id = 0; + lock->lock_count = 0; + lock->contention_count = 0; HYPERAMP_BARRIER(); - - /* Flush the lock state to main memory */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); } static inline void hyperamp_spinlock_lock(volatile HyperampSpinlock *lock, uint32_t zone_id) { if (!lock) return; - - int spin_count = 0; - const int max_spin = 100000; - +#if defined(__aarch64__) + uint32_t tmp, newval; + __asm__ volatile( + "1: ldaxr %w0, %2\n" + " cbnz %w0, 1b\n" + " mov %w1, #1\n" + " stlxr %w0, %w1, %2\n" + " cbnz %w0, 1b\n" + : "=&r" (tmp), "=&r" (newval), "+Q" (lock->lock_value) + : + : "memory" + ); + lock->owner_zone_id = zone_id; + lock->lock_count++; +#else while (1) { HYPERAMP_BARRIER(); - - volatile uint32_t current = lock->lock_value; - - if (current == 0) { + if (lock->lock_value == 0) { lock->lock_value = 1; HYPERAMP_BARRIER(); - - volatile uint32_t verify = lock->lock_value; - if (verify == 1) { + if (lock->lock_value == 1) { lock->owner_zone_id = zone_id; lock->lock_count++; - HYPERAMP_BARRIER(); - - /* Flush the updated lock state to main memory, - ensuring other cores/VMs observe that the lock is held */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); return; } } - lock->contention_count++; - spin_count++; - - if (spin_count > max_spin) { - spin_count = 0; -#if defined(__aarch64__) || defined(__arm__) - __asm__ volatile("yield" ::: "memory"); -#else - __asm__ volatile("pause" ::: "memory"); -#endif - } - - for (volatile int i = 0; i < 100; i++) { - HYPERAMP_BARRIER(); - } + __asm__ volatile("pause" ::: "memory"); } +#endif } static inline void hyperamp_spinlock_unlock(volatile HyperampSpinlock *lock) { if (!lock) return; - - HYPERAMP_BARRIER(); lock->owner_zone_id = 0; +#if defined(__aarch64__) + __asm__ volatile( + "stlr wzr, %0\n" + : "+Q" (lock->lock_value) + : + : "memory" + ); +#else + HYPERAMP_BARRIER(); lock->lock_value = 0; HYPERAMP_BARRIER(); - - /* Critical: flush the unlocked state to main memory, - ensuring other cores/VMs observe that the lock is released */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +#endif } +/* 验证 queue_lock.lock_value 在内存中满足 LDAXR/STXR 需要的 4 字节对齐 */ +_Static_assert(offsetof(HyperampShmQueue, queue_lock) % 4 == 0, + "HyperampShmQueue.queue_lock must be 4-byte aligned for LDAXR/STXR"); +_Static_assert(sizeof(HyperampSpinlock) == 16, + "HyperampSpinlock size must be 16 bytes"); +/* 安全获取 packed 结构体中 queue_lock 的指针(避免 -Waddress-of-packed-member) */ +#define HYPERAMP_QUEUE_LOCK(q) \ + ((volatile HyperampSpinlock *)((volatile uint8_t *)(q) + offsetof(HyperampShmQueue, queue_lock))) /* ==================== Secure Memory Operations ==================== */ static inline void hyperamp_safe_memset(volatile void *dst, uint8_t val, size_t len) @@ -201,7 +197,7 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, if (data_len > block_size) return HYPERAMP_ERROR; // Acquire the lock - hyperamp_spinlock_lock(&queue->queue_lock, zone_id); + hyperamp_spinlock_lock(HYPERAMP_QUEUE_LOCK(queue), zone_id); // Safely read header and tail uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); @@ -216,7 +212,7 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, // Check if the queue is full if (new_header == tail) { - hyperamp_spinlock_unlock(&queue->queue_lock); + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); return HYPERAMP_AGAIN; } @@ -248,7 +244,7 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, hyperamp_cache_clean((volatile void *)queue, 64); /* Only flush the first 64 bytes containing control fields */ // Release the lock - hyperamp_spinlock_unlock(&queue->queue_lock); + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); return HYPERAMP_OK; } @@ -311,7 +307,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, HYPERAMP_BARRIER(); // Acquire the lock - hyperamp_spinlock_lock(&queue->queue_lock, zone_id); + hyperamp_spinlock_lock(HYPERAMP_QUEUE_LOCK(queue), zone_id); // Safely read header, tail, block_size, and capacity uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); @@ -322,7 +318,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, // Check if the queue is empty printf("In %s, tail = %u, header = %u\n", __func__, tail, header); if (tail == header) { - hyperamp_spinlock_unlock(&queue->queue_lock); + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); return HYPERAMP_AGAIN; } @@ -387,7 +383,7 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, HYPERAMP_BARRIER(); // Release the lock - hyperamp_spinlock_unlock(&queue->queue_lock); + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); printf("After unlock spinlock\n"); DUMP_PROXY_MSG_HEADER(data); diff --git a/projects/sel4test/apps/hyperamp-server/include/shm/hyperamp_shm_queue.h b/projects/sel4test/apps/hyperamp-server/include/shm/hyperamp_shm_queue.h index 7cc91c7..e4e264d 100644 --- a/projects/sel4test/apps/hyperamp-server/include/shm/hyperamp_shm_queue.h +++ b/projects/sel4test/apps/hyperamp-server/include/shm/hyperamp_shm_queue.h @@ -39,21 +39,18 @@ typedef enum { #if defined(__aarch64__) || defined(__arm__) #define HYPERAMP_DMB() __asm__ volatile("dmb sy" ::: "memory") - #define HYPERAMP_DSB() __asm__ volatile("dsb sy" ::: "memory") #define HYPERAMP_ISB() __asm__ volatile("isb" ::: "memory") - /* 数据缓存清理 - 将缓存行刷新到主存 (用于共享内存写入) */ + /* 缓存操作 - 仅用于数据区 (DEVICE_nGnRnE uncached),队列控制块由 LDAXR/STLXR 处理一致性 */ static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { volatile char *p = (volatile char *)addr; volatile char *end = p + size; - /* ARM64 缓存行通常是 64 字节 */ for (; p < end; p += 64) { __asm__ volatile("dc cvac, %0" : : "r"(p) : "memory"); } __asm__ volatile("dsb sy" ::: "memory"); } - /* 数据缓存失效 - 丢弃缓存内容,强制从内存读取 (用于共享内存读取) */ static inline void hyperamp_cache_invalidate(volatile void *addr, size_t size) { volatile char *p = (volatile char *)addr; volatile char *end = p + size; @@ -64,7 +61,6 @@ typedef enum { } #else #define HYPERAMP_DMB() __asm__ volatile("mfence" ::: "memory") - #define HYPERAMP_DSB() __asm__ volatile("mfence" ::: "memory") #define HYPERAMP_ISB() __asm__ volatile("" ::: "memory") static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { @@ -78,7 +74,8 @@ typedef enum { } #endif -#define HYPERAMP_BARRIER() do { HYPERAMP_DMB(); HYPERAMP_DSB(); } while(0) +/* HYPERAMP_BARRIER 仅使用 DMB:DSB 已由 LDXR/STLXR 内置语义取代 */ +#define HYPERAMP_BARRIER() HYPERAMP_DMB() /* ==================== 软件自旋锁 ==================== */ @@ -87,79 +84,72 @@ typedef struct { volatile uint32_t owner_zone_id; volatile uint32_t lock_count; volatile uint32_t contention_count; -} __attribute__((packed)) HyperampSpinlock; +} __attribute__((aligned(4))) HyperampSpinlock; +/* 注:移除 __packed__,保留 __aligned(4)。 + * lock_value 必须 4 字节对齐以支持 LDAXR/STXR。4 个 uint32_t 自然对齐,sizeof = 16B。 + */ static inline void hyperamp_spinlock_init(volatile HyperampSpinlock *lock) { if (!lock) return; - - volatile uint8_t *p = (volatile uint8_t *)lock; - for (size_t i = 0; i < sizeof(HyperampSpinlock); i++) { - p[i] = 0; - } + lock->lock_value = 0; + lock->owner_zone_id = 0; + lock->lock_count = 0; + lock->contention_count = 0; HYPERAMP_BARRIER(); - - /* 刷新锁状态到主存 */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); } static inline void hyperamp_spinlock_lock(volatile HyperampSpinlock *lock, uint32_t zone_id) { if (!lock) return; - - int spin_count = 0; - const int max_spin = 100000; - +#if defined(__aarch64__) + uint32_t tmp, newval; + __asm__ volatile( + "1: ldaxr %w0, %2\n" + " cbnz %w0, 1b\n" + " mov %w1, #1\n" + " stlxr %w0, %w1, %2\n" + " cbnz %w0, 1b\n" + : "=&r" (tmp), "=&r" (newval), "+Q" (lock->lock_value) + : + : "memory" + ); + lock->owner_zone_id = zone_id; + lock->lock_count++; +#else while (1) { HYPERAMP_BARRIER(); - - volatile uint32_t current = lock->lock_value; - - if (current == 0) { + if (lock->lock_value == 0) { lock->lock_value = 1; HYPERAMP_BARRIER(); - - volatile uint32_t verify = lock->lock_value; - if (verify == 1) { + if (lock->lock_value == 1) { lock->owner_zone_id = zone_id; lock->lock_count++; - HYPERAMP_BARRIER(); - - /* 刷新锁状态到主存,确保其他核心/虚拟机能看到锁已被占用 */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); return; } } - lock->contention_count++; - spin_count++; - - if (spin_count > max_spin) { - spin_count = 0; -#if defined(__aarch64__) || defined(__arm__) - __asm__ volatile("yield" ::: "memory"); -#else - __asm__ volatile("pause" ::: "memory"); -#endif - } - - for (volatile int i = 0; i < 100; i++) { - HYPERAMP_BARRIER(); - } + __asm__ volatile("pause" ::: "memory"); } +#endif } static inline void hyperamp_spinlock_unlock(volatile HyperampSpinlock *lock) { if (!lock) return; - - HYPERAMP_BARRIER(); lock->owner_zone_id = 0; +#if defined(__aarch64__) + __asm__ volatile( + "stlr wzr, %0\n" + : "+Q" (lock->lock_value) + : + : "memory" + ); +#else + HYPERAMP_BARRIER(); lock->lock_value = 0; HYPERAMP_BARRIER(); - - /* 关键:刷新锁状态到主存,确保其他核心/虚拟机能看到锁已释放 */ - hyperamp_cache_clean((volatile void *)lock, sizeof(HyperampSpinlock)); +#endif } /* ==================== 地址映射表项 ==================== */ @@ -196,6 +186,16 @@ typedef struct { } __attribute__((packed)) HyperampShmQueue; +/* 验证 queue_lock.lock_value 在内存中满足 LDAXR/STXR 需要的 4 字节对齐 */ +_Static_assert(offsetof(HyperampShmQueue, queue_lock) % 4 == 0, + "HyperampShmQueue.queue_lock must be 4-byte aligned for LDAXR/STXR"); +_Static_assert(sizeof(HyperampSpinlock) == 16, + "HyperampSpinlock size must be 16 bytes"); + +/* 安全获取 packed 结构体中 queue_lock 的指针(避免 -Waddress-of-packed-member) */ +#define HYPERAMP_QUEUE_LOCK(q) \ + ((volatile HyperampSpinlock *)((volatile uint8_t *)(q) + offsetof(HyperampShmQueue, queue_lock))) + /* ==================== 消息头结构 ==================== */ typedef struct { @@ -393,7 +393,7 @@ static inline int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, if (data_len > block_size) return HYPERAMP_ERROR; // 获取锁 - hyperamp_spinlock_lock(&queue->queue_lock, zone_id); + hyperamp_spinlock_lock(HYPERAMP_QUEUE_LOCK(queue), zone_id); // 安全读取 header 和 tail uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); @@ -407,7 +407,7 @@ static inline int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, // 检查队列是否满 if (new_header == tail) { - hyperamp_spinlock_unlock(&queue->queue_lock); + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); return HYPERAMP_AGAIN; } @@ -439,7 +439,7 @@ static inline int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, hyperamp_cache_clean((volatile void *)queue, 64); /* 只刷新前 64 字节控制字段 */ // 释放锁 - hyperamp_spinlock_unlock(&queue->queue_lock); + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); return HYPERAMP_OK; } @@ -468,7 +468,7 @@ static inline int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, hyperamp_cache_invalidate((volatile void *)queue, 64); // 获取锁 - hyperamp_spinlock_lock(&queue->queue_lock, zone_id); + hyperamp_spinlock_lock(HYPERAMP_QUEUE_LOCK(queue), zone_id); // 安全读取 header, tail, block_size, capacity uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); @@ -478,7 +478,7 @@ static inline int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, // 检查队列是否为空 if (tail == header) { - hyperamp_spinlock_unlock(&queue->queue_lock); + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); return HYPERAMP_AGAIN; } @@ -520,7 +520,7 @@ static inline int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, HYPERAMP_BARRIER(); // 释放锁 - hyperamp_spinlock_unlock(&queue->queue_lock); + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); return HYPERAMP_OK; } -- Gitee From 69e2efbc5fd1f8e9c17a6d6e22c982cfe77b140c Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 19 Mar 2026 12:01:22 +0800 Subject: [PATCH 097/133] IoT codes --- projects/sel4test/apps/front/include/dev.h | 140 +++++++++- projects/sel4test/apps/front/include/engine.h | 1 + .../sel4test/apps/front/include/message.h | 261 +++++++++++++++++- .../sel4test/apps/front/include/session.h | 102 +++++++ .../sel4test/apps/front/src/frontend_proto.c | 6 +- 5 files changed, 503 insertions(+), 7 deletions(-) diff --git a/projects/sel4test/apps/front/include/dev.h b/projects/sel4test/apps/front/include/dev.h index 8505f0e..0fbfe9d 100644 --- a/projects/sel4test/apps/front/include/dev.h +++ b/projects/sel4test/apps/front/include/dev.h @@ -1,11 +1,13 @@ #ifndef DEV_H_ #define DEV_H_ +#include + #define MAX_DEV_NAME 32 #define MAX_HS_DEV_NUM 16 #define MIN_HS_DEV_NUM 1 - +#define MAX_IOT_DEV_NUM 16 /* * High Speed Network device type enumeration * Contains five common types of network devices @@ -56,4 +58,140 @@ extern FrontendDevListCfg *p_global_dev_list_cfg; void frontend_init_dev_list(); + + +/** + * @brief IoT device type enumeration (matches IotProtoType in message definition) + */ +typedef enum { + IOT_DEV_TYPE_UNKNOWN = 0, // Unknown IoT device type + IOT_DEV_TYPE_BLUETOOTH, // Bluetooth device (BLE/Classic Bluetooth) + IOT_DEV_TYPE_ZIGBEE, // Zigbee device (802.15.4) + IOT_DEV_TYPE_CAN, // CAN bus device (CAN 2.0/CAN FD) + IOT_DEV_TYPE_LORA, // LoRa/LoRaWAN device + IOT_DEV_TYPE_POWERLINK, // OpenPowerLink (Ethernet POWERLINK) device + IOT_DEV_TYPE_MODBUSTCP // Modbus TCP device (Modbus protocol over TCP/IP). +} IotDevType; + +/** + * @brief IoT device status enumeration + */ +typedef enum IotDevStatus_{ + IOT_DEV_STATUS_OFFLINE = 0, // Device offline (disconnected/unavailable) + IOT_DEV_STATUS_ONLINE, // Device online (connected/available) + IOT_DEV_STATUS_ERROR, // Device error (fault/abnormal state) + IOT_DEV_STATUS_CONFIGURING // Device being configured (temporary state) +} IotDevStatus; + + +/** + * @brief Bluetooth device specific parameters + */ +typedef struct BluetoothDevAttr_{ + uint16_t bt_port; // Bluetooth port/channel number + uint8_t bt_mac[6]; // Bluetooth MAC address (6 bytes) + uint8_t bt_version; // Bluetooth version (0x01=BLE 5.0, 0x02=BLE 5.1, etc.) + uint16_t conn_interval; // BLE connection interval (unit: ms) +} BluetoothDevAttr; + +/** + * @brief CAN device specific parameters + */ +typedef struct CANDevAttr_{ + uint16_t can_port; // CAN port number + uint32_t can_bitrate; // CAN bus bitrate (bps, e.g., 500000 for 500Kbps) + uint8_t can_mode; // CAN mode (0x01=normal, 0x02=loopback, 0x03=silent) + uint32_t can_filter_id; // CAN filter ID (for frame filtering) +} CANDevAttr; + +/** + * @brief Zigbee device specific parameters + */ +typedef struct ZigbeeDevAttr_{ + uint16_t zigbee_pan_id; // Zigbee PAN ID + uint8_t zigbee_channel; // Zigbee channel (range: 11-26) + uint8_t zigbee_mac[8]; // Zigbee MAC address (8 bytes) + uint8_t zigbee_role; // Zigbee role (0x01=coordinator, 0x02=router, 0x03=end device) +} ZigbeeDevAttr; + +/** + * @brief LoRa device specific parameters + */ +typedef struct LoRaDevAttr_{ + uint16_t lora_port; // LoRa port number + uint8_t lora_freq_band; // LoRa frequency band (0x01=EU868, 0x02=US915, 0x03=CN470, etc.) + uint8_t lora_sf; // LoRa spreading factor (range: 7-12) + uint8_t lora_cr; // LoRa coding rate (range: 1-4, corresponds to 4/5 ~ 4/8) + uint32_t lora_dev_eui; // LoRa device EUI (unique identifier) +} LoRaDevAttr; + + + +/** + * @brief OpenPowerLink device specific parameters (Industrial Real-Time Ethernet) + */ +typedef struct PowerLinkDevAttr_ { + uint16_t plk_port; // POWERLINK port number (corresponding to Ethernet interface) + uint8_t plk_mac[6]; // Device MAC address (6 bytes) + uint16_t plk_node_id; // POWERLINK NodeID (range: 1-240) + uint8_t plk_role; // POWERLINK role (0: MN (Managing Node), 1: CN (Controlled Node)) + uint32_t plk_cycle_ms; // Real-time cycle time (unit: ms, typically 1~10ms) + uint16_t plk_rx_pdo_len; // Length of received PDO (Process Data Object) + uint16_t plk_tx_pdo_len; // Length of transmitted PDO (Process Data Object) +} PowerLinkDevAttr; + +/** + * @brief Union for IoT device specific attributes (memory optimization) + */ +typedef union { + BluetoothDevAttr bt_attr; // Bluetooth device attributes + CANDevAttr can_attr; // CAN device attributes + ZigbeeDevAttr zigbee_attr; // Zigbee device attributes + LoRaDevAttr lora_attr; // LoRa device attributes + PowerLinkDevAttr plk_attr; // OpenPowerLink device attributes +} IotDevSpecificAttr; + +/** + * @brief IoT device performance statistics + */ +typedef struct IotDevStat_{ + uint64_t tx_packets; // Total transmitted packets + uint64_t rx_packets; // Total received packets + uint64_t tx_bytes; // Total transmitted bytes + uint64_t rx_bytes; // Total received bytes + uint32_t error_count; // Total error count + uint64_t last_active_ts; // Last active timestamp (ms since epoch) +} IotDevStat; + + +/** + * @brief Main structure for IoT device (Bluetooth/CAN/Zigbee/LoRa/POWERLINK) + * @note Refer to HighSpeedNetDevice design, optimized for IoT characteristics + */ +typedef struct IotDevice_ { + // Device identification information + int dev_id; // Unique device ID (global) + IotDevType dev_type; // IoT device type + char *ns_name; // Namespace name (for device grouping) + IotDevStatus dev_status; // Device online/offline status + + // Device attribute information + char name[MAX_DEV_NAME]; // Device name (human-readable) + IotDevSpecificAttr specific_attr; // Protocol-specific attributes + + // Session connection information + int sess_id; // Associated session ID (initialized on startup) + + // Performance statistics information + IotDevStat stat; // Device performance statistics + + // Device hardware/port information + int physical_port; // Physical port number (e.g., /dev/ttyUSB0 mapped to ID) + int fd; // Device file descriptor (for hardware access) +} IotDevice; + +typedef struct FrontendIoTDeviceSet_ { + IotDevice iot_dev[MAX_IOT_DEV_NUM]; +}FrontendIoTDeviceSet; + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index af8cf54..9866b52 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -78,6 +78,7 @@ typedef enum { typedef struct FrontendEngine_{ uint16_t dev_num; FrontendHighSpeedNetDeviceSet *dev_set; // High speed network device set + FrontendIoTDeviceSet *iot_dev_set; // IoT device set uint16_t sel_id; uint16_t active_mask; // Show the positions of all the active high-speed network devices as a mask. uint16_t eng_cmd_id; // Used when constructing device (dev) messages and strategy (strgy) messages, filled into the msg_id member of these messages. diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index 1e1da6d..21f7fe1 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -580,14 +580,157 @@ typedef struct { } __attribute__((packed)) SessIPv6Params; + +/** + * @brief IoT protocol type enumeration for IoT message sub-header + * @note Used to distinguish different IoT protocols in PROXY_MSG_TYPE_IOT payload + */ +typedef enum { + IOT_PROTO_TYPE_BLUETOOTH = 1, // Bluetooth protocol (BLE/Classic Bluetooth) + IOT_PROTO_TYPE_ZIGBEE, // Zigbee protocol (802.15.4) + IOT_PROTO_TYPE_CAN, // CAN bus protocol (CAN 2.0/CAN FD) + IOT_PROTO_TYPE_LORA, // LoRa/LoRaWAN protocol + IOT_PROTO_TYPE_POWERLINK, // OpenPowerLink (Ethernet POWERLINK) protocol + IOT_PROTO_TYPE_MODBUSTCP // OpenPowerLink (Ethernet POWERLINK) protocol +} IotProtoType; + +/** + * @brief IoT message operation code enumeration + * @note Defines universal operation semantics for IoT message interaction + */ +typedef enum { + IOT_OPCODE_DATA_REPORT = 0, // Device actively reports data (receive direction) + IOT_OPCODE_CMD_DOWNLINK, // Backend sends control command (send direction) + IOT_OPCODE_DEVICE_DISCOVER, // Discover IoT devices in the network + IOT_OPCODE_DEVICE_REGISTER, // Device network access/registration + IOT_OPCODE_STATUS_QUERY, // Query device status (connection/signal/power) + IOT_OPCODE_PROTO_CONFIG, // Configure IoT protocol parameters + IOT_OPCODE_EXCEPTION_NOTIFY // Report protocol/device exception events +} IotOpcode; + +/** + * @brief IoT message header (for PROXY_MSG_TYPE_IOT payload) + * @note Fixed length: 10 bytes (packed structure) + * @note Field order: proto_ver -> proto_type -> opcode -> dev_port_id -> payload_len -> reserve + */ +typedef struct IotMsgHeader_{ + uint16_t proto_ver; // IoT sub-protocol version (fixed: 0x01 for current version) + uint16_t proto_type; // IoT protocol type (Bluetooth/Zigbee/CAN/LoRa/POWERLINK/MODBUS) + uint16_t opcode; // IoT message operation code + uint16_t dev_port_id; // Device/port ID (to distinguish multi-port/device) + uint16_t payload_len; // Length of IoT protocol raw data payload + uint16_t reserve; // Reserved field (fixed: 0x0000) +} __attribute__((packed)) IotMsgHeader; + + +/* -------------------------- IoT Protocol Address Structures -------------------------- */ +/** + * @brief Bluetooth device address (MAC + port/channel) + */ +typedef struct { + uint8_t mac[18]; /**< Bluetooth MAC string (e.g., "AA:BB:CC:DD:EE:FF" + '\0') */ + uint16_t port; /**< Bluetooth port/channel number (PSM or CID) */ +} __attribute__((packed)) IotBtAddr; + + +/** + * @brief CAN device address (port + CAN ID + bus ID) + */ +typedef struct { + uint16_t port; /**< CAN port number */ + uint32_t can_id; /**< CAN frame ID (11/29-bit) */ + uint8_t bus_id; /**< CAN bus ID (multi-bus system) */ +} __attribute__((packed))IotCanAddr; + +/** + * @brief ZigBee device address (64-bit MAC + PAN ID + endpoint) + */ +typedef struct { + uint8_t mac[8]; /**< ZigBee 64-bit extended MAC address */ + uint16_t pan_id; /**< ZigBee PAN ID */ + uint8_t endpoint; /**< ZigBee endpoint (0~255) */ +} __attribute__((packed))IotZigbeeAddr; + +/** + * @brief LoRa device address (DevEUI + port + frequency band) + */ +typedef struct { + uint64_t dev_eui; /**< LoRa unique device EUI */ + uint16_t port; /**< LoRa application port */ + uint8_t freq_band; /**< LoRa frequency band (EU868/US915/CN470) */ +} __attribute__((packed))IotLoraAddr; + +/** + * @brief PowerLink device address (NodeID + MAC + PDO ID) + */ +typedef struct { + uint16_t node_id; /**< PowerLink node ID (1~240) */ + uint8_t mac[6]; /**< PowerLink MAC address */ + uint16_t pdo_id; /**< PDO object identifier */ +} __attribute__((packed))IotPowerLinkAddr; + + +/** + * @brief Modbus TCP device address (IPv4 + Port) + * + * Note: Modbus TCP addressing relies on the TCP/IP stack. + * - IP Address: Target server/client IPv4 address. + * - Port: Typically 502 (standard), but can be custom. + * - Unit ID is usually part of the PDU payload, not the transport address, + * so it is not included here to keep the struct compact. + */ +typedef struct { + uint8_t ip[4]; /**< IPv4 address (e.g., 192.168.1.10) */ + uint16_t port; /**< TCP port number (Default: 502) */ +} __attribute__((packed)) IotModbusTcpAddr; + + +/* -------------------------- Unified IoT Address Structure -------------------------- */ +/** + * @brief Unified IoT device address structure (type + union address) + */ +/** + * @brief Unified IoT device address structure (type + union address) + */ +typedef struct IotAddr_ { + IotProtoType addr_type; /**< Protocol type (matches session type) */ + union { + IotBtAddr bt_addr; /**< Bluetooth address */ + IotCanAddr can_addr; /**< CAN address */ + IotZigbeeAddr zigbee_addr; /**< ZigBee address */ + IotLoraAddr lora_addr; /**< LoRa address */ + IotPowerLinkAddr powerlink_addr; /**< PowerLink address */ + IotModbusTcpAddr modbus_tcp_addr;/**< Modbus TCP address (New) */ + uint8_t raw[16]; /**< Raw fallback bytes (max 16 bytes) */ + } addr_info; +} IotAddr; + +typedef struct IotMsgBuffer_ { + uint8_t *data; /**< Pointer to raw data buffer */ + uint32_t len; /**< Length of data (bytes) */ + uint32_t msg_id; /**< Unique message ID (for tracking) */ + uint64_t timestamp; /**< Message timestamp (ms since epoch) */ + IotAddr addr; /**< Address info: + - Send: Destination address + - Receive: Source address */ + void *ext_info; /**< Protocol-specific extended info (beyond address) */ +} IotMsgBuffer; + typedef struct{ ProxyMsgHeader outer_header; // ProxyMsgType msg_type; union { // Nested union to reduce memory usage (avoids redundant space) - DevMsgHeader dev_hdr; // Device message header member - StrgyMsgHeader strgy_hdr; // Strategy message header member - SessMsgHeader sess_hdr; // Session message header member + DevMsgHeader dev_hdr; // Device message header member + StrgyMsgHeader strgy_hdr; // Strategy message header member + SessMsgHeader sess_hdr; // Session message header member + IotMsgHeader iot_hdr; // IoT message header member } inner_header; // Nested union alias for easy access to specific headers + IotAddr iot_addr; /**< IoT address (ONLY used for IoT messages; 0-initialized for non-IoT) + - Stores bt_addr/can_addr/zigbee_addr/lora_addr/powerlink_addr + - Size: 18 bytes (IotProtoType + 16-byte union) → minimal overhead */ + uint16_t iot_addr_len; /**< Length of valid IoT address data (bytes) + - 0: Non-IoT message (ignore iot_addr) + - >0: IoT message (valid address length for specific protocol) */ } GeneralProxyMsgHeader; struct FrontendEngine_; @@ -599,6 +742,87 @@ typedef enum { MEMORY_ALLOC_AMPQUEUE // Message is placed directly into the HyperAMP queue } MemoryAllocMode; + +/* -------------------------------------------------------------------------- */ +/* Protocol Payload Limits */ +/* -------------------------------------------------------------------------- */ + +/** + * @brief Maximum pure payload for Bluetooth (Classic/BLE). + * + * Standards Reference: + * - BLE: Default ATT MTU is 23 bytes (20 bytes payload). With MTU exchange, + * it can go up to 247 bytes (244 bytes payload). + * - Classic Bluetooth: Varies by L2CAP configuration. + * + * Configured Value: 244 bytes (Assumes BLE MTU negotiation enabled). + * Adjust to 20 if operating in legacy BLE mode without MTU exchange. + */ +#define BACKEND_BLUETOOTH_MAX_PAYLOAD (244) + +/** + * @brief Maximum pure payload for ZigBee (IEEE 802.15.4). + * + * Standards Reference: + * - IEEE 802.15.4 MAC frame max is 127 bytes. + * - After subtracting MAC, Network (NWK), and APS headers, the APS payload + * typically allows ~80 to 100 bytes without fragmentation. + * + * Configured Value: 100 bytes (Conservative limit to avoid fragmentation). + */ +#define BACKEND_ZIGBEE_MAX_PAYLOAD (100) + +/** + * @brief Maximum pure payload for LoRa / LoRaWAN. + * + * Standards Reference: + * - LoRaWAN payload size depends on Spreading Factor (SF) and regional regulations. + * - Typical max payload ranges from 51 bytes (SF12) to 243 bytes (SF7). + * + * Configured Value: 222 bytes (Safe upper bound for most SF settings). + * Reduce to ~50-60 bytes for long-range/low-data-rate scenarios (SF11/SF12). + */ +#define BACKEND_LORA_MAX_PAYLOAD (222) + +/** + * @brief Maximum pure payload for PowerLink (Power Line Communication). + * + * Standards Reference: + * - Depends on specific PLC chipset (e.g., G3-PLC, PRIME, or proprietary). + * - High noise environments often require smaller frames for reliability. + * + * Configured Value: 128 bytes (Typical value for many PLC applications). + * Verify against specific hardware datasheet. + */ +#define BACKEND_POWERLINK_MAX_PAYLOAD (128) + +/** + * @brief Maximum pure payload for CAN bus. + * + * Standards Reference: + * - CAN 2.0 (Standard/Extended): Max 8 bytes data field. + * - CAN FD (Flexible Data-rate): Max 64 bytes data field. + * + * Configured Value: 64 bytes (Assumes CAN FD support). + * IMPORTANT: Change to 8 if using legacy CAN 2.0 hardware. + */ +#define BACKEND_CAN_MAX_PAYLOAD (64) + +/** + * @brief Maximum pure payload for Modbus TCP. + * + * Standards Reference: + * - Modbus TCP ADU (Application Data Unit) is encapsulated in TCP. + * - The MBAP header is 7 bytes. + * - While TCP allows large segments, standard Modbus implementations often + * limit the PDU (Protocol Data Unit) to 253 bytes (0x00FD) to ensure + * compatibility with embedded devices and avoid TCP fragmentation issues. + * - Theoretical max is 65535, but 253 is the de-facto standard safe limit. + * + * Configured Value: 253 bytes (Standard Modbus TCP PDU limit). + */ +#define BACKEND_MODBUS_TCP_MAX_PAYLOAD (253) + int build_proxy_general_message(struct FrontendEngine_ *engine, GeneralProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg, MemoryAllocMode alloc_mode, struct SharedMemoryPoolQueue *ring_buf); int build_proxy_dev_message(DevMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); @@ -606,5 +830,36 @@ int build_proxy_strgy_message(StrgyMsgHeader *header, const uint8_t *payload, si int build_proxy_sess_message(SessMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); int build_proxy_data_message(ProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg); +/** + * @brief Build IoT proxy message (supports 3-layer structure: ProxyMsgHeader + IotMsgHeader + IotAddr + payload) + * + * Core construction function for IoT proxy messages (called by build_proxy_general_message): + * 1. Validates IotMsgHeader (proto_type/opcode/payload_len) and IotAddr (addr_type matches proto_type) + * 2. Calculates total length: sizeof(ProxyMsgHeader) + sizeof(IotMsgHeader) + header->iot_addr_len + payload_len + * 3. Allocates memory for the full message (heap/pool based on caller context) + * 4. Writes data in order: + * - ProxyMsgHeader (from header->outer_header) + * - IotMsgHeader (from header->inner_header.iot_hdr) + * - IotAddr (protocol-specific address from header->iot_addr, truncated to header->iot_addr_len) + * - Payload (raw data from payload parameter) + * 5. Updates ProxyMsgHeader.total_len with the full message length + * + * @param header Pointer to GeneralProxyMsgHeader (must contain valid iot_hdr + iot_addr + iot_addr_len) + * @param payload Pointer to IoT payload (raw data after address; NULL if no payload) + * @param payload_len Length of IoT payload (bytes; 0 if no payload) + * @param result_msg Output pointer to constructed IoT proxy message (allocated internally) + * @return int Construction result + * - BACKEND_PROXY_PROCESS_OK: IoT message built successfully + * - BACKEND_PROXY_PROCESS_ERROR: Failed (invalid parameters/address/protocol mismatch/memory error) + * + * @note Validates protocol consistency: header->inner_header.iot_hdr.proto_type must match header->iot_addr.addr_type + * @note header->iot_addr_len must be >0 and ≤ sizeof(IotAddr) (e.g., 8 for bt_addr, 7 for can_addr) + * @note IotMsgHeader.payload_len is automatically set to (header->iot_addr_len + payload_len) + * @note Handles frontend-to-backend IoT messages transmitted via HyperAMP (same as other proxy messages) + */ +int build_proxy_iot_message(GeneralProxyMsgHeader *header, + const uint8_t *payload, + size_t payload_len, + uint8_t **result_msg); #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index 1580f4c..6cfe243 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -413,4 +413,106 @@ void default_session_event_callback(struct FrontendSession* sess, FrontendSessio int session_send(struct FrontendSession* sess, const uint8_t* data, uint32_t size); int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size); + +/** + * @brief IoT Session related event types + * + * This enumeration defines all event types involved in the lifecycle, command interaction, + * data reporting, and exception handling of an IoT device session (IoTSession). + * It is tailored for scenarios involving device registration, remote control, and telemetry. + */ +typedef enum { + /* --- Lifecycle Events --- */ + + /** + * @brief IoT session registration/login successful. + * Triggered when the device completes authentication and is officially online in the system. + * Distinct from physical connection; implies logical business availability. + */ + IOT_SESS_EVENT_REGISTERED, + + /** + * @brief IoT session logout/unregistered. + * Triggered when the device actively logs out or is kicked offline by the server (e.g., duplicate login). + */ + IOT_SESS_EVENT_UNREGISTERED, + + /** + * @brief Physical connection lost. + * Triggered when the underlying transport (TCP/MQTT/CoAP/BLE) disconnects unexpectedly. + * The session may attempt reconnection depending on policy. + */ + IOT_SESS_EVENT_DISCONNECTED, + + /* --- Data & Command Interaction Events --- */ + + /** + * @brief Downlink command received. + * Triggered when the session receives a control instruction from the cloud/app (e.g., "Turn On Light", "Set Temp"). + * Payload requires parsing and execution. + */ + IOT_SESS_EVENT_CMD_RECEIVED, + + /** + * @brief Uplink telemetry/report received. + * Triggered when the session receives periodic status data or sensor readings from the device (e.g., Temperature, GPS). + * Payload is typically forwarded to storage or analysis modules. + */ + IOT_SESS_EVENT_REPORT_RECEIVED, + + /** + * @brief Command execution response received. + * Triggered when the device replies with the result of a previously sent command (Success/Failure/Timeout). + */ + IOT_SESS_EVENT_CMD_RESPONSE, + + /** + * @brief Heartbeat/Ping received. + * Triggered when a keep-alive packet is received, used to reset the session timeout timer. + */ + IOT_SESS_EVENT_HEARTBEAT, + + /** + * @brief Session state synchronization required. + * Triggered when the device reconnects after a dropout and needs to sync its latest state with the server. + */ + IOT_SESS_EVENT_SYNC_REQUIRED, + + /* --- Exception & Error Events --- */ + + /** + * @brief Session timeout. + * Triggered when no heartbeat or data is received within the predefined threshold (Device might be asleep or dead). + */ + IOT_SESS_EVENT_TIMEOUT, + + /** + * @brief Authentication failure. + * Triggered when the device provides invalid credentials, token expired, or unauthorized access attempt. + */ + IOT_SESS_EVENT_AUTH_FAILED, + + /** + * @brief General abnormal error. + * Triggered for unexpected issues: protocol parsing errors, memory allocation failure, or invalid payload format. + */ + IOT_SESS_EVENT_ABNORMAL, + + /** + * @brief Device resource warning (IoT Specific). + * Triggered when the device reports low battery, weak signal, or storage full (if conveyed via session metadata). + */ + IOT_SESS_EVENT_RESOURCE_WARNING, + + /* --- Boundary --- */ + + /** + * @brief Total number of IoT session event types. + * Used for array sizing and boundary checking. Not a valid event type. + */ + IOT_SESS_EVENT_MAX +} IoTSessionEvent; + + + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index d8c5827..fda3eb5 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -204,10 +204,10 @@ int build_proxy_sess_message(SessMsgHeader *sess_hdr, const uint8_t *payload, si * mismatched lengths) or memory allocation fails. */ int build_proxy_data_message(ProxyMsgHeader *proxy_msg_hdr, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ - utils_print("In %s\n", __func__); + printf("In %s\n", __func__); uint8_t *data_msg; - utils_print("In %s, payload_len = %d, proxy_msg_hdr->payload_len = %d\n", __func__, payload_len, proxy_msg_hdr->payload_len); - utils_print("frontend_sess_id = %d, frontend_sess_id =%d\n", proxy_msg_hdr->frontend_sess_id, proxy_msg_hdr->backend_sess_id); + printf("In %s, payload_len = %d, proxy_msg_hdr->payload_len = %d\n", __func__, payload_len, proxy_msg_hdr->payload_len); + printf("frontend_sess_id = %d, frontend_sess_id =%d\n", proxy_msg_hdr->frontend_sess_id, proxy_msg_hdr->backend_sess_id); #if 0 if(payload_len != proxy_msg_hdr->payload_len){ -- Gitee From 029251a8d556e17529b8fd4f8216b6c8f6db2802 Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 19 Mar 2026 16:07:28 +0800 Subject: [PATCH 098/133] frontend prorocol process functions --- .../apps/front/include/frontend_api.h | 4 + .../apps/front/include/frontend_proto.h | 39 ++ .../sel4test/apps/front/include/message.h | 7 +- .../sel4test/apps/front/include/session.h | 153 +++++++ projects/sel4test/apps/front/src/engine.c | 2 +- .../sel4test/apps/front/src/frontend_proto.c | 422 +++++++++++++++++- projects/sel4test/apps/front/src/session.c | 7 + 7 files changed, 627 insertions(+), 7 deletions(-) diff --git a/projects/sel4test/apps/front/include/frontend_api.h b/projects/sel4test/apps/front/include/frontend_api.h index e725e60..d77aa8d 100644 --- a/projects/sel4test/apps/front/include/frontend_api.h +++ b/projects/sel4test/apps/front/include/frontend_api.h @@ -87,4 +87,8 @@ int frontend_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t siz int frontend_sess_bind_callback(struct FrontendSession *sess, SESS_EVENT_CALLBACK event_callback); +int frontend_iot_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t size); +int frontend_iot_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t size); + + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/frontend_proto.h b/projects/sel4test/apps/front/include/frontend_proto.h index bbaba3a..04cdb62 100644 --- a/projects/sel4test/apps/front/include/frontend_proto.h +++ b/projects/sel4test/apps/front/include/frontend_proto.h @@ -77,4 +77,43 @@ int frontend_proxy_shmem_data_msg_send(struct FrontendSession *sess, const uint8 int frontend_proxy_sock_data_msg_recv(struct FrontendSession *sess, uint8_t *msg); int frontend_proxy_sock_data_msg_send(struct FrontendSession *sess, uint8_t *msg); + + +int frontend_proxy_iot_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t msg_len, uint8_t *msg); + + +int frontend_proxy_bluetooth_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data); + + +int frontend_proxy_can_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data); + + +int frontend_proxy_zigbee_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data); + + +int frontend_proxy_lora_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data); + + +int frontend_proxy_powerlink_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data); + +int frontend_proxy_modbustcp_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data); + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index 21f7fe1..9761e3c 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -17,7 +17,8 @@ typedef enum { PROXY_MSG_TYPE_DEV = 0, // Device message PROXY_MSG_TYPE_STRGY, // Strategy message PROXY_MSG_TYPE_SESS, // Session message - PROXY_MSG_TYPE_DATA // Data message + PROXY_MSG_TYPE_DATA, // Data message + PROXY_MSG_TYPE_IOT // IoT message } ProxyMsgType; @@ -849,8 +850,8 @@ int build_proxy_data_message(ProxyMsgHeader *header, const uint8_t *payload, siz * @param payload_len Length of IoT payload (bytes; 0 if no payload) * @param result_msg Output pointer to constructed IoT proxy message (allocated internally) * @return int Construction result - * - BACKEND_PROXY_PROCESS_OK: IoT message built successfully - * - BACKEND_PROXY_PROCESS_ERROR: Failed (invalid parameters/address/protocol mismatch/memory error) + * - FRONTEND_PROXY_PROCESS_OK: IoT message built successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed (invalid parameters/address/protocol mismatch/memory error) * * @note Validates protocol consistency: header->inner_header.iot_hdr.proto_type must match header->iot_addr.addr_type * @note header->iot_addr_len must be >0 and ≤ sizeof(IotAddr) (e.g., 8 for bt_addr, 7 for can_addr) diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index 6cfe243..08a8b73 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -515,4 +515,157 @@ typedef enum { +/** + * @brief IoT session link state enumeration + */ +typedef enum { + IOT_SESS_STATE_UNINIT = 0, // Session uninitialized + IOT_SESS_STATE_LINKED, // Session linked to IoT device (active) + IOT_SESS_STATE_UNLINKED, // Session unlinked from device (inactive) + IOT_SESS_STATE_ERROR // Session in error state (need reconnection) +} IotSessLinkState; + + +/** + * @brief Socket node for managing multiple file descriptors within a single session. + * + * Some IoT sessions (e.g., Bluetooth Gateway, Modbus TCP Server) may involve + * multiple sockets: one listening socket, one or more connected client sockets, + * or separate control/data channels. This struct allows tracking them in a list. + */ +typedef struct IotSockNode_ { + int fd; // The file descriptor of the socket + TAILQ_ENTRY(IotSockNode_) entries; // Linkage member for the TAILQ +} IotSockNode; + +// Define the TAILQ head type for the list of IotSockNode +TAILQ_HEAD(IotSockList_, IotSockNode_); +typedef struct IotSockList_ IotSockList; + + +/** + * @brief Working mode of an IoT session/device. + * + * Defines the operational role of the entity in the IoT architecture. + * This dictates the connection behavior (initiator vs listener) and data flow direction. + * + * - CLIENT_MODE: The device initiates connections and reports data (e.g., Sensors, Actuators). + * - SERVER_MODE: The device listens for connections and manages clients (e.g., Gateways, Hubs). + */ +typedef enum IotWorkMode_ { + /** + * @brief Client Mode (Initiator / Edge Device). + * + * Characteristics: + * - Actively initiates connections to a server/gateway. + * - Primary data flow: Uplink (Device -> Server). + * - Responsible for auto-reconnection on link failure. + * - Typical devices: Temperature sensors, Smart plugs, Wearables. + * - Network behavior: Uses connect(), no listen_fd. + */ + IOT_WORK_MODE_CLIENT = 0, + + /** + * @brief Server Mode (Listener / Gateway / Hub). + * + * Characteristics: + * - Passively waits for incoming connections from clients. + * - Primary data flow: Downlink management & Uplink aggregation. + * - Responsible for accepting new clients and managing multiple sessions. + * - Typical devices: IoT Gateways, Edge Servers, Modbus TCP Masters (acting as servers). + * - Network behavior: Uses bind() + listen() + accept(), owns listen_fd. + */ + IOT_WORK_MODE_SERVER = 1 +} IotWorkMode; + + +/** + * @brief IoT message queue structure (optimized for IoT small data packets) + * @note Simplified version of SessMsgQueue for IoT characteristics + */ +typedef struct IotSessMsgQueue_ { + void *msg_buf; // Message buffer (fixed size for IoT) + uint32_t buf_size; // Buffer size (e.g., 4096 bytes) + uint32_t msg_count; // Number of pending messages + int queue_fd; // Event fd for queue notification +} IotSessMsgQueue; + + +typedef struct IotMsgBuffer_; +typedef struct IotMsgBuffer_ IotMsgBuffer; +struct IoTFrontendSession_; + +typedef void (*IOTSESS_EVENT_CALLBACK)(struct IoTFrontendSession_ *sess, IoTSessionEvent event, uint8_t iot_data, uint16_t data_len); + + +/** + * @brief IoT backend session object (for Bluetooth/CAN/Zigbee/LoRa/OpenPowerLink) + * @note Each IoT device has exactly one corresponding session (1:1 mapping) + * @note No session pool required - directly bound to IoT device instance + */ +typedef struct IoTFrontendSession_ { + // 1. Session core identification (1:1 binding with IoT device) + int sess_type; // IoT session type (matches protocol type) + int sess_role; + int dev_id; // Associated IoT device ID (global unique) + int sess_id; // IoT session ID (same as device's sess_id) + IotWorkMode working_mode; // Session working mode (client/gateway) + + // 2. Link state management (simplified for IoT 1:1 mapping) + IotSessLinkState sess_dev_link_state; // Session-device link state + uint64_t last_link_ts; // Last link state change timestamp (ms) + uint32_t reconnect_count; // Reconnection attempt count (for error recovery) + + // 3. Device association (direct pointer to IoT device object) + struct IotDevice_ *bound_dev; // Pointer to bound IoT device (core association) + struct BackendEngine_ *eng; // Pointer to parent backend engine + + // 4. Message queues (optimized for IoT small packets) + IotSessMsgQueue msg_dev2eng; // Device to engine message queue (data report/notify) + IotSessMsgQueue msg_eng2dev; // Engine to device message queue (control command) + + // 5. Protocol processing (IoT protocol-specific handler) + void *proto_handler; // Protocol-specific processing module (e.g., BLE handler) + void *pri_data; // Private data (protocol-specific context) + + // 6. Hardware/IO related (IoT device access) + int dev_fd; // Bound device file descriptor (same as device's fd) + int listen_fd; // Listening socket for connection-oriented protocols (e.g., Bluetooth L2CAP, Modbus TCP). + IotSockList sock_list; // Head of the linked list for socket nodes + uint32_t io_timeout; // IO operation timeout (ms, e.g., 5000) + + // 7. Statistics (session-level performance metrics) + uint64_t tx_packets; // Total packets sent via session + uint64_t rx_packets; // Total packets received via session + uint64_t tx_bytes; // Total bytes sent + uint64_t rx_bytes; // Total bytes received + uint32_t error_count; // Total session errors + /** + * @brief Send data to remote IoT device/network (protocol-agnostic) + * @param sess Pointer to IoTBackendSession instance + * @param msg_buf Pointer to IotMsgBuffer (contains data + destination address) + * @return int BACKEND_PROXY_PROCESS_OK on success, BACKEND_PROXY_PROCESS_ERROR on failure + */ + int (*send_to_remote)(struct IoTFrontendSession_ *sess, const IotMsgBuffer *msg_buf); + + /** + * @brief Receive data from remote IoT device/network (protocol-agnostic) + * @param sess Pointer to IoTBackendSession instance + * @param msg_buf Pointer to IotMsgBuffer (stores data + source address) + * @param timeout_ms Timeout in milliseconds (0 = non-blocking, -1 = blocking) + * @return int BACKEND_PROXY_PROCESS_OK on success, BACKEND_PROXY_PROCESS_ERROR on failure + */ + int (*recv_from_remote)(struct IoTFrontendSession_ *sess, IotMsgBuffer *msg_buf, int timeout_ms); + IOTSESS_EVENT_CALLBACK event_callback; + // 8. List linkage (for engine-level session management) + TAILQ_ENTRY(IoTFrontendSession_) entries; // Linked list node for engine session list +} IoTFrontendSession; + +extern IoTFrontendSession *frontend_bluetooth_sess; +extern IoTFrontendSession *frontend_can_sess; +extern IoTFrontendSession *frontend_zigbee_sess; +extern IoTFrontendSession *frontend_lora_sess; +extern IoTFrontendSession *frontend_powerlink_sess; +extern IoTFrontendSession *frontend_modbustcp_sess; + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 2c4b695..726a26b 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -982,7 +982,7 @@ eng_run_step2: eng_run_step3: /* - * Recall the BACKEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (2) procedure may renew the front-to-back queue (queue_f2b) of the session pool. + * Recall the FRONTEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (2) procedure may renew the front-to-back queue (queue_f2b) of the session pool. */ printf("In %s-9\n", __func__); FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); printf("In %s-10\n", __func__); diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index fda3eb5..ab08af9 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -225,6 +225,118 @@ int build_proxy_data_message(ProxyMsgHeader *proxy_msg_hdr, const uint8_t *paylo } +/** + * @brief Build IoT proxy message (supports 3-layer structure: ProxyMsgHeader + IotMsgHeader + IotAddr + payload) + * + * Core construction function for IoT proxy messages (called by build_proxy_general_message): + * 1. Validates IotMsgHeader (proto_type/opcode/payload_len) and IotAddr (addr_type matches proto_type) + * 2. Calculates total length: sizeof(ProxyMsgHeader) + sizeof(IotMsgHeader) + header->iot_addr_len + payload_len + * 3. Allocates memory for the full message (heap/pool based on caller context) + * 4. Writes data in order: + * - ProxyMsgHeader (from header->outer_header) + * - IotMsgHeader (from header->inner_header.iot_hdr) + * - IotAddr (protocol-specific address from header->iot_addr, truncated to header->iot_addr_len) + * - Payload (raw data from payload parameter) + * 5. Updates ProxyMsgHeader.total_len with the full message length + * + * @param header Pointer to GeneralProxyMsgHeader (must contain valid iot_hdr + iot_addr + iot_addr_len) + * @param payload Pointer to IoT payload (raw data after address; NULL if no payload) + * @param payload_len Length of IoT payload (bytes; 0 if no payload) + * @param result_msg Output pointer to constructed IoT proxy message (allocated internally) + * @return int Construction result + * - FRONTEND_PROXY_PROCESS_OK: IoT message built successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed (invalid parameters/address/protocol mismatch/memory error) + * + * @note Validates protocol consistency: header->inner_header.iot_hdr.proto_type must match header->iot_addr.addr_type + * @note header->iot_addr_len must be >0 and ≤ sizeof(IotAddr) (e.g., 8 for bt_addr, 7 for can_addr) + * @note IotMsgHeader.payload_len is automatically set to (header->iot_addr_len + payload_len) + * @note Handles frontend-to-backend IoT messages transmitted via HyperAMP (same as other proxy messages) + */ +int build_proxy_iot_message(GeneralProxyMsgHeader *header, + const uint8_t *payload, + size_t payload_len, + uint8_t **result_msg){ + /* + * 1. Basic Parameter Validation (Safety Check) + */ + if (!header || !payload || !result_msg || !(*result_msg)) { + error_print("build_proxy_iot_message failed: NULL pointer argument!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Extract the IoT protocol type from the inner header to determine address size + uint16_t proto_type = header->inner_header.iot_hdr.proto_type; + + // Optional Consistency Check: Ensure the header type matches the address type stored in IotAddr + if (proto_type != header->iot_addr.addr_type) { + error_print("build_proxy_iot_message warning: Header proto_type mismatch with IotAddr type!\n"); + // Proceeding anyway as the switch statement below will handle the actual size based on proto_type + } + + uint8_t *iot_data_msg = *result_msg; + size_t addr_info_len = 0; + + /* + * 2. Determine the exact valid length of the specific address structure + * Purpose: Prevent copying uninitialized garbage data from the union. + * Since we are NOT sending the 'addr_type' byte, we only copy the 'addr_info' union part. + */ + switch (proto_type) { + case IOT_PROTO_TYPE_BLUETOOTH: + addr_info_len = sizeof(IotBtAddr); + break; + case IOT_PROTO_TYPE_CAN: + addr_info_len = sizeof(IotCanAddr); + break; + case IOT_PROTO_TYPE_ZIGBEE: + addr_info_len = sizeof(IotZigbeeAddr); + break; + case IOT_PROTO_TYPE_LORA: + addr_info_len = sizeof(IotLoraAddr); + break; + case IOT_PROTO_TYPE_POWERLINK: + addr_info_len = sizeof(IotPowerLinkAddr); + break; + case IOT_PROTO_TYPE_MODBUSTCP: + addr_info_len = sizeof(IotModbusTcpAddr); + break; + default: + error_print("build_proxy_iot_message failed: unsupported IoT protocol type!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + // Sanity check: Ensure calculated length does not exceed the union's maximum size + if (addr_info_len > sizeof(header->iot_addr.addr_info)) { + error_print("build_proxy_iot_message failed: calculated address length exceeds union size!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + /* + * Refine length using 'iot_addr_len' if provided and smaller. + * This handles cases where the valid address data is shorter than the struct size (e.g., padding). + * Assumption: header->iot_addr_len refers only to the valid bytes within 'addr_info'. + */ + if (header->iot_addr_len > 0 && header->iot_addr_len < addr_info_len) { + addr_info_len = header->iot_addr_len; + } + + /* + * 3. Serialization Step A: Write Address Info Only + * We copy ONLY the 'addr_info' union part. The 'addr_type' is excluded from the transmission buffer. + */ + memcpy(iot_data_msg, &header->iot_addr.addr_info, addr_info_len); + iot_data_msg += addr_info_len; + + /* + * 4. Serialization Step B: Write Payload + */ + if (payload_len > 0) { + memcpy(iot_data_msg, payload, payload_len); + } + + + return FRONTEND_PROXY_PROCESS_OK; +} /** @@ -279,7 +391,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h DevMsgHeader *dev_hdr; StrgyMsgHeader *strgy_hdr; SessMsgHeader *sess_hdr; - int ret, alloc_size; + int ret, alloc_size, sub_iot_hdr_len; /* * Check the validity of the input parameters. @@ -383,6 +495,30 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h ret = build_proxy_data_message(proxy_msg_hdr, payload, payload_len, &msg_buf); utils_print("In %s, after build_proxy_data_message, the return value is %d\n", __func__, ret); break; + case PROXY_MSG_TYPE_IOT: + proxy_msg_payload_len = 0; + if(IOT_PROTO_TYPE_BLUETOOTH == header->inner_header.iot_hdr.proto_type){ + sub_iot_hdr_len = sizeof(IotBtAddr); + }else if(IOT_PROTO_TYPE_CAN == header->inner_header.iot_hdr.proto_type){ + sub_iot_hdr_len = sizeof(IotCanAddr); + }else if(IOT_PROTO_TYPE_ZIGBEE == header->inner_header.iot_hdr.proto_type){ + sub_iot_hdr_len = sizeof(IotZigbeeAddr); + }else if(IOT_PROTO_TYPE_LORA == header->inner_header.iot_hdr.proto_type){ + sub_iot_hdr_len = sizeof(IotLoraAddr); + }else if(IOT_PROTO_TYPE_POWERLINK == header->inner_header.iot_hdr.proto_type){ + sub_iot_hdr_len = sizeof(IotPowerLinkAddr); + }else if(IOT_PROTO_TYPE_MODBUSTCP == header->inner_header.iot_hdr.proto_type){ + sub_iot_hdr_len = sizeof(IotModbusTcpAddr); + }else{ + error_print("build_proxy_general_message failed: unsupported IoT protocol!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + proxy_msg_payload_len = sizeof(IotMsgHeader) + sub_iot_hdr_len; + + ret = build_proxy_iot_message(proxy_msg_hdr, payload, payload_len, &msg_buf); + utils_print("In %s, after build_proxy_iot_message, the return value is %d\n", __func__, ret); + break; default: /* * Message type is not supported!. @@ -543,7 +679,7 @@ int frontend_proxy_msg_process(uint8_t *msg){ }else if(PROXY_MSG_TYPE_SESS == msg_type){ printf("In %s-3\n", __func__); ret = frontend_proxy_sess_msg_process(frontend_sess_id, backend_sess_id, msg_ptr); - }else{ + }else if(PROXY_MSG_TYPE_DATA == msg_type){ /* * When msg_type is PROXY_MSG_TYPE_DATA, the frontend_sess_id and backend_sess_id should be checked to determine whether the session (if it exists) * is an application session. @@ -553,6 +689,14 @@ int frontend_proxy_msg_process(uint8_t *msg){ return FRONTEND_PROXY_PROCESS_ERROR; } ret = frontend_proxy_data_msg_prosess(frontend_sess_id, backend_sess_id, msg_len, msg_ptr); + }else if(PROXY_MSG_TYPE_IOT == msg_type){ +/* + * process IoT message. + */ + ret = frontend_proxy_iot_msg_process(frontend_sess_id, backend_sess_id, msg_len, msg_ptr); + }else{ + error_print("frontend_proxy_msg_process failed: unsupport message type!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; } return FRONTEND_PROXY_PROCESS_OK; @@ -1273,4 +1417,276 @@ int frontend_proxy_data_msg_prosess(uint16_t frontend_sess_id, uint16_t backend_ int frontend_proxy_data_msg_recv(struct FrontendSession *sess, uint8_t *msg); -int frontend_proxy_data_msg_send(struct FrontendSession *sess, uint8_t *msg); \ No newline at end of file +int frontend_proxy_data_msg_send(struct FrontendSession *sess, uint8_t *msg); + + +/** + * @brief Unified entry function for IoT proxy message processing + * + * @details This function serves as the top-level unified entry for all IoT proxy message handling. + * It processes messages transmitted from the backend to the frontend through the HyperAMP channel. + * It parses message headers, validates parameters and message integrity, + * and distributes messages to corresponding protocol-specific processing handlers. + * + * @param frontend_sess_id Frontend session ID for message interaction + * @param backend_sess_id Backend session ID for IoT communication matching + * @param msg_len Total length of the received IoT proxy message + * @param msg Pointer to the complete IoT proxy message data + * + * @return int Processing result + * - FRONTEND_PROXY_PROCESS_OK: Message processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Processing failed (invalid parameter/header/type/length) + * + * @note This is the core unified entry for all IoT-type proxy messages + * @note Messages are transmitted from backend to frontend via HyperAMP channel + * @note Consists of ProxyMsgHeader, IotMsgHeader and protocol-specific payload data + * @note Validates input parameters, message length and header integrity + * @note Extracts and uses protocol type to dispatch to corresponding IoT processing logic + * @note Adopts consistent error code standards with other proxy message modules + * + * @warning Ensure the input message pointer and length are valid before invocation + * @warning Session IDs must match the established frontend-backend communication link + */ +int frontend_proxy_iot_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, uint16_t msg_len, uint8_t *msg){ + IotMsgHeader *iot_msg_hdr; + uint8_t *iot_data; + int ret; + + iot_msg_hdr = (IotMsgHeader *)msg; + + if(NULL == iot_msg_hdr){ + error_print("frontend_proxy_iot_msg_process failed: msg pointer should not be NULL!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + iot_data = msg + sizeof(IotMsgHeader); + + switch(iot_msg_hdr->proto_type){ + case IOT_DEV_TYPE_BLUETOOTH: + if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotBtAddr)){ + error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for Bluetooth protocol!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + ret = frontend_proxy_bluetooth_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); + break; + case IOT_DEV_TYPE_CAN: + if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotCanAddr)){ + error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for CAN protocol!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + ret = frontend_proxy_can_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); + break; + case IOT_DEV_TYPE_ZIGBEE: + if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotZigbeeAddr)){ + error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for ZigBee protocol!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + ret = frontend_proxy_zigbee_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); + break; + case IOT_DEV_TYPE_LORA: + if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotLoraAddr)){ + error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for LoRa protocol!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + ret = frontend_proxy_lora_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); + break; + case IOT_DEV_TYPE_POWERLINK: + if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotPowerLinkAddr)){ + error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for PowerLink protocol!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + ret = frontend_proxy_powerlink_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); + break; + case IOT_DEV_TYPE_MODBUSTCP: + if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotModbusTcpAddr)){ + error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for ModbusTCP protocol!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + ret = frontend_proxy_modbustcp_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); + break; + default: + error_print("frontend_proxy_iot_msg_process failed: unsupported protocol type!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + return ret; +} + + +/** + * @brief Bluetooth-specific IoT proxy message processing + * + * Processes Bluetooth IoT proxy messages (send/receive/status) transmitted from backend to frontend via HyperAMP: + * - Parses Bluetooth address (IotAddr.bt_addr) from IoT protocol data + * - Validates Bluetooth-specific parameters (MAC/port/connection interval) + * - Dispatches to bluetooth_send_to_remote/bluetooth_recv_from_remote based on opcode + * - Constructs response message (if opcode = IOT_OPCODE_RESPONSE) + * + * @param frontend_sess_id Frontend session ID (from ProxyMsgHeader) + * @param backend_sess_id Backend IoT session ID (from ProxyMsgHeader) + * @param iot_header Pointer to parsed IotMsgHeader + * @param iot_data Pointer to Bluetooth protocol data (IotAddr + payload) + * @return int Processing result + * - FRONTEND_PROXY_PROCESS_OK: Bluetooth message processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed (invalid Bluetooth addr/params/session) + * + * @note Internal sub-function - called only by frontend_proxy_iot_msg_process() + * @note Handles backend-to-frontend Bluetooth messages sent via HyperAMP + * @note Binds to IoTFrontendSession via frontend_sess_id before processing + * @note Automatically updates Bluetooth session statistics (tx/rx packets/bytes) + */ +int frontend_proxy_bluetooth_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data){ + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief CAN-specific IoT proxy message processing + * + * Processes CAN IoT proxy messages (send/receive/status) transmitted from backend to frontend via HyperAMP: + * - Parses CAN address (IotAddr.can_addr) from IoT protocol data + * - Validates CAN-specific parameters (can_id/frame_type/baudrate) + * - Dispatches to can_send_to_remote/can_recv_from_remote based on opcode + * - Constructs response message (if opcode = IOT_OPCODE_RESPONSE) + * + * @param frontend_sess_id Frontend session ID (from ProxyMsgHeader) + * @param backend_sess_id Backend IoT session ID (from ProxyMsgHeader) + * @param iot_header Pointer to parsed IotMsgHeader + * @param iot_data Pointer to CAN protocol data (IotAddr + payload) + * @return int Processing result + * - FRONTEND_PROXY_PROCESS_OK: CAN message processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed (invalid CAN addr/params/session) + * + * @note Internal sub-function - called only by frontend_proxy_iot_msg_process() + * @note Handles backend-to-frontend CAN messages sent via HyperAMP + * @note Binds to IoTFrontendSession via frontend_sess_id before processing + * @note Automatically updates CAN session statistics (tx/rx packets/bytes) + */ +int frontend_proxy_can_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data){ + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief ZigBee-specific IoT proxy message processing + * + * Processes ZigBee IoT proxy messages (send/receive/status) transmitted from backend to frontend via HyperAMP: + * - Parses ZigBee address (IotAddr.zigbee_addr) from IoT protocol data + * - Validates ZigBee-specific parameters (short_addr/mac/channel/pan_id) + * - Dispatches to zigbee_send_to_remote/zigbee_recv_from_remote based on opcode + * - Constructs response message (if opcode = IOT_OPCODE_RESPONSE) + * + * @param frontend_sess_id Frontend session ID (from ProxyMsgHeader) + * @param backend_sess_id Backend IoT session ID (from ProxyMsgHeader) + * @param iot_header Pointer to parsed IotMsgHeader + * @param iot_data Pointer to ZigBee protocol data (IotAddr + payload) + * @return int Processing result + * - FRONTEND_PROXY_PROCESS_OK: ZigBee message processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed (invalid ZigBee addr/params/session) + * + * @note Internal sub-function - called only by frontend_proxy_iot_msg_process() + * @note Handles backend-to-frontend ZigBee messages sent via HyperAMP + * @note Binds to IoTFrontendSession via frontend_sess_id before processing + * @note Automatically updates ZigBee session statistics (tx/rx packets/bytes) + */ +int frontend_proxy_zigbee_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data){ + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief LoRa-specific IoT proxy message processing + * + * Processes LoRa IoT proxy messages (send/receive/status) transmitted from backend to frontend via HyperAMP: + * - Parses LoRa address (IotAddr.lora_addr) from IoT protocol data + * - Validates LoRa-specific parameters (dev_addr/freq/spreading_factor) + * - Dispatches to lora_send_to_remote/lora_recv_from_remote based on opcode + * - Constructs response message (if opcode = IOT_OPCODE_RESPONSE) + * + * @param frontend_sess_id Frontend session ID (from ProxyMsgHeader) + * @param backend_sess_id Backend IoT session ID (from ProxyMsgHeader) + * @param iot_header Pointer to parsed IotMsgHeader + * @param iot_data Pointer to LoRa protocol data (IotAddr + payload) + * @return int Processing result + * - FRONTEND_PROXY_PROCESS_OK: LoRa message processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed (invalid LoRa addr/params/session) + * + * @note Internal sub-function - called only by frontend_proxy_iot_msg_process() + * @note Handles backend-to-frontend LoRa messages sent via HyperAMP + * @note Binds to IoTFrontendSession via frontend_sess_id before processing + * @note Automatically updates LoRa session statistics (tx/rx packets/bytes) + */ +int frontend_proxy_lora_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data){ + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief PowerLink-specific IoT proxy message processing + * + * Processes PowerLink IoT proxy messages (send/receive/status) transmitted from backend to frontend via HyperAMP: + * - Parses PowerLink address (IotAddr.powerlink_addr) from IoT protocol data + * - Validates PowerLink-specific parameters (node_id/pdo_id/cycle_ms) + * - Dispatches to powerlink_send_to_remote/powerlink_recv_from_remote based on opcode + * - Constructs response message (if opcode = IOT_OPCODE_RESPONSE) + * + * @param frontend_sess_id Frontend session ID (from ProxyMsgHeader) + * @param backend_sess_id Backend IoT session ID (from ProxyMsgHeader) + * @param iot_header Pointer to parsed IotMsgHeader + * @param iot_data Pointer to PowerLink protocol data (IotAddr + payload) + * @return int Processing result + * - FRONTEND_PROXY_PROCESS_OK: PowerLink message processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed (invalid PowerLink addr/params/session) + * + * @note Internal sub-function - called only by frontend_proxy_iot_msg_process() + * @note Handles backend-to-frontend PowerLink messages sent via HyperAMP + * @note Binds to IoTFrontendSession via frontend_sess_id before processing + * @note Automatically updates PowerLink session statistics (tx/rx packets/bytes) + */ +int frontend_proxy_powerlink_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data){ + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief ModbusTCP-specific IoT proxy message processing + * + * Processes ModbusTCP IoT proxy messages (send/receive/status) transmitted from backend to frontend via HyperAMP: + * - Parses ModbusTCP address (IotAddr.modbustcp_addr) from IoT protocol data + * - Validates ModbusTCP-specific parameters (unit_id/ip/port/function_code) + * - Dispatches to modbustcp_send_to_remote/modbustcp_recv_from_remote based on opcode + * - Constructs response message (if opcode = IOT_OPCODE_RESPONSE) + * + * @param frontend_sess_id Frontend session ID (from ProxyMsgHeader) + * @param backend_sess_id Backend IoT session ID (from ProxyMsgHeader) + * @param iot_header Pointer to parsed IotMsgHeader + * @param iot_data Pointer to ModbusTCP protocol data (IotAddr + payload) + * @return int Processing result + * - FRONTEND_PROXY_PROCESS_OK: ModbusTCP message processed successfully + * - FRONTEND_PROXY_PROCESS_ERROR: Failed (invalid ModbusTCP addr/params/session) + * + * @note Internal sub-function - called only by frontend_proxy_iot_msg_process() + * @note Handles backend-to-frontend ModbusTCP messages sent via HyperAMP + * @note Binds to IoTFrontendSession via frontend_sess_id before processing + * @note Automatically updates ModbusTCP session statistics (tx/rx packets/bytes) + */ +int frontend_proxy_modbustcp_msg_process(uint16_t frontend_sess_id, + uint16_t backend_sess_id, + IotMsgHeader *iot_header, + uint8_t *iot_data){ + return FRONTEND_PROXY_PROCESS_OK; +} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index fee001f..5982954 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -2,6 +2,13 @@ #include "session.h" #include "common_utils.h" +IoTFrontendSession *frontend_bluetooth_sess; +IoTFrontendSession *frontend_can_sess; +IoTFrontendSession *frontend_zigbee_sess; +IoTFrontendSession *frontend_lora_sess; +IoTFrontendSession *frontend_powerlink_sess; +IoTFrontendSession *frontend_modbustcp_sess; + /** * @brief Allocates and initializes a SessMsgSeg structure * -- Gitee From bfd70b4a24a93ca3315ef34b8d3ed1c6873c94a3 Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 19 Mar 2026 19:39:11 +0800 Subject: [PATCH 099/133] IoT Device and session initialize --- projects/sel4test/apps/front/include/engine.h | 15 + .../sel4test/apps/front/include/session.h | 49 +- projects/sel4test/apps/front/src/engine.c | 491 ++++++++++++++++++ projects/sel4test/apps/front/src/session.c | 144 +++++ 4 files changed, 687 insertions(+), 12 deletions(-) diff --git a/projects/sel4test/apps/front/include/engine.h b/projects/sel4test/apps/front/include/engine.h index 9866b52..3ded90c 100644 --- a/projects/sel4test/apps/front/include/engine.h +++ b/projects/sel4test/apps/front/include/engine.h @@ -79,6 +79,7 @@ typedef struct FrontendEngine_{ uint16_t dev_num; FrontendHighSpeedNetDeviceSet *dev_set; // High speed network device set FrontendIoTDeviceSet *iot_dev_set; // IoT device set + uint16_t iot_dev_num; // IoT device number uint16_t sel_id; uint16_t active_mask; // Show the positions of all the active high-speed network devices as a mask. uint16_t eng_cmd_id; // Used when constructing device (dev) messages and strategy (strgy) messages, filled into the msg_id member of these messages. @@ -374,5 +375,19 @@ struct FrontendEngOps *get_hs_frontend_engine_ops(); void frontend_engine_init(); +int engine_init_iot_devices(FrontendEngine *eng); +int engine_init_iot_sessions(FrontendEngine *engine); + +int engine_init_bluetooth_session(IotDevice *dev, IoTFrontendSession *sess); + +int engine_init_can_session(IotDevice *dev, IoTFrontendSession *sess); + +int engine_init_zigbee_session(IotDevice *dev, IoTFrontendSession *sess); + +int engine_init_lora_session(IotDevice *dev, IoTFrontendSession *sess); + +int engine_init_powerlink_session(IotDevice *dev, IoTFrontendSession *sess); + +int engine_init_modbustcp_session(IotDevice *dev, IoTFrontendSession *sess); #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index 08a8b73..4202e7c 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -595,11 +595,11 @@ typedef struct IotMsgBuffer_; typedef struct IotMsgBuffer_ IotMsgBuffer; struct IoTFrontendSession_; -typedef void (*IOTSESS_EVENT_CALLBACK)(struct IoTFrontendSession_ *sess, IoTSessionEvent event, uint8_t iot_data, uint16_t data_len); +typedef void (*IOTSESS_EVENT_CALLBACK)(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf); /** - * @brief IoT backend session object (for Bluetooth/CAN/Zigbee/LoRa/OpenPowerLink) + * @brief IoT frontend session object (for Bluetooth/CAN/Zigbee/LoRa/OpenPowerLink/ModbusTCP) * @note Each IoT device has exactly one corresponding session (1:1 mapping) * @note No session pool required - directly bound to IoT device instance */ @@ -617,8 +617,8 @@ typedef struct IoTFrontendSession_ { uint32_t reconnect_count; // Reconnection attempt count (for error recovery) // 3. Device association (direct pointer to IoT device object) - struct IotDevice_ *bound_dev; // Pointer to bound IoT device (core association) - struct BackendEngine_ *eng; // Pointer to parent backend engine + struct IotDevice_ *bound_dev; // Pointer to bound IoT device (core association) + struct FrontendEngine_ *eng; // Pointer to parent backend engine // 4. Message queues (optimized for IoT small packets) IotSessMsgQueue msg_dev2eng; // Device to engine message queue (data report/notify) @@ -641,21 +641,26 @@ typedef struct IoTFrontendSession_ { uint64_t rx_bytes; // Total bytes received uint32_t error_count; // Total session errors /** - * @brief Send data to remote IoT device/network (protocol-agnostic) + * @brief Send data to backend (protocol-agnostic) * @param sess Pointer to IoTBackendSession instance * @param msg_buf Pointer to IotMsgBuffer (contains data + destination address) * @return int BACKEND_PROXY_PROCESS_OK on success, BACKEND_PROXY_PROCESS_ERROR on failure */ - int (*send_to_remote)(struct IoTFrontendSession_ *sess, const IotMsgBuffer *msg_buf); + int (*send_to_backend)(struct IoTFrontendSession_ *sess, const IotMsgBuffer *msg_buf); /** - * @brief Receive data from remote IoT device/network (protocol-agnostic) - * @param sess Pointer to IoTBackendSession instance - * @param msg_buf Pointer to IotMsgBuffer (stores data + source address) - * @param timeout_ms Timeout in milliseconds (0 = non-blocking, -1 = blocking) - * @return int BACKEND_PROXY_PROCESS_OK on success, BACKEND_PROXY_PROCESS_ERROR on failure + * @brief Receive data from backend (protocol-agnostic) + * @param sess Pointer to IoTFrontendSession instance + * @param msg_buf Pointer to IotMsgBuffer (assembled and returned to caller) + * @param data Raw data buffer received from backend + * @param data_len Length of raw data received from backend + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ - int (*recv_from_remote)(struct IoTFrontendSession_ *sess, IotMsgBuffer *msg_buf, int timeout_ms); + int (*recv_from_backend)(struct IoTFrontendSession_ *sess, + IotMsgBuffer *msg_buf, + uint8_t *data, + uint16_t data_len); + IOTSESS_EVENT_CALLBACK event_callback; // 8. List linkage (for engine-level session management) TAILQ_ENTRY(IoTFrontendSession_) entries; // Linked list node for engine session list @@ -668,4 +673,24 @@ extern IoTFrontendSession *frontend_lora_sess; extern IoTFrontendSession *frontend_powerlink_sess; extern IoTFrontendSession *frontend_modbustcp_sess; + + +int bluetooth_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf); +int bluetooth_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len); + +int can_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf); +int can_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len); + +int zigbee_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf); +int zigbee_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len); + +int lora_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf); +int lora_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len); + +int powerlink_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf); +int powerlink_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len); + +int modbustcp_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf); +int modbustcp_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len); + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 726a26b..bc5b829 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -1027,4 +1027,495 @@ eng_run_step3: } + +/** + * @brief Initialize IoT devices. + * @param engine Pointer to FrontendEngine instance. + * @return int Execution result + * - FRONTEND_PROXY_PROCESS_OK (0): All devices initialized successfully OR no devices to parse + * - FRONTEND_PROXY_PROCESS_ERROR (-1): Critical error (invalid input/INI load failed/no devices parsed) + * + * @warning Ensure MAX_IOT_DEV_NUM, MAX_DEV_NAME are defined before using this function + * @warning Call frontend_engine_cleanup_iot_devices() to free allocated memory + */ +int engine_init_iot_devices(FrontendEngine *engine){ + // 1. Validate input parameters + IotDevice *dev; + if (engine == NULL) { + fprintf(stderr, "[ERROR] Invalid BackendEngine pointer (NULL)\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + +// 2. Initialize IoT device set (allocate memory if not exists) + if (engine->iot_dev_set == NULL) { + engine->iot_dev_set = (FrontendIoTDeviceSet *)calloc(1, sizeof(FrontendIoTDeviceSet)); + if (engine->iot_dev_set == NULL) { + fprintf(stderr, "[ERROR] Failed to allocate memory for IoTDeviceSet\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + memset(engine->iot_dev_set, 0, sizeof(FrontendIoTDeviceSet)); + } + + dev = &engine->iot_dev_set->iot_dev[0]; + dev->dev_type = IOT_DEV_TYPE_BLUETOOTH; + dev->dev_status = IOT_DEV_STATUS_ONLINE; + + dev = &engine->iot_dev_set->iot_dev[1]; + dev->dev_type = IOT_DEV_TYPE_CAN; + dev->dev_status = IOT_DEV_STATUS_ONLINE; + + dev = &engine->iot_dev_set->iot_dev[2]; + dev->dev_type = IOT_DEV_TYPE_ZIGBEE; + dev->dev_status = IOT_DEV_STATUS_ONLINE; + + dev = &engine->iot_dev_set->iot_dev[3]; + dev->dev_type = IOT_DEV_TYPE_LORA; + dev->dev_status = IOT_DEV_STATUS_ONLINE; + + dev = &engine->iot_dev_set->iot_dev[4]; + dev->dev_type = IOT_DEV_TYPE_POWERLINK; + dev->dev_status = IOT_DEV_STATUS_ONLINE; + + dev = &engine->iot_dev_set->iot_dev[5]; + dev->dev_type = IOT_DEV_TYPE_MODBUSTCP; + dev->dev_status = IOT_DEV_STATUS_ONLINE; + + engine->iot_dev_num = 6; + + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief Initialize IoT device sessions. + * @param engine Pointer to FrontendEngine instance + * @return int Execution result + * - BACKEND_PROXY_PROCESS_OK (0): All sessions initialized successfully OR no sessions to start + * - BACKEND_PROXY_PROCESS_ERROR (-1): Critical error (invalid input/session creation failed) + * + * @note This function: + * 1. Iterates through all valid IoT devices in engine->iot_dev_set + * 2. Starts the corresponding session instance for each device sequentially + * 3. Initializes protocol-specific communication sessions for connected devices + * 4. Maintains session context for subsequent data interaction and control + * 5. **ONLY ONE SESSION PER PROTOCOL TYPE IS SUPPORTED** (Bluetooth/CAN/ZigBee/LoRa/PowerLink/ModbusTCP) + * + * @warning Call backend_iot_sess_destroy() to release session resources + * @warning Do NOT call this function repeatedly without proper cleanup + * @warning **Only one instance per IoT protocol type is supported in system** + */ +int engine_init_iot_sessions(FrontendEngine *engine){ + IotDevice *iot_dev; + int iot_dev_cnt, iot_dev_num, ret; + + iot_dev_cnt = 0; + iot_dev_num = engine->iot_dev_num; + + /* Initialize global IoT session handles to NULL */ + frontend_bluetooth_sess = NULL; + frontend_can_sess = NULL; + frontend_zigbee_sess = NULL; + frontend_lora_sess = NULL; + frontend_powerlink_sess = NULL; + frontend_modbustcp_sess = NULL; + + /* Check for invalid input pointers */ + if (NULL == engine || NULL == engine->iot_dev_set) { + error_print("engine_init_iot_sessions failed: invalid engine pointer!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + + while(iot_dev_cnt < iot_dev_num){ + iot_dev = &engine->iot_dev_set->iot_dev[iot_dev_cnt]; + if(IOT_DEV_STATUS_ONLINE == iot_dev->dev_status){ + switch (iot_dev->dev_type) { + case IOT_DEV_TYPE_BLUETOOTH: + /* Ensure only ONE Bluetooth device instance is supported */ + if(NULL != frontend_bluetooth_sess){ + error_print("engine_init_iot_sessions failed: only one bluetooth device is supported in frontendengine!\n"); + goto init_iot_sess_error; + } + + frontend_bluetooth_sess = malloc(sizeof(IoTFrontendSession)); + + if(NULL == frontend_bluetooth_sess){ + error_print("engine_init_iot_sessions failed: insufficient memory for allocating frontend_bluetooth_sess instance!\n"); + goto init_iot_sess_error; + } + + ret = engine_init_bluetooth_session(iot_dev, frontend_bluetooth_sess); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("engine_init_iot_sessions failed: failed to initialize frontend_bluetooth_sess instance!\n"); + goto init_iot_sess_error; + } + + frontend_bluetooth_sess->eng = engine; + + break; + case IOT_DEV_TYPE_CAN: + /* Ensure only ONE CAN device instance is supported */ + if(NULL != frontend_can_sess){ + error_print("engine_init_iot_sessions failed: only one CAN device is supported in frontendengine!\n"); + goto init_iot_sess_error; + } + + frontend_can_sess = malloc(sizeof(IoTFrontendSession)); + + if(NULL == frontend_can_sess){ + error_print("engine_init_iot_sessions failed: insufficient memory for allocating frontend_can_sess instance!\n"); + goto init_iot_sess_error; + } + + ret = engine_init_can_session(iot_dev, frontend_can_sess); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("engine_init_iot_sessions failed: failed to initialize frontend_can_sess instance!\n"); + goto init_iot_sess_error; + } + + frontend_can_sess->eng = engine; + + break; + case IOT_DEV_TYPE_ZIGBEE: + /* Ensure only ONE ZigBee device instance is supported */ + if(NULL != frontend_zigbee_sess){ + error_print("engine_init_iot_sessions failed: only one ZigBee device is supported in frontendengine!\n"); + goto init_iot_sess_error; + } + + frontend_zigbee_sess = malloc(sizeof(IoTFrontendSession)); + + if(NULL == frontend_zigbee_sess){ + error_print("engine_init_iot_sessions failed: insufficient memory for allocating frontend_zigbee_sess instance!\n"); + goto init_iot_sess_error; + } + + ret = engine_init_zigbee_session(iot_dev, frontend_zigbee_sess); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("engine_init_iot_sessions failed: failed to initialize frontend_zigbee_sess instance!\n"); + goto init_iot_sess_error; + } + + frontend_zigbee_sess->eng = engine; + + break; + case IOT_DEV_TYPE_LORA: + /* Ensure only ONE LoRa device instance is supported */ + if(NULL != frontend_lora_sess){ + error_print("engine_init_iot_sessions failed: only one LoRa device is supported in frontendengine!\n"); + goto init_iot_sess_error; + } + + frontend_lora_sess = malloc(sizeof(IoTFrontendSession)); + + if(NULL == frontend_lora_sess){ + error_print("engine_init_iot_sessions failed: insufficient memory for allocating frontend_lora_sess instance!\n"); + goto init_iot_sess_error; + } + + ret = engine_init_lora_session(iot_dev, frontend_lora_sess); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("engine_init_iot_sessions failed: failed to initialize frontend_lora_sess instance!\n"); + goto init_iot_sess_error; + } + + frontend_lora_sess->eng = engine; + + break; + case IOT_DEV_TYPE_POWERLINK: + /* Ensure only ONE PowerLink device instance is supported */ + if(NULL != frontend_powerlink_sess){ + error_print("engine_init_iot_sessions failed: only one PowerLink device is supported in frontendengine!\n"); + goto init_iot_sess_error; + } + + frontend_powerlink_sess = malloc(sizeof(IoTFrontendSession)); + + if(NULL == frontend_powerlink_sess){ + error_print("engine_init_iot_sessions failed: insufficient memory for allocating frontend_powerlink_sess instance!\n"); + goto init_iot_sess_error; + } + + ret = engine_init_powerlink_session(iot_dev, frontend_powerlink_sess); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("engine_init_iot_sessions failed: failed to initialize frontend_powerlink_sess instance!\n"); + goto init_iot_sess_error; + } + + frontend_powerlink_sess->eng = engine; + + break; + case IOT_DEV_TYPE_MODBUSTCP: + /* Ensure only ONE PowerLink device instance is supported */ + if(NULL != frontend_modbustcp_sess){ + error_print("engine_init_iot_sessions failed: only one modbusTCP device is supported in frontendengine!\n"); + goto init_iot_sess_error; + } + + frontend_modbustcp_sess = malloc(sizeof(IoTFrontendSession)); + + if(NULL == frontend_modbustcp_sess){ + error_print("engine_init_iot_sessions failed: insufficient memory for allocating frontend_modbustcp_sess instance!\n"); + goto init_iot_sess_error; + } + + ret = engine_init_modbustcp_session(iot_dev, frontend_modbustcp_sess); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("engine_init_iot_sessions failed: failed to initialize frontend_modbustcp_sess instance!\n"); + goto init_iot_sess_error; + } + + frontend_modbustcp_sess->eng = engine; + + break; + default: + error_print("engine_init_iot_sessions failed: unsupported device type!\n"); + goto init_iot_sess_error; + + } // switch (iot_dev_type) + } // if(IOT_DEV_STATUS_ONLINE == iot_dev->dev_status) + iot_dev_cnt++; + } + + + return FRONTEND_PROXY_PROCESS_OK; + +init_iot_sess_error: + /* Free all allocated session resources on error */ + if(NULL != frontend_bluetooth_sess){ + free(frontend_bluetooth_sess); + frontend_bluetooth_sess = NULL; + } + + if(NULL != frontend_can_sess){ + free(frontend_can_sess); + frontend_can_sess = NULL; + } + + if(NULL != frontend_zigbee_sess){ + free(frontend_zigbee_sess); + frontend_zigbee_sess = NULL; + } + + if(NULL != frontend_lora_sess){ + free(frontend_lora_sess); + frontend_lora_sess = NULL; + } + + if(NULL != frontend_powerlink_sess){ + free(frontend_powerlink_sess); + frontend_powerlink_sess = NULL; + } + + if(NULL != frontend_modbustcp_sess){ + free(frontend_modbustcp_sess); + frontend_modbustcp_sess = NULL; + } + + return FRONTEND_PROXY_PROCESS_ERROR; + +} + + +/** + * @brief Initialize Bluetooth communication session for IoT device + * @param dev Pointer to IotDevice instance (pre-initialized device) + * @param sess Pointer to IoTFrontendSession instance (memory pre-allocated) + * @return int Execution result + * - FRONTEND_PROXY_PROCESS_OK (0): Session initialized successfully + * - FRONTEND_PROXY_PROCESS_ERROR (-1): Session initialization failed + * + * @note This function only initializes session context, NOT device hardware. + * Device initialization is completed in the prior stage. + * The memory of IoTFrontendSession is pre-allocated, no need to manage. + * 1. Binds session context to the corresponding Bluetooth device + * 2. Configures session state and communication parameters + * 3. Sets up data transceiving logic for the session + * 4. Prepares session for subsequent data interaction + * + * @warning Device hardware initialization must be completed before calling + * @warning Session memory is managed externally, do not free in this function + */ +int engine_init_bluetooth_session(IotDevice *dev, IoTFrontendSession *sess){ + if(NULL == dev || NULL == sess){ + error_print("engine_init_bluetooth_session failed: invalid input parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess->eng = frontend_get_global_engine(); + sess->bound_dev = dev; + sess->send_to_backend = bluetooth_send_to_backend; + sess->recv_from_backend = bluetooth_recv_from_backend; + + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief Initialize CAN bus communication session for IoT device + * @param dev Pointer to IotDevice instance (pre-initialized device) + * @param sess Pointer to IoTFrontendSession instance (memory pre-allocated) + * @return int Execution result + * - FRONTEND_PROXY_PROCESS_OK (0): Session initialized successfully + * - FRONTEND_PROXY_PROCESS_ERROR (-1): Session initialization failed + * + * @note This function only initializes session context, NOT device hardware. + * Device initialization is completed in the prior stage. + * The memory of IoTFrontendSession is pre-allocated, no need to manage. + * 1. Binds session context to the corresponding CAN device + * 2. Configures session state and bus communication parameters + * 3. Sets up message queue and data processing logic + * 4. Prepares session for subsequent bus communication + * + * @warning Device hardware initialization must be completed before calling + * @warning Session memory is managed externally, do not free in this function + */ +int engine_init_can_session(IotDevice *dev, IoTFrontendSession *sess){ + if(NULL == dev || NULL == sess){ + error_print("engine_init_bluetooth_session failed: invalid input parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess->eng = frontend_get_global_engine(); + sess->bound_dev = dev; + sess->send_to_backend = can_send_to_backend; + sess->recv_from_backend = can_recv_from_backend; + + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief Initialize ZigBee communication session for IoT device + * @param dev Pointer to IotDevice instance (pre-initialized device) + * @param sess Pointer to IoTFrontendSession instance (memory pre-allocated) + * @return int Execution result + * - FRONTEND_PROXY_PROCESS_OK (0): Session initialized successfully + * - FRONTEND_PROXY_PROCESS_ERROR (-1): Session initialization failed + * + * @note This function only initializes session context, NOT device hardware. + * Device initialization is completed in the prior stage. + * The memory of IoTFrontendSession is pre-allocated, no need to manage. + * 1. Binds session context to the corresponding ZigBee device + * 2. Configures session state and network communication parameters + * 3. Sets up wireless data interaction logic + * 4. Prepares session for subsequent network communication + * + * @warning Device hardware initialization must be completed before calling + * @warning Session memory is managed externally, do not free in this function + */ +int engine_init_zigbee_session(IotDevice *dev, IoTFrontendSession *sess){ +if(NULL == dev || NULL == sess){ + error_print("engine_init_bluetooth_session failed: invalid input parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess->eng = frontend_get_global_engine(); + sess->bound_dev = dev; + sess->send_to_backend = zigbee_send_to_backend; + sess->recv_from_backend = zigbee_recv_from_backend; + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief Initialize LoRa communication session for IoT device + * @param dev Pointer to IotDevice instance (pre-initialized device) + * @param sess Pointer to IoTFrontendSession instance (memory pre-allocated) + * @return int Execution result + * - FRONTEND_PROXY_PROCESS_OK (0): Session initialized successfully + * - FRONTEND_PROXY_PROCESS_ERROR (-1): Session initialization failed + * + * @note This function only initializes session context, NOT device hardware. + * Device initialization is completed in the prior stage. + * The memory of IoTFrontendSession is pre-allocated, no need to manage. + * 1. Binds session context to the corresponding LoRa device + * 2. Configures session state and long-range communication parameters + * 3. Sets up uplink/downlink data transfer logic + * 4. Prepares session for subsequent RF communication + * + * @warning Device hardware initialization must be completed before calling + * @warning Session memory is managed externally, do not free in this function + */ +int engine_init_lora_session(IotDevice *dev, IoTFrontendSession *sess){ + if(NULL == dev || NULL == sess){ + error_print("engine_init_bluetooth_session failed: invalid input parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess->eng = frontend_get_global_engine(); + sess->bound_dev = dev; + sess->send_to_backend = lora_send_to_backend; + sess->recv_from_backend = lora_recv_from_backend; + + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief Initialize PowerLink real-time communication session for IoT device + * @param dev Pointer to IotDevice instance (pre-initialized device) + * @param sess Pointer to IoTFrontendSession instance (memory pre-allocated) + * @return int Execution result + * - FRONTEND_PROXY_PROCESS_OK (0): Session initialized successfully + * - FRONTEND_PROXY_PROCESS_ERROR (-1): Session initialization failed + * + * @note This function only initializes session context, NOT device hardware. + * Device initialization is completed in the prior stage. + * The memory of IoTFrontendSession is pre-allocated, no need to manage. + * 1. Binds real-time session context to the corresponding PowerLink device + * 2. Configures session state and synchronous communication parameters + * 3. Sets up real-time data buffer and processing logic + * 4. Prepares session for subsequent industrial Ethernet communication + * + * @warning Device hardware initialization must be completed before calling + * @warning Session memory is managed externally, do not free in this function + */ +int engine_init_powerlink_session(IotDevice *dev, IoTFrontendSession *sess){ + if(NULL == dev || NULL == sess){ + error_print("engine_init_bluetooth_session failed: invalid input parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess->eng = frontend_get_global_engine(); + sess->bound_dev = dev; + sess->send_to_backend = powerlink_send_to_backend; + sess->recv_from_backend = powerlink_recv_from_backend; + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief Initialize ModbusTCP communication session for IoT device + * @param dev Pointer to IotDevice instance (pre-initialized device) + * @param sess Pointer to IoTFrontendSession instance (memory pre-allocated) + * @return int Execution result + * - FRONTEND_PROXY_PROCESS_OK (0): Session initialized successfully + * - FRONTEND_PROXY_PROCESS_ERROR (-1): Session initialization failed + * + * @note This function only initializes session context, NOT device hardware. + * Device initialization is completed in the prior stage. + * The memory of IoTFrontendSession is pre-allocated, no need to manage. + * 1. Binds session context to the corresponding ModbusTCP device + * 2. Configures session state and TCP communication parameters + * 3. Sets up data buffer and Modbus frame processing logic + * 4. Prepares session for subsequent ModbusTCP communication + * + * @warning Device hardware initialization must be completed before calling + * @warning Session memory is managed externally, do not free in this function + */ +int engine_init_modbustcp_session(IotDevice *dev, IoTFrontendSession *sess){ + if(NULL == dev || NULL == sess){ + error_print("engine_init_bluetooth_session failed: invalid input parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + sess->eng = frontend_get_global_engine(); + sess->bound_dev = dev; + sess->send_to_backend = modbustcp_send_to_backend; + sess->recv_from_backend = modbustcp_recv_from_backend; + + return FRONTEND_PROXY_PROCESS_OK; +} + void frontend_engine_destory(){} \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 5982954..bbc8dc8 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -252,3 +252,147 @@ int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size) { return 0; } + + + +/** + * @brief Send Bluetooth data to backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (Bluetooth protocol session) + * @param msg_buf Pointer to IotMsgBuffer (contains Bluetooth data + destination address) + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int bluetooth_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; +} + +/** + * @brief Receive Bluetooth data from backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (Bluetooth protocol session) + * @param msg_buf Pointer to IotMsgBuffer (assembled and returned to caller) + * @param data Raw data buffer received from backend + * @param data_len Length of raw data received from backend + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int bluetooth_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Send CAN data to backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (CAN protocol session) + * @param msg_buf Pointer to IotMsgBuffer (contains CAN data + destination address) + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int can_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Receive CAN data from backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (CAN protocol session) + * @param msg_buf Pointer to IotMsgBuffer (assembled and returned to caller) + * @param data Raw data buffer received from backend + * @param data_len Length of raw data received from backend + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int can_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Send Zigbee data to backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (Zigbee protocol session) + * @param msg_buf Pointer to IotMsgBuffer (contains Zigbee data + destination address) + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int zigbee_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Receive Zigbee data from backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (Zigbee protocol session) + * @param msg_buf Pointer to IotMsgBuffer (assembled and returned to caller) + * @param data Raw data buffer received from backend + * @param data_len Length of raw data received from backend + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int zigbee_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Send LoRa data to backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (LoRa protocol session) + * @param msg_buf Pointer to IotMsgBuffer (contains LoRa data + destination address) + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int lora_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Receive LoRa data from backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (LoRa protocol session) + * @param msg_buf Pointer to IotMsgBuffer (assembled and returned to caller) + * @param data Raw data buffer received from backend + * @param data_len Length of raw data received from backend + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int lora_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Send PowerLink data to backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (PowerLink protocol session) + * @param msg_buf Pointer to IotMsgBuffer (contains PowerLink data + destination address) + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int powerlink_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Receive PowerLink data from backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (PowerLink protocol session) + * @param msg_buf Pointer to IotMsgBuffer (assembled and returned to caller) + * @param data Raw data buffer received from backend + * @param data_len Length of raw data received from backend + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int powerlink_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Send ModbusTCP data to backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (ModbusTCP protocol session) + * @param msg_buf Pointer to IotMsgBuffer (contains ModbusTCP data + destination address) + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int modbustcp_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ + return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Receive ModbusTCP data from backend via frontend session + * @param sess Pointer to IoTFrontendSession instance (ModbusTCP protocol session) + * @param msg_buf Pointer to IotMsgBuffer (assembled and returned to caller) + * @param data Raw data buffer received from backend + * @param data_len Length of raw data received from backend + * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure + */ +int modbustcp_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + return FRONTEND_PROXY_PROCESS_OK; +} \ No newline at end of file -- Gitee From 682bcf76ddc5b599786cc5c07c238674be9d6770 Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 19 Mar 2026 20:03:12 +0800 Subject: [PATCH 100/133] IoT process functions --- .../sel4test/apps/front/include/session.h | 14 ++ .../apps/front/include/shared_mem_io.h | 7 +- projects/sel4test/apps/front/src/engine.c | 8 ++ projects/sel4test/apps/front/src/session.c | 120 +++++++++++++++++- 4 files changed, 146 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index 4202e7c..766511e 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -453,6 +453,12 @@ typedef enum { */ IOT_SESS_EVENT_CMD_RECEIVED, + /** + * @brief Downlink data received. + * Triggered when the session receives a data message from the cloud/app. + */ + IOT_SESS_EVENT_DATA_RECEIVED, + /** * @brief Uplink telemetry/report received. * Triggered when the session receives periodic status data or sensor readings from the device (e.g., Temperature, GPS). @@ -693,4 +699,12 @@ int powerlink_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, int modbustcp_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf); int modbustcp_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len); +void default_bluetooth_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf); +void default_can_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf); +void default_zigbee_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf); +void default_lora_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf); +void default_powerlink_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf); +void default_modbustcp_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf); + + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/shared_mem_io.h b/projects/sel4test/apps/front/include/shared_mem_io.h index 71906ef..ed61338 100644 --- a/projects/sel4test/apps/front/include/shared_mem_io.h +++ b/projects/sel4test/apps/front/include/shared_mem_io.h @@ -553,7 +553,9 @@ int shared_mem_pool_queue_recv_zc(struct SharedMemoryPoolQueue *queue, * Must be paired with SHARED_MEM_QUEUE_UNLOCK using the same queue to prevent deadlocks; * FRONTEND_PROXY_PROCESS_AGAIN indicates temporary unavailability - callers should retry later */ -#define SHARED_MEM_QUEUE_LOCK(queue) (fetch_shared_mem_pool_lock((queue)->pool)) +//#define SHARED_MEM_QUEUE_LOCK(queue) fetch_shared_mem_pool_lock((queue)->pool) +#define SHARED_MEM_QUEUE_LOCK(queue) fetch_shared_mem_pool_lock(&((queue)->pool->lock)) + /** * @brief Release access right to the shared memory queue by releasing the shared memory pool lock @@ -564,7 +566,8 @@ int shared_mem_pool_queue_recv_zc(struct SharedMemoryPoolQueue *queue, * Must be paired with SHARED_MEM_QUEUE_LOCK using the same queue to prevent deadlocks; * Does not return FRONTEND_PROXY_PROCESS_AGAIN - release operation either succeeds or fails */ -#define SHARED_MEM_QUEUE_UNLOCK(queue) (release_shared_mem_pool_lock((queue)->pool)) +//#define SHARED_MEM_QUEUE_UNLOCK(queue) (release_shared_mem_pool_lock((queue)->pool)) +#define SHARED_MEM_QUEUE_UNLOCK(queue) (release_shared_mem_pool_lock(&(queue)->pool->lock)) struct SharedMemoryPoolQueue *shared_mem_pool_queue_create_frontendend(const SharedMemoryPoolQueueConfig *config); diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index bc5b829..77dc3ab 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -1351,6 +1351,7 @@ int engine_init_bluetooth_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = bluetooth_send_to_backend; sess->recv_from_backend = bluetooth_recv_from_backend; + sess->event_callback = default_bluetooth_event_callback; return FRONTEND_PROXY_PROCESS_OK; } @@ -1384,6 +1385,7 @@ int engine_init_can_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = can_send_to_backend; sess->recv_from_backend = can_recv_from_backend; + sess->event_callback = default_can_event_callback; return FRONTEND_PROXY_PROCESS_OK; } @@ -1417,6 +1419,8 @@ if(NULL == dev || NULL == sess){ sess->bound_dev = dev; sess->send_to_backend = zigbee_send_to_backend; sess->recv_from_backend = zigbee_recv_from_backend; + sess->event_callback = default_zigbee_event_callback; + return FRONTEND_PROXY_PROCESS_OK; } @@ -1449,6 +1453,7 @@ int engine_init_lora_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = lora_send_to_backend; sess->recv_from_backend = lora_recv_from_backend; + sess->event_callback = default_lora_event_callback; return FRONTEND_PROXY_PROCESS_OK; } @@ -1482,6 +1487,8 @@ int engine_init_powerlink_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = powerlink_send_to_backend; sess->recv_from_backend = powerlink_recv_from_backend; + sess->event_callback = default_powerlink_event_callback; + return FRONTEND_PROXY_PROCESS_OK; } @@ -1514,6 +1521,7 @@ int engine_init_modbustcp_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = modbustcp_send_to_backend; sess->recv_from_backend = modbustcp_recv_from_backend; + sess->event_callback = default_modbustcp_event_callback; return FRONTEND_PROXY_PROCESS_OK; } diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index bbc8dc8..a30c8c8 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -395,4 +395,122 @@ int modbustcp_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_ */ int modbustcp_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ return FRONTEND_PROXY_PROCESS_OK; -} \ No newline at end of file +} + + +/** + * @brief Default Bluetooth event callback for frontend session + * + * Default handler for Bluetooth frontend session events: + * - Prints callback entry log + * - Processes IOT_SESS_EVENT_DATA_RECEIVED event (reserved for data handling) + * + * @param sess Pointer to IoTFrontendSession instance + * @param event IoT session event type (e.g., data received/link state changed) + * @param msg_buf Pointer to IotMsgBuffer (event-related data buffer) + * @return None + */ +void default_bluetooth_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf){ + utils_print("In %s\n", __func__); + + if(IOT_SESS_EVENT_DATA_RECEIVED == event){ + + } +} + +/** + * @brief Default CAN event callback for frontend session + * + * Default handler for CAN frontend session events: + * - Prints callback entry log + * - Processes IOT_SESS_EVENT_DATA_RECEIVED event (reserved for data handling) + * + * @param sess Pointer to IoTFrontendSession instance + * @param event IoT session event type (e.g., data received/link state changed) + * @param msg_buf Pointer to IotMsgBuffer (event-related data buffer) + * @return None + */ +void default_can_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf){ + utils_print("In %s\n", __func__); + if(IOT_SESS_EVENT_DATA_RECEIVED == event){ + + } +} + +/** + * @brief Default Zigbee event callback for frontend session + * + * Default handler for Zigbee frontend session events: + * - Prints callback entry log + * - Processes IOT_SESS_EVENT_DATA_RECEIVED event (reserved for data handling) + * + * @param sess Pointer to IoTFrontendSession instance + * @param event IoT session event type (e.g., data received/link state changed) + * @param msg_buf Pointer to IotMsgBuffer (event-related data buffer) + * @return None + */ +void default_zigbee_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf){ + utils_print("In %s\n", __func__); + if(IOT_SESS_EVENT_DATA_RECEIVED == event){ + + } +} + +/** + * @brief Default LoRa event callback for frontend session + * + * Default handler for LoRa frontend session events: + * - Prints callback entry log + * - Processes IOT_SESS_EVENT_DATA_RECEIVED event (reserved for data handling) + * + * @param sess Pointer to IoTFrontendSession instance + * @param event IoT session event type (e.g., data received/link state changed) + * @param msg_buf Pointer to IotMsgBuffer (event-related data buffer) + * @return None + */ +void default_lora_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf){ + utils_print("In %s\n", __func__); + if(IOT_SESS_EVENT_DATA_RECEIVED == event){ + + } +} + +/** + * @brief Default PowerLink event callback for frontend session + * + * Default handler for PowerLink frontend session events: + * - Prints callback entry log + * - Processes IOT_SESS_EVENT_DATA_RECEIVED event (reserved for data handling) + * + * @param sess Pointer to IoTFrontendSession instance + * @param event IoT session event type (e.g., data received/link state changed) + * @param msg_buf Pointer to IotMsgBuffer (event-related data buffer) + * @return None + */ +void default_powerlink_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf){ + utils_print("In %s\n", __func__); + if(IOT_SESS_EVENT_DATA_RECEIVED == event){ + + } +} + +/** + * @brief Default ModbusTCP event callback for frontend session + * + * Default handler for ModbusTCP frontend session events: + * - Prints callback entry log + * - Processes IOT_SESS_EVENT_DATA_RECEIVED event (reserved for data handling) + * + * @param sess Pointer to IoTFrontendSession instance + * @param event IoT session event type (e.g., data received/link state changed) + * @param msg_buf Pointer to IotMsgBuffer (event-related data buffer) + * @return None + */ +void default_modbustcp_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf){ + utils_print("In %s\n", __func__); + if(IOT_SESS_EVENT_DATA_RECEIVED == event){ + + } +} + + -- Gitee From 85349acb30321ab9e03827ec8085540ff5c32cae Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 19 Mar 2026 21:25:28 +0800 Subject: [PATCH 101/133] bluetooth functions --- .../apps/front/include/frontend_api.h | 4 +- .../apps/front/include/frontend_proto.h | 3 +- .../sel4test/apps/front/include/message.h | 12 +-- .../sel4test/apps/front/include/session.h | 29 ++++++ .../sel4test/apps/front/src/frontend_api.c | 31 +++++++ .../sel4test/apps/front/src/frontend_proto.c | 89 +++++++++++++++++-- projects/sel4test/apps/front/src/session.c | 40 ++++++++- 7 files changed, 191 insertions(+), 17 deletions(-) diff --git a/projects/sel4test/apps/front/include/frontend_api.h b/projects/sel4test/apps/front/include/frontend_api.h index d77aa8d..7d034ce 100644 --- a/projects/sel4test/apps/front/include/frontend_api.h +++ b/projects/sel4test/apps/front/include/frontend_api.h @@ -87,8 +87,8 @@ int frontend_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t siz int frontend_sess_bind_callback(struct FrontendSession *sess, SESS_EVENT_CALLBACK event_callback); -int frontend_iot_sess_send(struct FrontendSession *sess, uint8_t *data, uint32_t size); -int frontend_iot_sess_recv(struct FrontendSession *sess, uint8_t *data, uint32_t size); +int frontend_iot_sess_send(IoTFrontendSession *sess, uint8_t *data, uint32_t size); +int frontend_iot_sess_recv(IoTFrontendSession *sess, uint8_t *data, uint32_t size); #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/frontend_proto.h b/projects/sel4test/apps/front/include/frontend_proto.h index 04cdb62..c2683d8 100644 --- a/projects/sel4test/apps/front/include/frontend_proto.h +++ b/projects/sel4test/apps/front/include/frontend_proto.h @@ -115,5 +115,6 @@ int frontend_proxy_modbustcp_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, IotMsgHeader *iot_header, uint8_t *iot_data); - + +size_t get_iot_addr_length(IotProtoType addr_type); #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index 9761e3c..01565b1 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -759,7 +759,7 @@ typedef enum { * Configured Value: 244 bytes (Assumes BLE MTU negotiation enabled). * Adjust to 20 if operating in legacy BLE mode without MTU exchange. */ -#define BACKEND_BLUETOOTH_MAX_PAYLOAD (244) +#define FRONTEND_BLUETOOTH_MAX_PAYLOAD (244) /** * @brief Maximum pure payload for ZigBee (IEEE 802.15.4). @@ -771,7 +771,7 @@ typedef enum { * * Configured Value: 100 bytes (Conservative limit to avoid fragmentation). */ -#define BACKEND_ZIGBEE_MAX_PAYLOAD (100) +#define FRONTEND_ZIGBEE_MAX_PAYLOAD (100) /** * @brief Maximum pure payload for LoRa / LoRaWAN. @@ -783,7 +783,7 @@ typedef enum { * Configured Value: 222 bytes (Safe upper bound for most SF settings). * Reduce to ~50-60 bytes for long-range/low-data-rate scenarios (SF11/SF12). */ -#define BACKEND_LORA_MAX_PAYLOAD (222) +#define FRONTEND_LORA_MAX_PAYLOAD (222) /** * @brief Maximum pure payload for PowerLink (Power Line Communication). @@ -795,7 +795,7 @@ typedef enum { * Configured Value: 128 bytes (Typical value for many PLC applications). * Verify against specific hardware datasheet. */ -#define BACKEND_POWERLINK_MAX_PAYLOAD (128) +#define FRONTEND_POWERLINK_MAX_PAYLOAD (128) /** * @brief Maximum pure payload for CAN bus. @@ -807,7 +807,7 @@ typedef enum { * Configured Value: 64 bytes (Assumes CAN FD support). * IMPORTANT: Change to 8 if using legacy CAN 2.0 hardware. */ -#define BACKEND_CAN_MAX_PAYLOAD (64) +#define FRONTEND_CAN_MAX_PAYLOAD (64) /** * @brief Maximum pure payload for Modbus TCP. @@ -822,7 +822,7 @@ typedef enum { * * Configured Value: 253 bytes (Standard Modbus TCP PDU limit). */ -#define BACKEND_MODBUS_TCP_MAX_PAYLOAD (253) +#define FRONTEND_MODBUS_TCP_MAX_PAYLOAD (253) int build_proxy_general_message(struct FrontendEngine_ *engine, GeneralProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg, MemoryAllocMode alloc_mode, struct SharedMemoryPoolQueue *ring_buf); diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index 766511e..1c81cf1 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -707,4 +707,33 @@ void default_powerlink_event_callback(struct IoTFrontendSession_ *sess, IoTSessi void default_modbustcp_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf); +/** + * @brief Get max payload length by frontend session type + * @param sess IoTFrontendSession* pointer + * @return Corresponding max payload value, 0 if invalid type + */ +#define FRONTEND_IOCT_SESS_GET_MAX_PAYLOAD(sess) \ +({ \ + uint16_t __max_pay = 0; \ + if ((sess) != NULL) { \ + switch ((sess)->sess_type) { \ + case IOT_PROTO_TYPE_BLUETOOTH: \ + __max_pay = FRONTEND_BLUETOOTH_MAX_PAYLOAD; break; \ + case IOT_PROTO_TYPE_ZIGBEE: \ + __max_pay = FRONTEND_ZIGBEE_MAX_PAYLOAD; break; \ + case IOT_PROTO_TYPE_CAN: \ + __max_pay = FRONTEND_CAN_MAX_PAYLOAD; break; \ + case IOT_PROTO_TYPE_LORA: \ + __max_pay = FRONTEND_LORA_MAX_PAYLOAD; break; \ + case IOT_PROTO_TYPE_POWERLINK: \ + __max_pay = FRONTEND_POWERLINK_MAX_PAYLOAD; break; \ + case IOT_PROTO_TYPE_MODBUSTCP: \ + __max_pay = FRONTEND_MODBUS_TCP_MAX_PAYLOAD; break;\ + default: \ + __max_pay = 0; break; \ + } \ + } \ + __max_pay; \ +}) + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/frontend_api.c b/projects/sel4test/apps/front/src/frontend_api.c index 39c2f42..a38f317 100644 --- a/projects/sel4test/apps/front/src/frontend_api.c +++ b/projects/sel4test/apps/front/src/frontend_api.c @@ -694,4 +694,35 @@ int frontend_sess_bind_callback(struct FrontendSession *sess, SESS_EVENT_CALLBAC sess->event_callback = event_callback; return FRONTEND_PROXY_PROCESS_OK; +} + + +int frontend_iot_sess_send(IoTFrontendSession *sess, uint8_t *data, uint32_t size){ + int ret, max_payload_size, data_size; + IotMsgBuffer iot_msg_buf; + if(NULL == sess || NULL == data){ + error_print("frontend_iot_sess_send failed: invalid parameters!\n"); + return -1; + } + + max_payload_size = FRONTEND_IOCT_SESS_GET_MAX_PAYLOAD(sess); + + if(0 == max_payload_size){ + error_print("frontend_iot_sess_send failed: invalid session type!\n"); + return -1; + } + + data_size = (size < max_payload_size) ? size : max_payload_size; + + iot_msg_buf.data = data; + iot_msg_buf.len = data_size; + + ret = sess->send_to_backend(sess, &iot_msg_buf); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_iot_sess_send failed: sess->send_to_backend failed!\n"); + return -1; + } + + return data_size; } \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index ab08af9..a02a729 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -1462,7 +1462,7 @@ int frontend_proxy_iot_msg_process(uint16_t frontend_sess_id, uint16_t backend_s iot_data = msg + sizeof(IotMsgHeader); switch(iot_msg_hdr->proto_type){ - case IOT_DEV_TYPE_BLUETOOTH: + case IOT_PROTO_TYPE_BLUETOOTH: if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotBtAddr)){ error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for Bluetooth protocol!\n"); return FRONTEND_PROXY_PROCESS_ERROR; @@ -1470,7 +1470,7 @@ int frontend_proxy_iot_msg_process(uint16_t frontend_sess_id, uint16_t backend_s ret = frontend_proxy_bluetooth_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); break; - case IOT_DEV_TYPE_CAN: + case IOT_PROTO_TYPE_CAN: if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotCanAddr)){ error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for CAN protocol!\n"); return FRONTEND_PROXY_PROCESS_ERROR; @@ -1478,7 +1478,7 @@ int frontend_proxy_iot_msg_process(uint16_t frontend_sess_id, uint16_t backend_s ret = frontend_proxy_can_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); break; - case IOT_DEV_TYPE_ZIGBEE: + case IOT_PROTO_TYPE_ZIGBEE: if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotZigbeeAddr)){ error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for ZigBee protocol!\n"); return FRONTEND_PROXY_PROCESS_ERROR; @@ -1486,7 +1486,7 @@ int frontend_proxy_iot_msg_process(uint16_t frontend_sess_id, uint16_t backend_s ret = frontend_proxy_zigbee_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); break; - case IOT_DEV_TYPE_LORA: + case IOT_PROTO_TYPE_LORA: if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotLoraAddr)){ error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for LoRa protocol!\n"); return FRONTEND_PROXY_PROCESS_ERROR; @@ -1494,14 +1494,14 @@ int frontend_proxy_iot_msg_process(uint16_t frontend_sess_id, uint16_t backend_s ret = frontend_proxy_lora_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); break; - case IOT_DEV_TYPE_POWERLINK: + case IOT_PROTO_TYPE_POWERLINK: if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotPowerLinkAddr)){ error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for PowerLink protocol!\n"); return FRONTEND_PROXY_PROCESS_ERROR; } ret = frontend_proxy_powerlink_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); break; - case IOT_DEV_TYPE_MODBUSTCP: + case IOT_PROTO_TYPE_MODBUSTCP: if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotModbusTcpAddr)){ error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for ModbusTCP protocol!\n"); return FRONTEND_PROXY_PROCESS_ERROR; @@ -1543,7 +1543,41 @@ int frontend_proxy_bluetooth_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, IotMsgHeader *iot_header, uint8_t *iot_data){ - return FRONTEND_PROXY_PROCESS_OK; + utils_print("In %s\n", __func__); + IoTFrontendSession *iot_sess; + IotBtAddr *bt_addr; + uint8_t *iot_payload; + IotMsgBuffer iot_msg_buf; + int ret; + + if(NULL == iot_header || NULL == iot_data){ + error_print("frontend_proxy_bluetooth_msg_process failed: invalid parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(iot_header->payload_len <= sizeof(IotBtAddr)){ + error_print("frontend_proxy_bluetooth_msg_process failed: invalid iot_header->payload_len!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + iot_sess = frontend_bluetooth_sess; + (void)iot_sess; + + bt_addr = (IotBtAddr *)iot_data; + iot_payload = iot_data + sizeof(IotBtAddr); + + (void)bt_addr; + (void)iot_payload; + + iot_msg_buf.addr.addr_type = IOT_PROTO_TYPE_BLUETOOTH; + iot_msg_buf.data = iot_payload; + iot_msg_buf.len = iot_header->payload_len - sizeof(sizeof(IotBtAddr)); + memcpy(&iot_msg_buf.addr.addr_info.bt_addr, bt_addr, sizeof(IotBtAddr)); + + ret = iot_sess->recv_from_backend(iot_sess, &iot_msg_buf, iot_payload, iot_header->payload_len - sizeof(sizeof(IotBtAddr))); + + + return ret; } /** @@ -1689,4 +1723,45 @@ int frontend_proxy_modbustcp_msg_process(uint16_t frontend_sess_id, IotMsgHeader *iot_header, uint8_t *iot_data){ return FRONTEND_PROXY_PROCESS_OK; +} + + +/** + * @brief Helper function to calculate protocol-specific IoT address length (NEW) + * + * Calculates the actual byte length of valid address data for a given IoT protocol type (avoids padding): + * - BLUETOOTH: 8 bytes (6-byte MAC + 2-byte port) + * - CAN: 7 bytes (2-byte port + 4-byte CAN ID + 1-byte bus ID) + * - ZIGBEE: 11 bytes (8-byte MAC + 2-byte PAN ID + 1-byte endpoint) + * - LORA: 11 bytes (8-byte DevEUI + 2-byte port + 1-byte freq band) + * - POWERLINK: 9 bytes (2-byte NodeID + 6-byte MAC + 2-byte PDO ID) + * - UNKNOWN: 0 bytes (invalid) + * + * @param addr_type IoT protocol type (from IotProtoType) + * @return size_t Valid address length (bytes) for the protocol; 0 if invalid + * + * @note Used by build_proxy_iot_message to validate header->iot_addr_len + * @note Ensures minimal address data is included in the message (no unused union space) + * @note Returns 0 for all unrecognized/invalid IotProtoType values (fast fail) + */ +size_t get_iot_addr_length(IotProtoType addr_type) +{ + // Return exact address length per protocol type (no padding) + switch (addr_type) { + case IOT_PROTO_TYPE_BLUETOOTH: + return sizeof(IotBtAddr); + case IOT_PROTO_TYPE_CAN: + return sizeof(IotCanAddr); + case IOT_PROTO_TYPE_ZIGBEE: + return sizeof(IotZigbeeAddr); + case IOT_PROTO_TYPE_LORA: + return sizeof(IotLoraAddr); + case IOT_PROTO_TYPE_POWERLINK: + return sizeof(IotPowerLinkAddr); + case IOT_PROTO_TYPE_MODBUSTCP: + return sizeof(IotModbusTcpAddr); + default: + // Invalid/unknown protocol type - return 0 (invalid length) + return 0; + } } \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index a30c8c8..8113b32 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -262,7 +262,43 @@ int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size) * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int bluetooth_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ - return FRONTEND_PROXY_PROCESS_OK; + utils_print("In %s\n", __func__); + GeneralProxyMsgHeader iot_proxy_msg_hdr; + uint8_t *payload, **res_pointer; + uint8_t *res_buf[100] = {NULL}; + int ret; + char dest[18] = "01:23:45:67:89:AB"; + + + + if(NULL == sess || NULL == msg_buf){ + error_print("bluetooth_send_to_backend failed: invalid parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + iot_proxy_msg_hdr.outer_header.backend_sess_id = 0; + iot_proxy_msg_hdr.outer_header.frontend_sess_id = 0; + iot_proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_IOT; + iot_proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + res_pointer = res_buf; + + iot_proxy_msg_hdr.inner_header.iot_hdr.dev_port_id = 0; + iot_proxy_msg_hdr.inner_header.iot_hdr.opcode = 0; + iot_proxy_msg_hdr.inner_header.iot_hdr.proto_type = IOT_PROTO_TYPE_BLUETOOTH; + iot_proxy_msg_hdr.inner_header.iot_hdr.proto_ver = 0; + iot_proxy_msg_hdr.inner_header.iot_hdr.payload_len = msg_buf->len + get_iot_addr_length(IOT_PROTO_TYPE_BLUETOOTH); + iot_proxy_msg_hdr.inner_header.iot_hdr.reserve = 0; + + iot_proxy_msg_hdr.iot_addr_len = get_iot_addr_length(IOT_PROTO_TYPE_BLUETOOTH); + iot_proxy_msg_hdr.iot_addr.addr_type = IOT_PROTO_TYPE_BLUETOOTH; + iot_proxy_msg_hdr.iot_addr.addr_info.bt_addr.port = 1001; + memcpy(iot_proxy_msg_hdr.iot_addr.addr_info.bt_addr.mac, dest, sizeof(iot_proxy_msg_hdr.iot_addr.addr_info.bt_addr.mac)); + + + res_pointer = res_buf; + ret = build_proxy_general_message(sess->eng, &iot_proxy_msg_hdr, msg_buf->data, msg_buf->len, res_pointer, MEMORY_ALLOC_AMPQUEUE, NULL); + + return ret; } /** @@ -274,6 +310,8 @@ int bluetooth_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_ * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int bluetooth_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + sess->event_callback(sess, IOT_SESS_EVENT_DATA_RECEIVED, msg_buf); + return FRONTEND_PROXY_PROCESS_OK; } -- Gitee From b313aabcc3913644518824a4131580af17678226 Mon Sep 17 00:00:00 2001 From: xj009 Date: Thu, 19 Mar 2026 22:44:45 +0800 Subject: [PATCH 102/133] add util function for debug --- .../apps/front/include/common_utils.h | 2 + .../sel4test/apps/front/src/common_utils.c | 274 ++++++++++++++++++ 2 files changed, 276 insertions(+) diff --git a/projects/sel4test/apps/front/include/common_utils.h b/projects/sel4test/apps/front/include/common_utils.h index 4b122e6..377b54c 100644 --- a/projects/sel4test/apps/front/include/common_utils.h +++ b/projects/sel4test/apps/front/include/common_utils.h @@ -97,4 +97,6 @@ int debug_print(const char *format, ...); printf("===========================================\n"); \ } while (0) +int parse_proxy_protocol_and_print(const uint8_t *buffer); + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/common_utils.c b/projects/sel4test/apps/front/src/common_utils.c index f9898d6..4fb021c 100644 --- a/projects/sel4test/apps/front/src/common_utils.c +++ b/projects/sel4test/apps/front/src/common_utils.c @@ -1,4 +1,6 @@ #include "common_utils.h" +#include "session.h" +#include void error_print(char *error){ #if UTILS_ENABLE_DEBUG @@ -14,4 +16,276 @@ int debug_print(const char *format, ...){ vprintf(format, args); va_end(args); #endif +} + +/** + * @brief Parses the custom proxy protocol packet and prints details directly. + * + * @note This function does NOT take a buffer length parameter. It relies entirely + * on the 'payload_len' field in the ProxyMsgHeader. Ensure the input buffer is + * valid and large enough to avoid segmentation faults. + * + * @param buffer Pointer to the input buffer containing the full packet. + * @return int: BACKEND_PROXY_PROCESS_OK on success, BACKEND_PROXY_PROCESS_ERROR on failure. + */ +int parse_proxy_protocol_and_print(const uint8_t *buffer) { + if (!buffer) { + printf("[PROXY_PARSE_ERR] Input buffer is NULL.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + /* 1. Parse Proxy Header */ + const ProxyMsgHeader *hdr = (const ProxyMsgHeader *)buffer; + + if (hdr->version != 1) { + printf("[PROXY_PARSE_ERR] Invalid protocol version: %u (Expected 1).\n", hdr->version); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + const uint8_t *payload_start = buffer + sizeof(ProxyMsgHeader); + + printf("=== Proxy Packet Received ===\n"); + /* Using %hu for uint16_t fields to be safe across platforms */ + printf("Header: Ver=%u, Type=%u, F-SID=%hu, B-SID=%hu, PayloadLen=%hu\n", + hdr->version, hdr->proxy_msg_type, hdr->frontend_sess_id, + hdr->backend_sess_id, hdr->payload_len); + + /* 2. Dispatch based on message type */ + switch (hdr->proxy_msg_type) { + case PROXY_MSG_TYPE_DEV: { + if (hdr->payload_len < sizeof(DevMsgHeader)) { + printf("[PROXY_PARSE_ERR] Device message payload too short.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + const DevMsgHeader *dev_hdr = (const DevMsgHeader *)payload_start; + + const char *type_str = (dev_hdr->msg_type == DEV_MSG_DISABLE) ? "DISABLE" : + (dev_hdr->msg_type == DEV_MSG_ENABLE) ? "ENABLE" : "QUERY"; + const char *action_str = (dev_hdr->action_type == ACTION_TYPE_COMMAND) ? "CMD" : "RESP"; + + printf("[DEVICE MSG] Type=%s(%hu), Action=%s(%hu), MsgID=%hu, SubLen=%hu\n", + type_str, dev_hdr->msg_type, action_str, dev_hdr->action_type, + dev_hdr->msg_id, dev_hdr->payload_len); + + size_t content_offset = sizeof(DevMsgHeader); + if (hdr->payload_len < content_offset) { + return FRONTEND_PROXY_PROCESS_ERROR; + } + + uint16_t content_len = hdr->payload_len - content_offset; + const uint8_t *content = payload_start + content_offset; + + if (content_len > 0) { + printf(" Content Hex: "); + for (uint16_t i = 0; i < content_len && i < 16; i++) { + printf("%02X ", content[i]); + } + if (content_len > 16) printf("..."); + printf("\n"); + + if (dev_hdr->action_type == ACTION_TYPE_RESPONSE && content_len >= 2) { + printf(" -> Result: Success=%u, ErrCode=%u\n", content[0], content[1]); + } + } else { + printf(" Content: (Empty)\n"); + } + break; + } + + case PROXY_MSG_TYPE_STRGY: { + if (hdr->payload_len < sizeof(StrgyMsgHeader)) { + printf("[PROXY_PARSE_ERR] Strategy message payload too short.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + const StrgyMsgHeader *strgy_hdr = (const StrgyMsgHeader *)payload_start; + + const char *type_str = (strgy_hdr->msg_type == STRGY_MSG_SET) ? "SET" : "QUERY"; + const char *action_str = (strgy_hdr->action_type == ACTION_TYPE_COMMAND) ? "CMD" : "RESP"; + + printf("[STRATEGY MSG] Type=%s(%hu), Action=%s(%hu), MsgID=%hu, SubLen=%hu\n", + type_str, strgy_hdr->msg_type, action_str, strgy_hdr->action_type, + strgy_hdr->msg_id, strgy_hdr->payload_len); + break; + } + + case PROXY_MSG_TYPE_SESS: { + if (hdr->payload_len < sizeof(SessMsgHeader)) { + printf("[PROXY_PARSE_ERR] Session message payload too short.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + const SessMsgHeader *sess_hdr = (const SessMsgHeader *)payload_start; + + if (sess_hdr->ip_version != SESS_IPV4_PROTO && sess_hdr->ip_version != SESS_IPV6_PROTO) { + printf("[PROXY_PARSE_ERR] Invalid IP Version: %" PRIu16 " (Must be 4 or 6).\n", sess_hdr->ip_version); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + const char *type_str = (sess_hdr->msg_type == SESS_MSG_CREATE) ? "CREATE" : "CLOSE"; + const char *action_str = (sess_hdr->action_type == ACTION_TYPE_COMMAND) ? "CMD" : "RESP"; + const char *ip_str = (sess_hdr->ip_version == SESS_IPV4_PROTO) ? "IPv4" : "IPv6"; + + printf("[SESSION MSG] Type=%s(%" PRIu16 "), Action=%s(%" PRIu16 "), IP=%s(%" PRIu16 ")\n", + type_str, sess_hdr->msg_type, action_str, sess_hdr->action_type, ip_str, sess_hdr->ip_version); + + size_t header_consumed = sizeof(SessMsgHeader); + if (hdr->payload_len < header_consumed) { + return FRONTEND_PROXY_PROCESS_ERROR; + } + + uint16_t inner_len = hdr->payload_len - header_consumed; + const uint8_t *inner_payload = payload_start + header_consumed; + + if (sess_hdr->msg_type == SESS_MSG_CREATE && sess_hdr->action_type == ACTION_TYPE_COMMAND) { + if (sess_hdr->ip_version == SESS_IPV4_PROTO) { + if (inner_len < sizeof(SessParaIPv4)) { + printf("[PROXY_PARSE_ERR] IPv4 Create payload incomplete.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + const SessParaIPv4 *v4 = (const SessParaIPv4 *)inner_payload; + printf(" -> Target: DevID=%hu, Proto=%hu, Addr=%u.%u.%u.%u:%hu\n", + v4->dev_id, v4->trans_proto, + v4->ipv4_addr.data[0], v4->ipv4_addr.data[1], + v4->ipv4_addr.data[2], v4->ipv4_addr.data[3], + v4->port); + } else { + if (inner_len < sizeof(SessParaIPv6)) { + printf("[PROXY_PARSE_ERR] IPv6 Create payload incomplete.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + const SessParaIPv6 *v6 = (const SessParaIPv6 *)inner_payload; + printf(" -> Target: DevID=%hu, Proto=%hu, Addr=IPv6(%02x%02x:...)\n", + v6->dev_id, v6->trans_proto, + v6->ipv6_addr.data[0], v6->ipv6_addr.data[1]); + } + } else if (sess_hdr->action_type == ACTION_TYPE_RESPONSE) { + if (inner_len < 2) { + printf("[PROXY_PARSE_ERR] Session Response payload too short.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + printf(" -> Result: Success=%u, ErrCode=%u\n", inner_payload[0], inner_payload[1]); + } else { + printf(" -> Content: (Empty or Command Close)\n"); + } + break; + } + + case PROXY_MSG_TYPE_DATA: { + printf("[DATA MSG] Raw Data Length: %hu bytes\n", hdr->payload_len); + if (hdr->payload_len > 0) { + printf(" First 16 bytes: "); + uint16_t print_len = (hdr->payload_len < 16) ? hdr->payload_len : 16; + for (uint16_t i = 0; i < print_len; i++) { + printf("%02X ", payload_start[i]); + } + printf("\n"); + } + break; + } + + case PROXY_MSG_TYPE_IOT: { + if (hdr->payload_len < sizeof(IotMsgHeader)) { + printf("[PROXY_PARSE_ERR] IoT message payload too short for header.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + const IotMsgHeader *iot_hdr = (const IotMsgHeader *)payload_start; + + const char *proto_str = "UNKNOWN"; + switch (iot_hdr->proto_type) { + case IOT_PROTO_TYPE_BLUETOOTH: proto_str = "BLUETOOTH"; break; + case IOT_PROTO_TYPE_ZIGBEE: proto_str = "ZIGBEE"; break; + case IOT_PROTO_TYPE_CAN: proto_str = "CAN"; break; + case IOT_PROTO_TYPE_LORA: proto_str = "LORA"; break; + case IOT_PROTO_TYPE_POWERLINK: proto_str = "POWERLINK"; break; + case IOT_PROTO_TYPE_MODBUSTCP: proto_str = "MODBUS_TCP"; break; + } + + printf("[IOT MSG] Proto=%s(%hu), Opcode=%hu, PortID=%hu, PayloadLen=%hu\n", + proto_str, iot_hdr->proto_type, iot_hdr->opcode, + iot_hdr->dev_port_id, iot_hdr->payload_len); + + size_t header_consumed = sizeof(IotMsgHeader); + if (hdr->payload_len < header_consumed) { + return FRONTEND_PROXY_PROCESS_ERROR; + } + + size_t addr_size = 0; + switch (iot_hdr->proto_type) { + case IOT_PROTO_TYPE_BLUETOOTH: addr_size = sizeof(IotBtAddr); break; + case IOT_PROTO_TYPE_CAN: addr_size = sizeof(IotCanAddr); break; + case IOT_PROTO_TYPE_ZIGBEE: addr_size = sizeof(IotZigbeeAddr); break; + case IOT_PROTO_TYPE_LORA: addr_size = sizeof(IotLoraAddr); break; + case IOT_PROTO_TYPE_POWERLINK: addr_size = sizeof(IotPowerLinkAddr); break; + case IOT_PROTO_TYPE_MODBUSTCP: addr_size = sizeof(IotModbusTcpAddr); break; + default: + printf("[PROXY_PARSE_ERR] Unknown IoT Protocol Type: %hu\n", iot_hdr->proto_type); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if ((size_t)hdr->payload_len < header_consumed + addr_size + iot_hdr->payload_len) { + printf("[PROXY_PARSE_ERR] IoT message internal length mismatch.\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + const uint8_t *addr_ptr = payload_start + header_consumed; + const uint8_t *data_ptr = addr_ptr + addr_size; + + printf(" Address Info: "); + switch (iot_hdr->proto_type) { + case IOT_PROTO_TYPE_BLUETOOTH: { + const IotBtAddr *addr = (const IotBtAddr *)addr_ptr; + char safe_mac[19]; + memcpy(safe_mac, addr->mac, 18); + safe_mac[18] = '\0'; + printf("MAC=%s, Port=%hu\n", safe_mac, addr->port); + break; + } + case IOT_PROTO_TYPE_CAN: { + const IotCanAddr *addr = (const IotCanAddr *)addr_ptr; + printf("BusID=%u, CanID=0x%X, Port=%hu\n", addr->bus_id, addr->can_id, addr->port); + break; + } + case IOT_PROTO_TYPE_ZIGBEE: { + const IotZigbeeAddr *addr = (const IotZigbeeAddr *)addr_ptr; + printf("PAN=0x%04X, EP=%u, MAC=", addr->pan_id, addr->endpoint); + for(int i=0; i<8; i++) printf("%02X", addr->mac[i]); + printf("\n"); + break; + } + case IOT_PROTO_TYPE_LORA: { + const IotLoraAddr *addr = (const IotLoraAddr *)addr_ptr; + printf("DevEUI=%llu, Port=%hu, Band=%u\n", (unsigned long long)addr->dev_eui, addr->port, addr->freq_band); + break; + } + case IOT_PROTO_TYPE_POWERLINK: { + const IotPowerLinkAddr *addr = (const IotPowerLinkAddr *)addr_ptr; + printf("NodeID=%hu, PDO=%hu, MAC=", addr->node_id, addr->pdo_id); + for(int i=0; i<6; i++) printf("%02X", addr->mac[i]); + printf("\n"); + break; + } + case IOT_PROTO_TYPE_MODBUSTCP: { + const IotModbusTcpAddr *addr = (const IotModbusTcpAddr *)addr_ptr; + printf("IP=%u.%u.%u.%u, Port=%hu\n", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3], addr->port); + break; + } + } + + if (iot_hdr->payload_len > 0) { + printf(" IoT Payload (First 16 bytes): "); + uint16_t print_len = (iot_hdr->payload_len < 16) ? iot_hdr->payload_len : 16; + for (uint16_t i = 0; i < print_len; i++) { + printf("%02X ", data_ptr[i]); + } + printf("\n"); + } + break; + } + + default: + printf("[PROXY_PARSE_ERR] Unknown Proxy Message Type: %u\n", hdr->proxy_msg_type); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + printf("===========================\n"); + return FRONTEND_PROXY_PROCESS_OK; } \ No newline at end of file -- Gitee From 20e5da4eca53da93ae1f06468bc2465948f1b44c Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 20 Mar 2026 10:07:19 +0800 Subject: [PATCH 103/133] bluetooth test codes --- projects/sel4test/apps/front/src/engine.c | 25 ++++++++++++++++++- .../sel4test/apps/front/src/frontend_proto.c | 3 +++ projects/sel4test/apps/front/src/main.c | 1 + projects/sel4test/apps/front/src/session.c | 4 ++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 77dc3ab..a2358a4 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -436,6 +436,27 @@ void frontend_engine_init(){ free(p_g_fr_eng->dev_set); abort(); } + + ret = engine_init_iot_devices(p_g_fr_eng); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize iot devices!\n!"); + free(p_g_fr_eng->sess_pool); + free(p_g_fr_eng->dev_set); + abort(); + } + + ret = engine_init_iot_sessions(p_g_fr_eng); + + if(FRONTEND_PROXY_PROCESS_OK != ret){ + error_print("frontend_engine_init failed: failed to initialize iot sessions!\n!"); + free(p_g_fr_eng->sess_pool); + free(p_g_fr_eng->dev_set); + free(p_g_fr_eng->iot_dev_set); + abort(); + } + + utils_print("frontend_engine_init success!\n"); } @@ -1104,6 +1125,7 @@ int engine_init_iot_devices(FrontendEngine *engine){ * @warning **Only one instance per IoT protocol type is supported in system** */ int engine_init_iot_sessions(FrontendEngine *engine){ + utils_print("In %s\n", __func__); IotDevice *iot_dev; int iot_dev_cnt, iot_dev_num, ret; @@ -1124,10 +1146,11 @@ int engine_init_iot_sessions(FrontendEngine *engine){ return FRONTEND_PROXY_PROCESS_ERROR; } + utils_print("iot device number = %d\n", iot_dev_num); while(iot_dev_cnt < iot_dev_num){ iot_dev = &engine->iot_dev_set->iot_dev[iot_dev_cnt]; - if(IOT_DEV_STATUS_ONLINE == iot_dev->dev_status){ + if(IOT_DEV_STATUS_ONLINE == iot_dev->dev_status){ switch (iot_dev->dev_type) { case IOT_DEV_TYPE_BLUETOOTH: /* Ensure only ONE Bluetooth device instance is supported */ diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index a02a729..0d94eb1 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -306,6 +306,8 @@ int build_proxy_iot_message(GeneralProxyMsgHeader *header, } // Sanity check: Ensure calculated length does not exceed the union's maximum size + + utils_print("addr_info_len = %d, sizeof union = %d, header->iot_addr_len = %d\n", addr_info_len, sizeof(header->iot_addr.addr_info), header->iot_addr_len); if (addr_info_len > sizeof(header->iot_addr.addr_info)) { error_print("build_proxy_iot_message failed: calculated address length exceeds union size!\n"); return FRONTEND_PROXY_PROCESS_ERROR; @@ -516,6 +518,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h proxy_msg_payload_len = sizeof(IotMsgHeader) + sub_iot_hdr_len; + utils_print("In %s, before build_proxy_iot_message, sub_iot_hdr_len = %d, proxy_msg_payload_len = %d\n", sub_iot_hdr_len, proxy_msg_payload_len); ret = build_proxy_iot_message(proxy_msg_hdr, payload, payload_len, &msg_buf); utils_print("In %s, after build_proxy_iot_message, the return value is %d\n", __func__, ret); break; diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 1c177d9..3267ec4 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -91,6 +91,7 @@ int main(void){ ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.101:8888"); frontend_sess_bind_callback(sess, test_session_event_callback); + frontend_iot_sess_send(frontend_bluetooth_sess, "test bluetooth", strlen("test bluetooth")); #endif diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 8113b32..4e11b8e 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -267,7 +267,7 @@ int bluetooth_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_ uint8_t *payload, **res_pointer; uint8_t *res_buf[100] = {NULL}; int ret; - char dest[18] = "01:23:45:67:89:AB"; + char dest[18] = "A8:41:F4:8C:7F:E6"; @@ -276,6 +276,8 @@ int bluetooth_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_ return FRONTEND_PROXY_PROCESS_ERROR; } + utils_print("Bluetooth datalen = %d\n", msg_buf->len); + iot_proxy_msg_hdr.outer_header.backend_sess_id = 0; iot_proxy_msg_hdr.outer_header.frontend_sess_id = 0; iot_proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_IOT; -- Gitee From c2b36ed9e30568f1d1feb1d746a375fa56c91105 Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 20 Mar 2026 10:50:12 +0800 Subject: [PATCH 104/133] add debug codes --- projects/sel4test/apps/front/include/dev.h | 2 +- .../sel4test/apps/front/include/message.h | 1 + projects/sel4test/apps/front/src/engine.c | 30 +++++++++++-------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/projects/sel4test/apps/front/include/dev.h b/projects/sel4test/apps/front/include/dev.h index 0fbfe9d..33c5865 100644 --- a/projects/sel4test/apps/front/include/dev.h +++ b/projects/sel4test/apps/front/include/dev.h @@ -171,7 +171,7 @@ typedef struct IotDevStat_{ typedef struct IotDevice_ { // Device identification information int dev_id; // Unique device ID (global) - IotDevType dev_type; // IoT device type + int dev_type; // IoT device type, equal to protocol type char *ns_name; // Namespace name (for device grouping) IotDevStatus dev_status; // Device online/offline status diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index 01565b1..288e1bb 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -587,6 +587,7 @@ typedef struct { * @note Used to distinguish different IoT protocols in PROXY_MSG_TYPE_IOT payload */ typedef enum { + IOT_PROTO_TYPE_UNKNOWN, IOT_PROTO_TYPE_BLUETOOTH = 1, // Bluetooth protocol (BLE/Classic Bluetooth) IOT_PROTO_TYPE_ZIGBEE, // Zigbee protocol (802.15.4) IOT_PROTO_TYPE_CAN, // CAN bus protocol (CAN 2.0/CAN FD) diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index a2358a4..490c47b 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -1078,27 +1078,27 @@ int engine_init_iot_devices(FrontendEngine *engine){ } dev = &engine->iot_dev_set->iot_dev[0]; - dev->dev_type = IOT_DEV_TYPE_BLUETOOTH; + dev->dev_type = IOT_PROTO_TYPE_BLUETOOTH; dev->dev_status = IOT_DEV_STATUS_ONLINE; dev = &engine->iot_dev_set->iot_dev[1]; - dev->dev_type = IOT_DEV_TYPE_CAN; + dev->dev_type = IOT_PROTO_TYPE_CAN; dev->dev_status = IOT_DEV_STATUS_ONLINE; dev = &engine->iot_dev_set->iot_dev[2]; - dev->dev_type = IOT_DEV_TYPE_ZIGBEE; + dev->dev_type = IOT_PROTO_TYPE_ZIGBEE; dev->dev_status = IOT_DEV_STATUS_ONLINE; dev = &engine->iot_dev_set->iot_dev[3]; - dev->dev_type = IOT_DEV_TYPE_LORA; + dev->dev_type = IOT_PROTO_TYPE_LORA; dev->dev_status = IOT_DEV_STATUS_ONLINE; dev = &engine->iot_dev_set->iot_dev[4]; - dev->dev_type = IOT_DEV_TYPE_POWERLINK; + dev->dev_type = IOT_PROTO_TYPE_POWERLINK; dev->dev_status = IOT_DEV_STATUS_ONLINE; dev = &engine->iot_dev_set->iot_dev[5]; - dev->dev_type = IOT_DEV_TYPE_MODBUSTCP; + dev->dev_type = IOT_PROTO_TYPE_MODBUSTCP; dev->dev_status = IOT_DEV_STATUS_ONLINE; engine->iot_dev_num = 6; @@ -1152,7 +1152,7 @@ int engine_init_iot_sessions(FrontendEngine *engine){ iot_dev = &engine->iot_dev_set->iot_dev[iot_dev_cnt]; if(IOT_DEV_STATUS_ONLINE == iot_dev->dev_status){ switch (iot_dev->dev_type) { - case IOT_DEV_TYPE_BLUETOOTH: + case IOT_PROTO_TYPE_BLUETOOTH: /* Ensure only ONE Bluetooth device instance is supported */ if(NULL != frontend_bluetooth_sess){ error_print("engine_init_iot_sessions failed: only one bluetooth device is supported in frontendengine!\n"); @@ -1176,7 +1176,7 @@ int engine_init_iot_sessions(FrontendEngine *engine){ frontend_bluetooth_sess->eng = engine; break; - case IOT_DEV_TYPE_CAN: + case IOT_PROTO_TYPE_CAN: /* Ensure only ONE CAN device instance is supported */ if(NULL != frontend_can_sess){ error_print("engine_init_iot_sessions failed: only one CAN device is supported in frontendengine!\n"); @@ -1200,7 +1200,7 @@ int engine_init_iot_sessions(FrontendEngine *engine){ frontend_can_sess->eng = engine; break; - case IOT_DEV_TYPE_ZIGBEE: + case IOT_PROTO_TYPE_ZIGBEE: /* Ensure only ONE ZigBee device instance is supported */ if(NULL != frontend_zigbee_sess){ error_print("engine_init_iot_sessions failed: only one ZigBee device is supported in frontendengine!\n"); @@ -1224,7 +1224,7 @@ int engine_init_iot_sessions(FrontendEngine *engine){ frontend_zigbee_sess->eng = engine; break; - case IOT_DEV_TYPE_LORA: + case IOT_PROTO_TYPE_LORA: /* Ensure only ONE LoRa device instance is supported */ if(NULL != frontend_lora_sess){ error_print("engine_init_iot_sessions failed: only one LoRa device is supported in frontendengine!\n"); @@ -1248,7 +1248,7 @@ int engine_init_iot_sessions(FrontendEngine *engine){ frontend_lora_sess->eng = engine; break; - case IOT_DEV_TYPE_POWERLINK: + case IOT_PROTO_TYPE_POWERLINK: /* Ensure only ONE PowerLink device instance is supported */ if(NULL != frontend_powerlink_sess){ error_print("engine_init_iot_sessions failed: only one PowerLink device is supported in frontendengine!\n"); @@ -1272,7 +1272,7 @@ int engine_init_iot_sessions(FrontendEngine *engine){ frontend_powerlink_sess->eng = engine; break; - case IOT_DEV_TYPE_MODBUSTCP: + case IOT_PROTO_TYPE_MODBUSTCP: /* Ensure only ONE PowerLink device instance is supported */ if(NULL != frontend_modbustcp_sess){ error_print("engine_init_iot_sessions failed: only one modbusTCP device is supported in frontendengine!\n"); @@ -1374,6 +1374,7 @@ int engine_init_bluetooth_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = bluetooth_send_to_backend; sess->recv_from_backend = bluetooth_recv_from_backend; + sess->sess_type = IOT_PROTO_TYPE_BLUETOOTH; sess->event_callback = default_bluetooth_event_callback; return FRONTEND_PROXY_PROCESS_OK; @@ -1408,6 +1409,7 @@ int engine_init_can_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = can_send_to_backend; sess->recv_from_backend = can_recv_from_backend; + sess->sess_type = IOT_PROTO_TYPE_CAN; sess->event_callback = default_can_event_callback; return FRONTEND_PROXY_PROCESS_OK; @@ -1442,6 +1444,7 @@ if(NULL == dev || NULL == sess){ sess->bound_dev = dev; sess->send_to_backend = zigbee_send_to_backend; sess->recv_from_backend = zigbee_recv_from_backend; + sess->sess_type = IOT_PROTO_TYPE_ZIGBEE; sess->event_callback = default_zigbee_event_callback; return FRONTEND_PROXY_PROCESS_OK; @@ -1476,6 +1479,7 @@ int engine_init_lora_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = lora_send_to_backend; sess->recv_from_backend = lora_recv_from_backend; + sess->sess_type = IOT_PROTO_TYPE_LORA; sess->event_callback = default_lora_event_callback; return FRONTEND_PROXY_PROCESS_OK; @@ -1510,6 +1514,7 @@ int engine_init_powerlink_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = powerlink_send_to_backend; sess->recv_from_backend = powerlink_recv_from_backend; + sess->sess_type = IOT_PROTO_TYPE_POWERLINK; sess->event_callback = default_powerlink_event_callback; return FRONTEND_PROXY_PROCESS_OK; @@ -1544,6 +1549,7 @@ int engine_init_modbustcp_session(IotDevice *dev, IoTFrontendSession *sess){ sess->bound_dev = dev; sess->send_to_backend = modbustcp_send_to_backend; sess->recv_from_backend = modbustcp_recv_from_backend; + sess->sess_type = IOT_PROTO_TYPE_MODBUSTCP; sess->event_callback = default_modbustcp_event_callback; return FRONTEND_PROXY_PROCESS_OK; -- Gitee From f9304bdf2531d5087c7f58dd783c7aff90b2989c Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 20 Mar 2026 12:02:29 +0800 Subject: [PATCH 105/133] debug functions --- .../apps/front/include/common_utils.h | 4 + .../sel4test/apps/front/include/message.h | 2 +- .../sel4test/apps/front/src/common_utils.c | 203 ++++++++++++++++++ .../sel4test/apps/front/src/frontend_proto.c | 5 +- 4 files changed, 211 insertions(+), 3 deletions(-) diff --git a/projects/sel4test/apps/front/include/common_utils.h b/projects/sel4test/apps/front/include/common_utils.h index 377b54c..d06fa47 100644 --- a/projects/sel4test/apps/front/include/common_utils.h +++ b/projects/sel4test/apps/front/include/common_utils.h @@ -99,4 +99,8 @@ int debug_print(const char *format, ...); int parse_proxy_protocol_and_print(const uint8_t *buffer); +struct GeneralProxyMsgHeader_; +typedef struct GeneralProxyMsgHeader_ GeneralProxyMsgHeader; +void print_general_proxy_msg_header(const GeneralProxyMsgHeader *hdr); + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index 288e1bb..bdd5497 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -718,7 +718,7 @@ typedef struct IotMsgBuffer_ { void *ext_info; /**< Protocol-specific extended info (beyond address) */ } IotMsgBuffer; -typedef struct{ +typedef struct GeneralProxyMsgHeader_{ ProxyMsgHeader outer_header; // ProxyMsgType msg_type; union { // Nested union to reduce memory usage (avoids redundant space) diff --git a/projects/sel4test/apps/front/src/common_utils.c b/projects/sel4test/apps/front/src/common_utils.c index 4fb021c..1dbece6 100644 --- a/projects/sel4test/apps/front/src/common_utils.c +++ b/projects/sel4test/apps/front/src/common_utils.c @@ -288,4 +288,207 @@ int parse_proxy_protocol_and_print(const uint8_t *buffer) { printf("===========================\n"); return FRONTEND_PROXY_PROCESS_OK; +} + + +#define PRINT_SEPARATOR() printf("--------------------------------------------------\n") + + +/** + * @brief Prints the detailed information of a GeneralProxyMsgHeader. + * + * This function parses the outer header and, depending on the message type, + * parses the specific inner header and IoT address information. + * + * @param hdr Pointer to the GeneralProxyMsgHeader structure. + */ +void print_general_proxy_msg_header(const GeneralProxyMsgHeader *hdr) { + if (hdr == NULL) { + printf("[ERROR] Input header pointer is NULL.\n"); + return; + } + + const ProxyMsgHeader *outer = &hdr->outer_header; + + printf("=== GeneralProxyMsgHeader Dump ===\n"); + printf("[Outer Header]\n"); + printf(" Version: %u\n", outer->version); + printf(" Msg Type: %u ", outer->proxy_msg_type); + + // Print human-readable message type + switch (outer->proxy_msg_type) { + case PROXY_MSG_TYPE_DEV: printf("(DEV)\n"); break; + case PROXY_MSG_TYPE_STRGY: printf("(STRATEGY)\n"); break; + case PROXY_MSG_TYPE_SESS: printf("(SESSION)\n"); break; + case PROXY_MSG_TYPE_DATA: printf("(DATA)\n"); break; + case PROXY_MSG_TYPE_IOT: printf("(IOT)\n"); break; + default: printf("(UNKNOWN)\n"); break; + } + + printf(" Frontend Sess ID: %u\n", outer->frontend_sess_id); + printf(" Backend Sess ID: %u\n", outer->backend_sess_id); + printf(" Payload Len: %u\n", outer->payload_len); + + // Parse Inner Header based on message type + switch (outer->proxy_msg_type) { + case PROXY_MSG_TYPE_DEV: { + const DevMsgHeader *dev = (const DevMsgHeader *)&hdr->inner_header.dev_hdr; + printf("[Inner Header: DEV]\n"); + printf(" Version: %u\n", dev->version); + printf(" Msg Type: %u\n", dev->msg_type); + printf(" Msg ID: %u\n", dev->msg_id); + printf(" Action Type: %u\n", dev->action_type); + printf(" Payload Len: %u\n", dev->payload_len); + break; + } + + case PROXY_MSG_TYPE_STRGY: { + const StrgyMsgHeader *strgy = (const StrgyMsgHeader *)&hdr->inner_header.strgy_hdr; + printf("[Inner Header: STRATEGY]\n"); + printf(" Version: %u\n", strgy->version); + printf(" Msg Type: %u\n", strgy->msg_type); + printf(" Msg ID: %u\n", strgy->msg_id); + printf(" Action Type: %u\n", strgy->action_type); + printf(" Payload Len: %u\n", strgy->payload_len); + break; + } + + case PROXY_MSG_TYPE_SESS: { + const SessMsgHeader *sess = (const SessMsgHeader *)&hdr->inner_header.sess_hdr; + printf("[Inner Header: SESSION]\n"); + printf(" Version: %u\n", sess->version); + printf(" Msg Type: %u\n", sess->msg_type); + printf(" Action Type: %u\n", sess->action_type); + printf(" IP Version: %u\n", sess->ip_version); + printf(" Payload Len: %u\n", sess->payload_len); + break; + } + + case PROXY_MSG_TYPE_DATA: { + printf("[Inner Header: DATA]\n"); + printf(" (No specific inner header to parse for DATA messages)\n"); + break; + } + + case PROXY_MSG_TYPE_IOT: { + const IotMsgHeader *iot_hdr = (const IotMsgHeader *)&hdr->inner_header.iot_hdr; + printf("[Inner Header: IOT]\n"); + printf(" Proto Ver: %u\n", iot_hdr->proto_ver); + printf(" Proto Type: %u ", iot_hdr->proto_type); + + // Print human-readable protocol type + switch (iot_hdr->proto_type) { + case IOT_PROTO_TYPE_BLUETOOTH: printf("(BLUETOOTH)\n"); break; + case IOT_PROTO_TYPE_ZIGBEE: printf("(ZIGBEE)\n"); break; + case IOT_PROTO_TYPE_CAN: printf("(CAN)\n"); break; + case IOT_PROTO_TYPE_LORA: printf("(LORA)\n"); break; + case IOT_PROTO_TYPE_POWERLINK: printf("(POWERLINK)\n"); break; + case IOT_PROTO_TYPE_MODBUSTCP: printf("(MODBUS TCP)\n"); break; + default: printf("(UNKNOWN)\n"); break; + } + + printf(" Opcode: %u\n", iot_hdr->opcode); + printf(" Dev Port ID: %u\n", iot_hdr->dev_port_id); + printf(" Payload Len: %u\n", iot_hdr->payload_len); + printf(" Reserve: %u\n", iot_hdr->reserve); + + // Parse IoT Address Information + printf("[IoT Address Info]\n"); + printf(" Addr Len: %u\n", hdr->iot_addr_len); + + if (hdr->iot_addr_len == 0) { + printf(" (Address invalid/empty as length is 0)\n"); + } else { + // Check consistency between header proto_type and address type + if (hdr->iot_addr.addr_type != iot_hdr->proto_type) { + printf(" [WARNING] Mismatch: Header proto_type (%u) != Addr type (%u)\n", + iot_hdr->proto_type, hdr->iot_addr.addr_type); + } + + switch (hdr->iot_addr.addr_type) { + case IOT_PROTO_TYPE_UNKNOWN: + printf(" Type: UNKNOWN (No parsing performed)\n"); + break; + + case IOT_PROTO_TYPE_BLUETOOTH: { + const IotBtAddr *bt = &hdr->iot_addr.addr_info.bt_addr; + printf(" Type: BLUETOOTH\n"); + // Assuming mac is a null-terminated string or fixed length + printf(" MAC: %.17s\n", bt->mac); + printf(" Port: %u\n", bt->port); + break; + } + + case IOT_PROTO_TYPE_ZIGBEE: { + const IotZigbeeAddr *zb = &hdr->iot_addr.addr_info.zigbee_addr; + printf(" Type: ZIGBEE\n"); + printf(" MAC: "); + for (int i = 0; i < 8; i++) { + printf("%02X", zb->mac[i]); + if (i < 7) printf(":"); + } + printf("\n"); + printf(" PAN ID: 0x%04X\n", zb->pan_id); + printf(" Endpoint:%u\n", zb->endpoint); + break; + } + + case IOT_PROTO_TYPE_CAN: { + const IotCanAddr *can = &hdr->iot_addr.addr_info.can_addr; + printf(" Type: CAN\n"); + printf(" Port: %u\n", can->port); + printf(" CAN ID: 0x%08X\n", (unsigned int)can->can_id); + printf(" Bus ID: %u\n", can->bus_id); + break; + } + + case IOT_PROTO_TYPE_LORA: { + const IotLoraAddr *lora = &hdr->iot_addr.addr_info.lora_addr; + printf(" Type: LORA\n"); + printf(" Dev EUI: 0x%016" PRIx64 "\n", lora->dev_eui); + printf(" Port: %u\n", lora->port); + printf(" Freq Band: %u\n", lora->freq_band); + break; + } + + case IOT_PROTO_TYPE_POWERLINK: { + const IotPowerLinkAddr *pl = &hdr->iot_addr.addr_info.powerlink_addr; + printf(" Type: POWERLINK\n"); + printf(" Node ID: %u\n", pl->node_id); + printf(" MAC: "); + for (int i = 0; i < 6; i++) { + printf("%02X", pl->mac[i]); + if (i < 5) printf(":"); + } + printf("\n"); + printf(" PDO ID: %u\n", pl->pdo_id); + break; + } + + case IOT_PROTO_TYPE_MODBUSTCP: { + const IotModbusTcpAddr *mb = &hdr->iot_addr.addr_info.modbus_tcp_addr; + printf(" Type: MODBUS TCP\n"); + printf(" IP: %u.%u.%u.%u\n", mb->ip[0], mb->ip[1], mb->ip[2], mb->ip[3]); + printf(" Port: %u\n", mb->port); + break; + } + + default: + printf(" Type: UNSUPPORTED (%u)\n", hdr->iot_addr.addr_type); + // Optional: Print raw bytes for debugging + printf(" Raw Bytes: "); + for(int i=0; i<16; i++) printf("%02X ", hdr->iot_addr.addr_info.raw[i]); + printf("\n"); + break; + } + } + break; + } + + default: + printf("[Inner Header]: Unknown proxy_msg_type (%u), cannot parse inner header.\n", outer->proxy_msg_type); + break; + } + + PRINT_SEPARATOR(); } \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 0d94eb1..798a1c0 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -411,7 +411,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h outer_msg_type = header->outer_header.proxy_msg_type; // header->inner_header.dev_hdr.payload_len; - + /* * Allocate shared-memory for storing the proxy message. @@ -499,6 +499,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h break; case PROXY_MSG_TYPE_IOT: proxy_msg_payload_len = 0; + utils_print("CASE PROXY_MSG_TYPE_IOT:\n"); if(IOT_PROTO_TYPE_BLUETOOTH == header->inner_header.iot_hdr.proto_type){ sub_iot_hdr_len = sizeof(IotBtAddr); }else if(IOT_PROTO_TYPE_CAN == header->inner_header.iot_hdr.proto_type){ @@ -518,7 +519,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h proxy_msg_payload_len = sizeof(IotMsgHeader) + sub_iot_hdr_len; - utils_print("In %s, before build_proxy_iot_message, sub_iot_hdr_len = %d, proxy_msg_payload_len = %d\n", sub_iot_hdr_len, proxy_msg_payload_len); + utils_print("In %s, before build_proxy_iot_message, sub_iot_hdr_len = %d, proxy_msg_payload_len = %d\n", __func__, sub_iot_hdr_len, proxy_msg_payload_len); ret = build_proxy_iot_message(proxy_msg_hdr, payload, payload_len, &msg_buf); utils_print("In %s, after build_proxy_iot_message, the return value is %d\n", __func__, ret); break; -- Gitee From e084af6bf87c3ed2b2bc769ba82ab2d7d2215e63 Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 20 Mar 2026 13:48:15 +0800 Subject: [PATCH 106/133] add debug functions --- .../apps/front/include/common_utils.h | 4 + .../sel4test/apps/front/src/common_utils.c | 200 ++++++++++++++++++ 2 files changed, 204 insertions(+) diff --git a/projects/sel4test/apps/front/include/common_utils.h b/projects/sel4test/apps/front/include/common_utils.h index d06fa47..cec8db7 100644 --- a/projects/sel4test/apps/front/include/common_utils.h +++ b/projects/sel4test/apps/front/include/common_utils.h @@ -103,4 +103,8 @@ struct GeneralProxyMsgHeader_; typedef struct GeneralProxyMsgHeader_ GeneralProxyMsgHeader; void print_general_proxy_msg_header(const GeneralProxyMsgHeader *hdr); +struct IotMsgBuffer_; +typedef struct IotMsgBuffer_ IotMsgBuffer; +void print_iot_msg_buffer(const IotMsgBuffer *buf); + #endif \ No newline at end of file diff --git a/projects/sel4test/apps/front/src/common_utils.c b/projects/sel4test/apps/front/src/common_utils.c index 1dbece6..771c27e 100644 --- a/projects/sel4test/apps/front/src/common_utils.c +++ b/projects/sel4test/apps/front/src/common_utils.c @@ -490,5 +490,205 @@ void print_general_proxy_msg_header(const GeneralProxyMsgHeader *hdr) { break; } + PRINT_SEPARATOR(); +} + + +/** + * @brief Helper function to print IotAddr details. + * + * Determines the expected size and content based solely on addr_type. + * No external addr_len parameter is required. + * + * @param addr Pointer to the IotAddr structure. + */ +void print_iot_addr(const IotAddr *addr) { + if (addr == NULL) { + printf(" [Address] NULL\n"); + return; + } + + printf(" Protocol Type: %u ", addr->addr_type); + + // Print human-readable type name and determine expected size + int expected_size = 0; + switch (addr->addr_type) { + case IOT_PROTO_TYPE_UNKNOWN: + printf("(UNKNOWN)\n"); + printf(" (No address parsing for UNKNOWN type)\n"); + return; + + case IOT_PROTO_TYPE_BLUETOOTH: + printf("(BLUETOOTH)\n"); + expected_size = sizeof(IotBtAddr); // 18 + 2 = 20 bytes + break; + + case IOT_PROTO_TYPE_ZIGBEE: + printf("(ZIGBEE)\n"); + expected_size = sizeof(IotZigbeeAddr); // 8 + 2 + 1 = 11 bytes + break; + + case IOT_PROTO_TYPE_CAN: + printf("(CAN)\n"); + expected_size = sizeof(IotCanAddr); // 2 + 4 + 1 = 7 bytes + break; + + case IOT_PROTO_TYPE_LORA: + printf("(LORA)\n"); + expected_size = sizeof(IotLoraAddr); // 8 + 2 + 1 = 11 bytes + break; + + case IOT_PROTO_TYPE_POWERLINK: + printf("(POWERLINK)\n"); + expected_size = sizeof(IotPowerLinkAddr); // 2 + 6 + 2 = 10 bytes + break; + + case IOT_PROTO_TYPE_MODBUSTCP: + printf("(MODBUS TCP)\n"); + expected_size = sizeof(IotModbusTcpAddr); // 4 + 2 = 6 bytes + break; + + default: + printf("(UNSUPPORTED)\n"); + printf(" Raw Bytes: "); + for(int i=0; i<16; i++) printf("%02X ", addr->addr_info.raw[i]); + printf("\n"); + return; + } + + // Parse and print specific fields based on type + switch (addr->addr_type) { + case IOT_PROTO_TYPE_BLUETOOTH: { + const IotBtAddr *bt = &addr->addr_info.bt_addr; + printf(" MAC: %.17s\n", bt->mac); + printf(" Port: %u\n", bt->port); + break; + } + + case IOT_PROTO_TYPE_ZIGBEE: { + const IotZigbeeAddr *zb = &addr->addr_info.zigbee_addr; + printf(" MAC: "); + for (int i = 0; i < 8; i++) { + printf("%02X", zb->mac[i]); + if (i < 7) printf(":"); + } + printf("\n"); + printf(" PAN ID: 0x%04X\n", zb->pan_id); + printf(" Endpoint:%u\n", zb->endpoint); + break; + } + + case IOT_PROTO_TYPE_CAN: { + const IotCanAddr *can = &addr->addr_info.can_addr; + printf(" Port: %u\n", can->port); + printf(" CAN ID: 0x%08X\n", (unsigned int)can->can_id); + printf(" Bus ID: %u\n", can->bus_id); + break; + } + + case IOT_PROTO_TYPE_LORA: { + const IotLoraAddr *lora = &addr->addr_info.lora_addr; + printf(" Dev EUI: 0x%016" PRIx64 "\n", lora->dev_eui); + printf(" Port: %u\n", lora->port); + printf(" Freq Band: %u\n", lora->freq_band); + break; + } + + case IOT_PROTO_TYPE_POWERLINK: { + const IotPowerLinkAddr *pl = &addr->addr_info.powerlink_addr; + printf(" Node ID: %u\n", pl->node_id); + printf(" MAC: "); + for (int i = 0; i < 6; i++) { + printf("%02X", pl->mac[i]); + if (i < 5) printf(":"); + } + printf("\n"); + printf(" PDO ID: %u\n", pl->pdo_id); + break; + } + + case IOT_PROTO_TYPE_MODBUSTCP: { + const IotModbusTcpAddr *mb = &addr->addr_info.modbus_tcp_addr; + printf(" IP: %u.%u.%u.%u\n", mb->ip[0], mb->ip[1], mb->ip[2], mb->ip[3]); + printf(" Port: %u\n", mb->port); + break; + } + + default: + break; + } + + // Optional: Print the calculated expected size for debugging verification + printf(" (Expected Struct Size: %d bytes)\n", expected_size); +} + + +/** + * @brief Prints the detailed information of an IotMsgBuffer. + * + * Parses buffer metadata, decodes IotAddr based on type (no len param needed), + * and prints a hex dump of the raw data. ext_info is not dereferenced. + * + * @param buf Pointer to the IotMsgBuffer structure. + */ +void print_iot_msg_buffer(const IotMsgBuffer *buf) { + if (buf == NULL) { + printf("[ERROR] Input IotMsgBuffer pointer is NULL.\n"); + return; + } + + printf("=== IotMsgBuffer Dump ===\n"); + + // 1. Basic Metadata + printf("[Metadata]\n"); + printf(" Message ID: %" PRIu32 "\n", buf->msg_id); + printf(" Timestamp: %" PRIu64 " ms\n", buf->timestamp); + printf(" Data Length: %" PRIu32 " bytes\n", buf->len); + printf(" Data Pointer: %p\n", (void*)buf->data); + + if (buf->data == NULL && buf->len > 0) { + printf(" [WARNING] Data length is > 0 but data pointer is NULL!\n"); + } + + // 2. Extended Info (Pointer only, not dereferenced) + printf("[Extended Info]\n"); + if (buf->ext_info == NULL) { + printf(" Ptr: NULL\n"); + } else { + printf(" Ptr: %p (Content not parsed)\n", buf->ext_info); + } + + // 3. Address Information (No addr_len passed) + printf("[Address Information]\n"); + print_iot_addr(&buf->addr); + + // 4. Raw Data Hex Dump + printf("[Raw Data Payload]\n"); + if (buf->data == NULL || buf->len == 0) { + printf(" (No data to display)\n"); + } else { + // Limit dump size to avoid flooding console + uint32_t dump_len = (buf->len > 64) ? 64 : buf->len; + + printf(" Showing first %u of %u bytes:\n", dump_len, buf->len); + for (uint32_t i = 0; i < dump_len; i++) { + if (i % 16 == 0) { + printf(" %04X: ", i); + } + printf("%02X ", buf->data[i]); + + if ((i + 1) % 16 == 0) { + printf("\n"); + } + } + if (dump_len % 16 != 0) { + printf("\n"); + } + + if (buf->len > 64) { + printf(" ... (%" PRIu32 " bytes omitted)\n", buf->len - 64); + } + } + PRINT_SEPARATOR(); } \ No newline at end of file -- Gitee From 5426480f9cd70d2407c1a88fc0ff97b4bb8e6b8f Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 20 Mar 2026 16:11:25 +0800 Subject: [PATCH 107/133] fix a bug --- .../sel4test/apps/front/src/frontend_proto.c | 36 ++++++++++++++++--- .../apps/front/src/hyperamp_shm_queue.c | 2 ++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 798a1c0..3945e08 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -256,7 +256,12 @@ int build_proxy_iot_message(GeneralProxyMsgHeader *header, const uint8_t *payload, size_t payload_len, uint8_t **result_msg){ - /* + utils_print("In %s\n", __func__); + IotMsgHeader *iot_msg_hdr; + uint8_t *iot_data_msg; + size_t addr_info_len; + + /* * 1. Basic Parameter Validation (Safety Check) */ if (!header || !payload || !result_msg || !(*result_msg)) { @@ -266,6 +271,10 @@ int build_proxy_iot_message(GeneralProxyMsgHeader *header, // Extract the IoT protocol type from the inner header to determine address size uint16_t proto_type = header->inner_header.iot_hdr.proto_type; + + utils_print("proto_type = %d\n", proto_type); + utils_print("header->inner_header.iot_hdr.proto_type = %d, address of header->inner_header.iot_hdr.proto_type = %p\n", + header->inner_header.iot_hdr.proto_type, &header->inner_header.iot_hdr.proto_type); // Optional Consistency Check: Ensure the header type matches the address type stored in IotAddr if (proto_type != header->iot_addr.addr_type) { @@ -273,14 +282,29 @@ int build_proxy_iot_message(GeneralProxyMsgHeader *header, // Proceeding anyway as the switch statement below will handle the actual size based on proto_type } - uint8_t *iot_data_msg = *result_msg; - size_t addr_info_len = 0; + print_general_proxy_msg_header(header); +/* + * Fill IoT header. + */ + + iot_data_msg = *result_msg; + iot_msg_hdr = (IotMsgHeader *)iot_data_msg; + iot_msg_hdr->dev_port_id = 0; + iot_msg_hdr->opcode = 0; + iot_msg_hdr->proto_type = header->inner_header.iot_hdr.proto_type; + iot_msg_hdr->proto_ver = 0; + iot_msg_hdr->reserve = 0; + + + iot_data_msg = iot_data_msg + sizeof(IotMsgHeader); + addr_info_len = 0; /* * 2. Determine the exact valid length of the specific address structure * Purpose: Prevent copying uninitialized garbage data from the union. * Since we are NOT sending the 'addr_type' byte, we only copy the 'addr_info' union part. */ + switch (proto_type) { case IOT_PROTO_TYPE_BLUETOOTH: addr_info_len = sizeof(IotBtAddr); @@ -336,6 +360,7 @@ int build_proxy_iot_message(GeneralProxyMsgHeader *header, memcpy(iot_data_msg, payload, payload_len); } + iot_msg_hdr->payload_len = payload_len + addr_info_len; return FRONTEND_PROXY_PROCESS_OK; } @@ -499,7 +524,8 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h break; case PROXY_MSG_TYPE_IOT: proxy_msg_payload_len = 0; - utils_print("CASE PROXY_MSG_TYPE_IOT:\n"); + utils_print("CASE PROXY_MSG_TYPE_IOT, inner_header.iot_hdr.proto_type = %d, address of inner_header.iot_hdr.proto_type = %p\n", + header->inner_header.iot_hdr.proto_type, &header->inner_header.iot_hdr.proto_type); if(IOT_PROTO_TYPE_BLUETOOTH == header->inner_header.iot_hdr.proto_type){ sub_iot_hdr_len = sizeof(IotBtAddr); }else if(IOT_PROTO_TYPE_CAN == header->inner_header.iot_hdr.proto_type){ @@ -520,7 +546,7 @@ int build_proxy_general_message(FrontendEngine *engine, GeneralProxyMsgHeader *h proxy_msg_payload_len = sizeof(IotMsgHeader) + sub_iot_hdr_len; utils_print("In %s, before build_proxy_iot_message, sub_iot_hdr_len = %d, proxy_msg_payload_len = %d\n", __func__, sub_iot_hdr_len, proxy_msg_payload_len); - ret = build_proxy_iot_message(proxy_msg_hdr, payload, payload_len, &msg_buf); + ret = build_proxy_iot_message(header, payload, payload_len, &msg_buf); utils_print("In %s, after build_proxy_iot_message, the return value is %d\n", __func__, ret); break; default: diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index 5c4780d..3701154 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -210,6 +210,8 @@ int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, new_header -= capacity; } + parse_proxy_protocol_and_print(data); + // Check if the queue is full if (new_header == tail) { hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); -- Gitee From 91ee23c0d21b5fbb9751eaef425004997e6828af Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 20 Mar 2026 17:47:55 +0800 Subject: [PATCH 108/133] fix a bug --- .../sel4test/apps/front/src/common_utils.c | 205 +++++++++++++++++- 1 file changed, 204 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/common_utils.c b/projects/sel4test/apps/front/src/common_utils.c index 771c27e..67c8f41 100644 --- a/projects/sel4test/apps/front/src/common_utils.c +++ b/projects/sel4test/apps/front/src/common_utils.c @@ -221,7 +221,8 @@ int parse_proxy_protocol_and_print(const uint8_t *buffer) { return FRONTEND_PROXY_PROCESS_ERROR; } - if ((size_t)hdr->payload_len < header_consumed + addr_size + iot_hdr->payload_len) { + printf("hdr->payload_len = %d, header_consumed = %zu, addr_size = %zu, iot_hdr->payload_len = %d\n", hdr->payload_len, header_consumed, addr_size, iot_hdr->payload_len); + if ((size_t)hdr->payload_len < header_consumed + iot_hdr->payload_len) { printf("[PROXY_PARSE_ERR] IoT message internal length mismatch.\n"); return FRONTEND_PROXY_PROCESS_ERROR; } @@ -294,6 +295,7 @@ int parse_proxy_protocol_and_print(const uint8_t *buffer) { #define PRINT_SEPARATOR() printf("--------------------------------------------------\n") +#if 0 /** * @brief Prints the detailed information of a GeneralProxyMsgHeader. * @@ -492,6 +494,207 @@ void print_general_proxy_msg_header(const GeneralProxyMsgHeader *hdr) { PRINT_SEPARATOR(); } +#endif + +/** + * @brief Helper to print IotAddr based solely on type (no len param). + */ +void print_iot_addr_details(const IotAddr *addr) { + if (addr == NULL) return; + + printf(" Protocol Type: %u ", addr->addr_type); + + switch (addr->addr_type) { + case IOT_PROTO_TYPE_UNKNOWN: + printf("(UNKNOWN)\n"); + return; + case IOT_PROTO_TYPE_BLUETOOTH: + printf("(BLUETOOTH)\n"); + { + const IotBtAddr *bt = &addr->addr_info.bt_addr; + printf(" MAC: %.17s\n", bt->mac); + printf(" Port: %u\n", bt->port); + } + break; + case IOT_PROTO_TYPE_ZIGBEE: + printf("(ZIGBEE)\n"); + { + const IotZigbeeAddr *zb = &addr->addr_info.zigbee_addr; + printf(" MAC: "); + for (int i = 0; i < 8; i++) { printf("%02X", zb->mac[i]); if(i<7) printf(":"); } + printf("\n PAN ID: 0x%04X\n Endpoint:%u\n", zb->pan_id, zb->endpoint); + } + break; + case IOT_PROTO_TYPE_CAN: + printf("(CAN)\n"); + { + const IotCanAddr *can = &addr->addr_info.can_addr; + printf(" Port: %u\n CAN ID: 0x%08X\n Bus ID: %u\n", can->port, (unsigned int)can->can_id, can->bus_id); + } + break; + case IOT_PROTO_TYPE_LORA: + printf("(LORA)\n"); + { + const IotLoraAddr *lora = &addr->addr_info.lora_addr; + printf(" Dev EUI: 0x%016" PRIx64 "\n Port: %u\n Freq Band: %u\n", lora->dev_eui, lora->port, lora->freq_band); + } + break; + case IOT_PROTO_TYPE_POWERLINK: + printf("(POWERLINK)\n"); + { + const IotPowerLinkAddr *pl = &addr->addr_info.powerlink_addr; + printf(" Node ID: %u\n MAC: ", pl->node_id); + for (int i = 0; i < 6; i++) { printf("%02X", pl->mac[i]); if(i<5) printf(":"); } + printf("\n PDO ID: %u\n", pl->pdo_id); + } + break; + case IOT_PROTO_TYPE_MODBUSTCP: + printf("(MODBUS TCP)\n"); + { + const IotModbusTcpAddr *mb = &addr->addr_info.modbus_tcp_addr; + printf(" IP: %u.%u.%u.%u\n Port: %u\n", mb->ip[0], mb->ip[1], mb->ip[2], mb->ip[3], mb->port); + } + break; + default: + printf("(UNSUPPORTED)\n"); + break; + } +} + + +/** + * @brief Prints the GeneralProxyMsgHeader. + * + * CORRECTION APPLIED: + * The length validation logic for IOT messages has been fixed. + * Logic: hdr->payload_len MUST EQUAL (sizeof(IotMsgHeader) + iot_hdr->payload_len). + * Where iot_hdr->payload_len includes both the Address and the Actual Data. + */ +void print_general_proxy_msg_header(const GeneralProxyMsgHeader *hdr) { + if (hdr == NULL) { + printf("[ERROR] Input header pointer is NULL.\n"); + return; + } + + const ProxyMsgHeader *outer = &hdr->outer_header; + + printf("=== GeneralProxyMsgHeader Dump ===\n"); + printf("[Outer Header]\n"); + printf(" Version: %u\n", outer->version); + printf(" Msg Type: %u ", outer->proxy_msg_type); + + switch (outer->proxy_msg_type) { + case PROXY_MSG_TYPE_DEV: printf("(DEV)\n"); break; + case PROXY_MSG_TYPE_STRGY: printf("(STRATEGY)\n"); break; + case PROXY_MSG_TYPE_SESS: printf("(SESSION)\n"); break; + case PROXY_MSG_TYPE_DATA: printf("(DATA)\n"); break; + case PROXY_MSG_TYPE_IOT: printf("(IOT)\n"); break; + default: printf("(UNKNOWN)\n"); break; + } + + printf(" Frontend Sess ID: %u\n", outer->frontend_sess_id); + printf(" Backend Sess ID: %u\n", outer->backend_sess_id); + printf(" Payload Len: %u\n", outer->payload_len); + + switch (outer->proxy_msg_type) { + case PROXY_MSG_TYPE_DEV: { + const DevMsgHeader *dev = (const DevMsgHeader *)&hdr->inner_header.dev_hdr; + printf("[Inner Header: DEV]\n"); + printf(" Version: %u, MsgType: %u, ID: %u, Action: %u, PayLen: %u\n", + dev->version, dev->msg_type, dev->msg_id, dev->action_type, dev->payload_len); + break; + } + case PROXY_MSG_TYPE_STRGY: { + const StrgyMsgHeader *s = (const StrgyMsgHeader *)&hdr->inner_header.strgy_hdr; + printf("[Inner Header: STRATEGY]\n"); + printf(" Version: %u, MsgType: %u, ID: %u, Action: %u, PayLen: %u\n", + s->version, s->msg_type, s->msg_id, s->action_type, s->payload_len); + break; + } + case PROXY_MSG_TYPE_SESS: { + const SessMsgHeader *s = (const SessMsgHeader *)&hdr->inner_header.sess_hdr; + printf("[Inner Header: SESSION]\n"); + printf(" Version: %u, MsgType: %u, Action: %u, IPVer: %u, PayLen: %u\n", + s->version, s->msg_type, s->action_type, s->ip_version, s->payload_len); + break; + } + case PROXY_MSG_TYPE_DATA: + printf("[Inner Header: DATA] (No specific inner header)\n"); + break; + + case PROXY_MSG_TYPE_IOT: { + const IotMsgHeader *iot_hdr = (const IotMsgHeader *)&hdr->inner_header.iot_hdr; + printf("[Inner Header: IOT]\n"); + printf(" Proto Ver: %u\n", iot_hdr->proto_ver); + printf(" Proto Type: %u ", iot_hdr->proto_type); + + // Basic validation: Ensure payload_len is at least large enough for the smallest address? + // Actually, we trust the type for printing, but we can log a warning if it looks too small. + + switch (iot_hdr->proto_type) { + case IOT_PROTO_TYPE_BLUETOOTH: printf("(BLUETOOTH)\n"); break; + case IOT_PROTO_TYPE_ZIGBEE: printf("(ZIGBEE)\n"); break; + case IOT_PROTO_TYPE_CAN: printf("(CAN)\n"); break; + case IOT_PROTO_TYPE_LORA: printf("(LORA)\n"); break; + case IOT_PROTO_TYPE_POWERLINK: printf("(POWERLINK)\n"); break; + case IOT_PROTO_TYPE_MODBUSTCP: printf("(MODBUS TCP)\n"); break; + default: printf("(UNKNOWN)\n"); break; + } + + printf(" Opcode: %u\n", iot_hdr->opcode); + printf(" Dev Port ID: %u\n", iot_hdr->dev_port_id); + printf(" Payload Len: %u (Includes Addr + Data)\n", iot_hdr->payload_len); + printf(" Reserve: %u\n", iot_hdr->reserve); + + // --- LENGTH VALIDATION FIX --- + // The outer payload_len must cover: sizeof(IotMsgHeader) + iot_hdr->payload_len + size_t header_consumed = sizeof(IotMsgHeader); + + // Correct Logic: + // Total Available (outer) == Header Size + Declared Inner Payload (Addr + Data) + if (outer->payload_len != (header_consumed + iot_hdr->payload_len)) { + printf(" [WARNING] Length Mismatch!\n"); + printf(" Outer Payload (%u) != IotHeader Size (%zu) + Inner Payload (%u)\n", + outer->payload_len, header_consumed, iot_hdr->payload_len); + printf(" Expected: %zu, Got: %u\n", + (header_consumed + iot_hdr->payload_len), outer->payload_len); + } else { + printf(" [OK] Length validation passed.\n"); + } + + // Optional: Check if inner payload is at least big enough for the address type + size_t min_addr_size = 0; + switch (iot_hdr->proto_type) { + case IOT_PROTO_TYPE_BLUETOOTH: min_addr_size = sizeof(IotBtAddr); break; + case IOT_PROTO_TYPE_CAN: min_addr_size = sizeof(IotCanAddr); break; + case IOT_PROTO_TYPE_ZIGBEE: min_addr_size = sizeof(IotZigbeeAddr); break; + case IOT_PROTO_TYPE_LORA: min_addr_size = sizeof(IotLoraAddr); break; + case IOT_PROTO_TYPE_POWERLINK: min_addr_size = sizeof(IotPowerLinkAddr); break; + case IOT_PROTO_TYPE_MODBUSTCP: min_addr_size = sizeof(IotModbusTcpAddr); break; + default: min_addr_size = 0; break; + } + + if (min_addr_size > 0 && iot_hdr->payload_len < min_addr_size) { + printf(" [ERROR] Inner Payload (%u) is smaller than minimum Address Size (%zu) for this protocol!\n", + iot_hdr->payload_len, min_addr_size); + } + + // Print Address Info + // Note: Since this function takes a Struct Pointer, we assume 'hdr->iot_addr' + // has been correctly populated by the caller's parsing logic. + printf("[IoT Address Info]\n"); + print_iot_addr_details(&hdr->iot_addr); + + break; + } + + default: + printf("[Inner Header]: Unknown type.\n"); + break; + } + + PRINT_SEPARATOR(); +} /** -- Gitee From 376aa7afead90f5201e602a00289a34977593f42 Mon Sep 17 00:00:00 2001 From: xj009 Date: Sat, 21 Mar 2026 17:16:42 +0800 Subject: [PATCH 109/133] renew debug functions --- projects/sel4test/apps/front/include/message.h | 3 +++ projects/sel4test/apps/front/src/common_utils.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index bdd5497..b6754d6 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -684,6 +684,9 @@ typedef struct { typedef struct { uint8_t ip[4]; /**< IPv4 address (e.g., 192.168.1.10) */ uint16_t port; /**< TCP port number (Default: 502) */ + uint8_t unit_id; /**< Modbus unit ID (slave ID) */ + uint16_t reg_addr; /**< Start address of target register */ + uint16_t reg_num; /**< Total registers for read/write operations */ } __attribute__((packed)) IotModbusTcpAddr; diff --git a/projects/sel4test/apps/front/src/common_utils.c b/projects/sel4test/apps/front/src/common_utils.c index 67c8f41..dd4ce45 100644 --- a/projects/sel4test/apps/front/src/common_utils.c +++ b/projects/sel4test/apps/front/src/common_utils.c @@ -267,6 +267,8 @@ int parse_proxy_protocol_and_print(const uint8_t *buffer) { case IOT_PROTO_TYPE_MODBUSTCP: { const IotModbusTcpAddr *addr = (const IotModbusTcpAddr *)addr_ptr; printf("IP=%u.%u.%u.%u, Port=%hu\n", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3], addr->port); + printf(" UnitID=%u, RegAddr=%hu, RegNum=%hu\n", + addr->unit_id, addr->reg_addr, addr->reg_num); break; } } @@ -553,6 +555,8 @@ void print_iot_addr_details(const IotAddr *addr) { { const IotModbusTcpAddr *mb = &addr->addr_info.modbus_tcp_addr; printf(" IP: %u.%u.%u.%u\n Port: %u\n", mb->ip[0], mb->ip[1], mb->ip[2], mb->ip[3], mb->port); + printf(" UnitID=%u, RegAddr=%hu, RegNum=%hu\n", + mb->unit_id, mb->reg_addr, mb->reg_num); } break; default: -- Gitee From 392a23c1090bc20068dd03bfa1876471c1be65a4 Mon Sep 17 00:00:00 2001 From: xj009 Date: Sat, 21 Mar 2026 18:52:30 +0800 Subject: [PATCH 110/133] ModbusTcp and CAN --- .../apps/front/include/frontend_api.h | 67 ----------- .../sel4test/apps/front/include/session.h | 65 +++++++++++ .../sel4test/apps/front/src/frontend_proto.c | 67 ++++++++++- projects/sel4test/apps/front/src/session.c | 104 +++++++++++++++--- 4 files changed, 220 insertions(+), 83 deletions(-) diff --git a/projects/sel4test/apps/front/include/frontend_api.h b/projects/sel4test/apps/front/include/frontend_api.h index 7d034ce..cf20859 100644 --- a/projects/sel4test/apps/front/include/frontend_api.h +++ b/projects/sel4test/apps/front/include/frontend_api.h @@ -9,73 +9,6 @@ #include "common_utils.h" -/** - * @brief Macro to convert a dotted-decimal IPv4 string to an IPv4Address structure - * @details Parses a string in "xxx.xxx.xxx.xxx" format, validates each octet range (0-255), - * and populates the IPv4Address structure with the binary representation. - * Provides error messages to stderr for invalid formats or out-of-range values. - * - * @param ip_str Input string in dotted-decimal IPv4 format (e.g., "192.168.1.1") - * @param addr_struct Output IPv4Address structure to be populated with parsed values - */ -#define IPV4_STR_TO_ADDR(ip_str, addr_struct) do { \ - unsigned int a, b, c, d; /**< Temporary storage for parsed octet values */ \ - \ - /* Attempt to parse 4 octets from the input string */ \ - if (sscanf(ip_str, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) { \ - \ - /* Validate all octets are within the 0-255 range */ \ - if (a <= 255 && b <= 255 && c <= 255 && d <= 255) { \ - /* Populate the structure with validated octets */ \ - (addr_struct).data[0] = (uint8_t)a; \ - (addr_struct).data[1] = (uint8_t)b; \ - (addr_struct).data[2] = (uint8_t)c; \ - (addr_struct).data[3] = (uint8_t)d; \ - } else { \ - /* Handle octet values outside valid range */ \ - error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 address!"); \ - } \ - } else { \ - /* Handle invalid string format (not matching xxx.xxx.xxx.xxx) */ \ - error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 format!"); \ - } \ -} while(0) - - - - -/** - * @brief Macro to convert an IPv4:port string to an IPv4PortTuple structure - * @details Parses a string in "xxx.xxx.xxx.xxx:port" format, splits it into IP address - * and port components, validates both parts, and populates the IPv4PortTuple. - * Port numbers must be in the range 0-65535. - * - * @param ip_port_str Input string in "xxx.xxx.xxx.xxx:port" format (e.g., "192.168.1.100:8080") - * @param tuple_struct Output IPv4PortTuple structure to be populated with parsed values - */ -#define IPV4_PORT_STR_TO_TUPLE(ip_port_str, tuple_struct) do { \ - char ip_str[16]; /* Buffer to store the IP address part (max IPv4 string length is 15) */ \ - unsigned int port; \ - \ - /* Parse the IP address and port from the input string */ \ - if (sscanf(ip_port_str, "%15[^:]:%u", ip_str, &port) == 2) { \ - \ - /* Convert and validate the IP address part */ \ - IPV4_STR_TO_ADDR(ip_str, (tuple_struct).ipv4_addr); \ - \ - /* Validate the port number (0-65535 range) */ \ - if (port > 65535) { \ - error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid port number: must be 0-65535)!"); \ - } else { \ - (tuple_struct).port = (uint16_t)port; \ - } \ - } else { \ - error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid IP:port format!"); \ - } \ -} while(0) - - - int frontend_eng_command(struct FrontendEngine_ *eng, GeneralProxyMsgHeader *hdr, uint8_t *data, uint32_t size); struct FrontendSession *frontend_sess_new(struct FrontendEngine_ *eng); diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index 1c81cf1..0fd559a 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -413,6 +413,71 @@ void default_session_event_callback(struct FrontendSession* sess, FrontendSessio int session_send(struct FrontendSession* sess, const uint8_t* data, uint32_t size); int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size); +/** + * @brief Macro to convert a dotted-decimal IPv4 string to an IPv4Address structure + * @details Parses a string in "xxx.xxx.xxx.xxx" format, validates each octet range (0-255), + * and populates the IPv4Address structure with the binary representation. + * Provides error messages to stderr for invalid formats or out-of-range values. + * + * @param ip_str Input string in dotted-decimal IPv4 format (e.g., "192.168.1.1") + * @param addr_struct Output IPv4Address structure to be populated with parsed values + */ +#define IPV4_STR_TO_ADDR(ip_str, addr_struct) do { \ + unsigned int a, b, c, d; /**< Temporary storage for parsed octet values */ \ + \ + /* Attempt to parse 4 octets from the input string */ \ + if (sscanf(ip_str, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) { \ + \ + /* Validate all octets are within the 0-255 range */ \ + if (a <= 255 && b <= 255 && c <= 255 && d <= 255) { \ + /* Populate the structure with validated octets */ \ + (addr_struct).data[0] = (uint8_t)a; \ + (addr_struct).data[1] = (uint8_t)b; \ + (addr_struct).data[2] = (uint8_t)c; \ + (addr_struct).data[3] = (uint8_t)d; \ + } else { \ + /* Handle octet values outside valid range */ \ + error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 address!"); \ + } \ + } else { \ + /* Handle invalid string format (not matching xxx.xxx.xxx.xxx) */ \ + error_print("IPV4_STR_TO_ADDR failed: Invalid IPv4 format!"); \ + } \ +} while(0) + + + +/** + * @brief Macro to convert an IPv4:port string to an IPv4PortTuple structure + * @details Parses a string in "xxx.xxx.xxx.xxx:port" format, splits it into IP address + * and port components, validates both parts, and populates the IPv4PortTuple. + * Port numbers must be in the range 0-65535. + * + * @param ip_port_str Input string in "xxx.xxx.xxx.xxx:port" format (e.g., "192.168.1.100:8080") + * @param tuple_struct Output IPv4PortTuple structure to be populated with parsed values + */ +#define IPV4_PORT_STR_TO_TUPLE(ip_port_str, tuple_struct) do { \ + char ip_str[16]; /* Buffer to store the IP address part (max IPv4 string length is 15) */ \ + unsigned int port; \ + \ + /* Parse the IP address and port from the input string */ \ + if (sscanf(ip_port_str, "%15[^:]:%u", ip_str, &port) == 2) { \ + \ + /* Convert and validate the IP address part */ \ + IPV4_STR_TO_ADDR(ip_str, (tuple_struct).ipv4_addr); \ + \ + /* Validate the port number (0-65535 range) */ \ + if (port > 65535) { \ + error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid port number: must be 0-65535)!"); \ + } else { \ + (tuple_struct).port = (uint16_t)port; \ + } \ + } else { \ + error_print("IPV4_PORT_STR_TO_TUPLE failed: invalid IP:port format!"); \ + } \ +} while(0) + + /** * @brief IoT Session related event types diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 3945e08..38b6951 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -1636,7 +1636,37 @@ int frontend_proxy_can_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, IotMsgHeader *iot_header, uint8_t *iot_data){ - return FRONTEND_PROXY_PROCESS_OK; + utils_print("In %s\n", __func__); + IoTFrontendSession *iot_sess; + IotCanAddr *can_addr; + uint8_t *iot_payload; + IotMsgBuffer iot_msg_buf; + int ret; + + if(NULL == iot_header || NULL == iot_data){ + error_print("frontend_proxy_can_msg_process failed: invalid parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(iot_header->payload_len <= sizeof(IotCanAddr)){ + error_print("frontend_proxy_can_msg_process failed: invalid iot_header->payload_len!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + iot_sess = frontend_can_sess; + (void)iot_sess; + + can_addr = (IotCanAddr *)iot_data; + iot_payload = iot_data + sizeof(IotCanAddr); + + iot_msg_buf.addr.addr_type = IOT_PROTO_TYPE_CAN; + iot_msg_buf.data = iot_payload; + iot_msg_buf.len = iot_header->payload_len - sizeof(sizeof(IotCanAddr)); + memcpy(&iot_msg_buf.addr.addr_info.can_addr, can_addr, sizeof(IotCanAddr)); + + ret = iot_sess->recv_from_backend(iot_sess, &iot_msg_buf, iot_payload, iot_header->payload_len - sizeof(sizeof(IotCanAddr))); + + return ret; } /** @@ -1752,7 +1782,40 @@ int frontend_proxy_modbustcp_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, IotMsgHeader *iot_header, uint8_t *iot_data){ - return FRONTEND_PROXY_PROCESS_OK; + utils_print("In %s\n", __func__); + IoTFrontendSession *iot_sess; + IotModbusTcpAddr *modbus_tcp_addr; + uint8_t *iot_payload; + IotMsgBuffer iot_msg_buf; + int ret; + + if(NULL == iot_header || NULL == iot_data){ + error_print("frontend_proxy_modbustcp_msg_process failed: invalid parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + if(iot_header->payload_len <= sizeof(IotModbusTcpAddr)){ + error_print("frontend_proxy_modbustcp_msg_process failed: invalid iot_header->payload_len!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + iot_sess = frontend_modbustcp_sess; + (void)iot_sess; + + modbus_tcp_addr = (IotModbusTcpAddr *)iot_data; + iot_payload = iot_data + sizeof(IotModbusTcpAddr); + + (void)modbus_tcp_addr; + (void)iot_payload; + + iot_msg_buf.addr.addr_type = IOT_PROTO_TYPE_MODBUSTCP; + iot_msg_buf.data = iot_payload; + iot_msg_buf.len = iot_header->payload_len - sizeof(sizeof(IotModbusTcpAddr)); + memcpy(&iot_msg_buf.addr.addr_info.modbus_tcp_addr, modbus_tcp_addr, sizeof(IotModbusTcpAddr)); + + ret = iot_sess->recv_from_backend(iot_sess, &iot_msg_buf, iot_payload, iot_header->payload_len - sizeof(sizeof(IotModbusTcpAddr))); + + return ret; } diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 4e11b8e..cb7325f 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -2,12 +2,12 @@ #include "session.h" #include "common_utils.h" -IoTFrontendSession *frontend_bluetooth_sess; -IoTFrontendSession *frontend_can_sess; -IoTFrontendSession *frontend_zigbee_sess; -IoTFrontendSession *frontend_lora_sess; -IoTFrontendSession *frontend_powerlink_sess; -IoTFrontendSession *frontend_modbustcp_sess; +IoTFrontendSession *frontend_bluetooth_sess = NULL; +IoTFrontendSession *frontend_can_sess = NULL; +IoTFrontendSession *frontend_zigbee_sess = NULL; +IoTFrontendSession *frontend_lora_sess = NULL; +IoTFrontendSession *frontend_powerlink_sess = NULL; +IoTFrontendSession *frontend_modbustcp_sess = NULL; /** * @brief Allocates and initializes a SessMsgSeg structure @@ -263,7 +263,7 @@ int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size) */ int bluetooth_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ utils_print("In %s\n", __func__); - GeneralProxyMsgHeader iot_proxy_msg_hdr; + GeneralProxyMsgHeader iot_proxy_msg_hdr; uint8_t *payload, **res_pointer; uint8_t *res_buf[100] = {NULL}; int ret; @@ -278,11 +278,10 @@ int bluetooth_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_ utils_print("Bluetooth datalen = %d\n", msg_buf->len); - iot_proxy_msg_hdr.outer_header.backend_sess_id = 0; - iot_proxy_msg_hdr.outer_header.frontend_sess_id = 0; - iot_proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_IOT; - iot_proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; - res_pointer = res_buf; + iot_proxy_msg_hdr.outer_header.backend_sess_id = 0; + iot_proxy_msg_hdr.outer_header.frontend_sess_id = 0; + iot_proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_IOT; + iot_proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; iot_proxy_msg_hdr.inner_header.iot_hdr.dev_port_id = 0; iot_proxy_msg_hdr.inner_header.iot_hdr.opcode = 0; @@ -325,7 +324,41 @@ int bluetooth_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int can_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ - return FRONTEND_PROXY_PROCESS_OK; + utils_print("In %s\n", __func__); + GeneralProxyMsgHeader iot_proxy_msg_hdr; + uint8_t *payload, **res_pointer; + uint8_t *res_buf[100] = {NULL}; + int ret; + + if(NULL == sess || NULL == msg_buf){ + error_print("can_send_to_backend failed: invalid parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + utils_print("CAN datalen = %d\n", msg_buf->len); + + iot_proxy_msg_hdr.outer_header.backend_sess_id = 0; + iot_proxy_msg_hdr.outer_header.frontend_sess_id = 0; + iot_proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_IOT; + iot_proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + + iot_proxy_msg_hdr.inner_header.iot_hdr.dev_port_id = 0; + iot_proxy_msg_hdr.inner_header.iot_hdr.opcode = 0; + iot_proxy_msg_hdr.inner_header.iot_hdr.proto_type = IOT_PROTO_TYPE_CAN; + iot_proxy_msg_hdr.inner_header.iot_hdr.proto_ver = 0; + iot_proxy_msg_hdr.inner_header.iot_hdr.payload_len = msg_buf->len + get_iot_addr_length(IOT_PROTO_TYPE_CAN); + iot_proxy_msg_hdr.inner_header.iot_hdr.reserve = 0; + + iot_proxy_msg_hdr.iot_addr_len = get_iot_addr_length(IOT_PROTO_TYPE_CAN); + iot_proxy_msg_hdr.iot_addr.addr_type = IOT_PROTO_TYPE_CAN; + iot_proxy_msg_hdr.iot_addr.addr_info.can_addr.bus_id = 0; + iot_proxy_msg_hdr.iot_addr.addr_info.can_addr.can_id = 0; + iot_proxy_msg_hdr.iot_addr.addr_info.can_addr.port = 0; + + res_pointer = res_buf; + ret = build_proxy_general_message(sess->eng, &iot_proxy_msg_hdr, msg_buf->data, msg_buf->len, res_pointer, MEMORY_ALLOC_AMPQUEUE, NULL); + + return ret; } @@ -338,6 +371,8 @@ int can_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int can_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + sess->event_callback(sess, IOT_SESS_EVENT_DATA_RECEIVED, msg_buf); + return FRONTEND_PROXY_PROCESS_OK; } @@ -421,7 +456,47 @@ int powerlink_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int modbustcp_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ - return FRONTEND_PROXY_PROCESS_OK; + GeneralProxyMsgHeader iot_proxy_msg_hdr; + uint8_t *payload, **res_pointer; + uint8_t *res_buf[100] = {NULL}; + IPv4PortTuple ipv4_port_tuple; + char ip_port_str[] = "192.168.1.100:500"; + int ret; + + if(NULL == sess || NULL == msg_buf){ + error_print("modbustcp_send_to_backend failed: invalid parameters!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + IPV4_PORT_STR_TO_TUPLE(ip_port_str, ipv4_port_tuple); + + utils_print("ModbusTcp datalen = %d\n", msg_buf->len); + + iot_proxy_msg_hdr.outer_header.backend_sess_id = 0; + iot_proxy_msg_hdr.outer_header.frontend_sess_id = 0; + iot_proxy_msg_hdr.outer_header.proxy_msg_type = PROXY_MSG_TYPE_IOT; + iot_proxy_msg_hdr.outer_header.version = PROXY_PROTO_VERSION_1; + res_pointer = res_buf; + + iot_proxy_msg_hdr.inner_header.iot_hdr.dev_port_id = 0; + iot_proxy_msg_hdr.inner_header.iot_hdr.opcode = 0; + iot_proxy_msg_hdr.inner_header.iot_hdr.proto_type = IOT_PROTO_TYPE_MODBUSTCP; + iot_proxy_msg_hdr.inner_header.iot_hdr.proto_ver = 0; + iot_proxy_msg_hdr.inner_header.iot_hdr.payload_len = msg_buf->len + get_iot_addr_length(IOT_PROTO_TYPE_MODBUSTCP); + iot_proxy_msg_hdr.inner_header.iot_hdr.reserve = 0; + + iot_proxy_msg_hdr.iot_addr_len = get_iot_addr_length(IOT_PROTO_TYPE_MODBUSTCP); + iot_proxy_msg_hdr.iot_addr.addr_type = IOT_PROTO_TYPE_MODBUSTCP; + iot_proxy_msg_hdr.iot_addr.addr_info.modbus_tcp_addr.reg_addr = 0; + iot_proxy_msg_hdr.iot_addr.addr_info.modbus_tcp_addr.reg_num = 1; + iot_proxy_msg_hdr.iot_addr.addr_info.modbus_tcp_addr.unit_id = 0; + iot_proxy_msg_hdr.iot_addr.addr_info.modbus_tcp_addr.port = ipv4_port_tuple.port; + memcpy(iot_proxy_msg_hdr.iot_addr.addr_info.modbus_tcp_addr.ip, ipv4_port_tuple.ipv4_addr.data, sizeof(ipv4_port_tuple.ipv4_addr.data)); + + res_pointer = res_buf; + ret = build_proxy_general_message(sess->eng, &iot_proxy_msg_hdr, msg_buf->data, msg_buf->len, res_pointer, MEMORY_ALLOC_AMPQUEUE, NULL); + + return ret; } @@ -434,6 +509,7 @@ int modbustcp_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_ * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int modbustcp_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + sess->event_callback(sess, IOT_SESS_EVENT_DATA_RECEIVED, msg_buf); return FRONTEND_PROXY_PROCESS_OK; } -- Gitee From 6919cb038b55d229ea44348174fc2c8f05bd9239 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 23 Mar 2026 09:19:10 +0800 Subject: [PATCH 111/133] add log point --- projects/sel4test/apps/front/src/session.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index cb7325f..7bd6d8a 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -1,5 +1,6 @@ #include "session.h" +#include "frontend_proto.h" #include "common_utils.h" IoTFrontendSession *frontend_bluetooth_sess = NULL; @@ -311,6 +312,7 @@ int bluetooth_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_ * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int bluetooth_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + utils_print("In %s\n", __func__); sess->event_callback(sess, IOT_SESS_EVENT_DATA_RECEIVED, msg_buf); return FRONTEND_PROXY_PROCESS_OK; -- Gitee From 034ff4dfb669285e3708eef57d45afded6171d7e Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 23 Mar 2026 14:38:53 +0800 Subject: [PATCH 112/133] add debug information --- projects/sel4test/apps/front/src/frontend_proto.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 38b6951..32f9551 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -718,14 +718,17 @@ int frontend_proxy_msg_process(uint8_t *msg){ error_print("Both the frontend session ID and backend session ID in the proxy data message must pass the application session ID validation!"); return FRONTEND_PROXY_PROCESS_ERROR; } + printf("In %s-4\n", __func__); ret = frontend_proxy_data_msg_prosess(frontend_sess_id, backend_sess_id, msg_len, msg_ptr); }else if(PROXY_MSG_TYPE_IOT == msg_type){ /* * process IoT message. */ + printf("In %s-5\n", __func__); ret = frontend_proxy_iot_msg_process(frontend_sess_id, backend_sess_id, msg_len, msg_ptr); }else{ error_print("frontend_proxy_msg_process failed: unsupport message type!\n"); + utils_print("msg_type = %d\n", msg_type); return FRONTEND_PROXY_PROCESS_ERROR; } -- Gitee From 4403fc6e1ac723142754a3d8db9507d0da10094b Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 23 Mar 2026 14:40:49 +0800 Subject: [PATCH 113/133] fix a bug --- projects/sel4test/apps/front/include/frontend_proto.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/include/frontend_proto.h b/projects/sel4test/apps/front/include/frontend_proto.h index c2683d8..e3e8011 100644 --- a/projects/sel4test/apps/front/include/frontend_proto.h +++ b/projects/sel4test/apps/front/include/frontend_proto.h @@ -10,7 +10,8 @@ #define PROXY_MSG_TYPE_VALID(x) (((x) == PROXY_MSG_TYPE_DEV) || \ ((x) == PROXY_MSG_TYPE_STRGY) || \ ((x) == PROXY_MSG_TYPE_SESS) || \ - ((x) == PROXY_MSG_TYPE_DATA)) + ((x) == PROXY_MSG_TYPE_DATA) || \ + ((x) == PROXY_MSG_TYPE_IOT)) #define PROXY_MSG_LEN_VALID(x) (((x) >= PROXY_MSG_MIN_SIZE) || \ ((x) <= PROXY_MSG_MAX_SIZE)) -- Gitee From 474831fd6b191db55a05e66810ba16f6bf670330 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 23 Mar 2026 14:59:45 +0800 Subject: [PATCH 114/133] bluetooth debug --- projects/sel4test/apps/front/src/session.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 7bd6d8a..908034d 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -532,7 +532,9 @@ void default_bluetooth_event_callback(struct IoTFrontendSession_ *sess, IoTSessi utils_print("In %s\n", __func__); if(IOT_SESS_EVENT_DATA_RECEIVED == event){ - + utils_print("IOT_SESS_EVENT_DATA_RECEIVED happens!\n"); + utils_print("bluetooth message length is %d ,content is %s\n", msg_buf->len, msg_buf->data); + frontend_iot_sess_send(sess, "test bluetooth", strlen("test bluetooth")); } } -- Gitee From 01ee02827d8839871e9585b3b148802652867268 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 23 Mar 2026 15:05:59 +0800 Subject: [PATCH 115/133] add bluetooth debug codes --- projects/sel4test/apps/front/src/frontend_proto.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 32f9551..96e702e 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -1588,6 +1588,8 @@ int frontend_proxy_bluetooth_msg_process(uint16_t frontend_sess_id, return FRONTEND_PROXY_PROCESS_ERROR; } + utils_print("iot_header->payload_len = %d\n", iot_header->payload_len); + if(iot_header->payload_len <= sizeof(IotBtAddr)){ error_print("frontend_proxy_bluetooth_msg_process failed: invalid iot_header->payload_len!\n"); return FRONTEND_PROXY_PROCESS_ERROR; -- Gitee From 0b7cdccfe41eee4a866a5c985cf0dc98c272b3bb Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 23 Mar 2026 17:28:03 +0800 Subject: [PATCH 116/133] add debug info --- projects/sel4test/apps/front/src/common_utils.c | 2 +- projects/sel4test/apps/front/src/frontend_proto.c | 3 ++- projects/sel4test/apps/front/src/hyperamp_shm_queue.c | 8 +++++--- projects/sel4test/apps/front/src/session.c | 2 ++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/projects/sel4test/apps/front/src/common_utils.c b/projects/sel4test/apps/front/src/common_utils.c index dd4ce45..dc90788 100644 --- a/projects/sel4test/apps/front/src/common_utils.c +++ b/projects/sel4test/apps/front/src/common_utils.c @@ -30,7 +30,7 @@ int debug_print(const char *format, ...){ */ int parse_proxy_protocol_and_print(const uint8_t *buffer) { if (!buffer) { - printf("[PROXY_PARSE_ERR] Input buffer is NULL.\n"); + printf("[PROXY_PARSE_ERR] buffer is NULL.\n"); return FRONTEND_PROXY_PROCESS_ERROR; } diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 96e702e..55015f1 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -1504,11 +1504,12 @@ int frontend_proxy_iot_msg_process(uint16_t frontend_sess_id, uint16_t backend_s ret = frontend_proxy_bluetooth_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); break; case IOT_PROTO_TYPE_CAN: + utils_print("CAN message length = %d\n", msg_len); if(msg_len < sizeof(IotMsgHeader)+ sizeof(IotCanAddr)){ error_print("frontend_proxy_iot_msg_process failed: invalid msg_len for CAN protocol!\n"); return FRONTEND_PROXY_PROCESS_ERROR; } - + utils_print("Before process CAN message\n"); ret = frontend_proxy_can_msg_process(frontend_sess_id, backend_sess_id, iot_msg_hdr, iot_data); break; case IOT_PROTO_TYPE_ZIGBEE: diff --git a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c index 3701154..abbc5a9 100644 --- a/projects/sel4test/apps/front/src/hyperamp_shm_queue.c +++ b/projects/sel4test/apps/front/src/hyperamp_shm_queue.c @@ -337,12 +337,14 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, // Read the data hyperamp_safe_memcpy(data, (const volatile void *)read_addr, read_len); + parse_proxy_protocol_and_print(data); + print_hex(data, read_len, 64); print_hex(read_addr, read_len, 64); #if 1 printf("In %s, before update the tail pointer and the dequeue count, the content of message header:\n", __func__); - DUMP_PROXY_MSG_HEADER(data); +// DUMP_PROXY_MSG_HEADER(data); #endif @@ -350,8 +352,8 @@ int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, *actual_len = read_len; } - printf("After set actual_len\n"); - DUMP_PROXY_MSG_HEADER(data); +// printf("After set actual_len\n"); +// DUMP_PROXY_MSG_HEADER(data); // Update tail (byte-by-byte write for atomicity) uint16_t new_tail = tail + 1; if (new_tail >= capacity) { diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 908034d..364bfad 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -373,6 +373,8 @@ int can_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int can_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + utils_print("In %s\n", __func__); + sess->event_callback(sess, IOT_SESS_EVENT_DATA_RECEIVED, msg_buf); return FRONTEND_PROXY_PROCESS_OK; -- Gitee From 8e3b5237a0aaa5b20f33bd7eac5108466f693b65 Mon Sep 17 00:00:00 2001 From: xj009 Date: Tue, 24 Mar 2026 10:41:26 +0800 Subject: [PATCH 117/133] add test codes --- .../sel4test/apps/front/src/frontend_proto.c | 36 +++++++++++++++++++ projects/sel4test/apps/front/src/main.c | 3 +- projects/sel4test/apps/front/src/session.c | 2 +- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index 55015f1..ebb4f23 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -1596,6 +1596,11 @@ int frontend_proxy_bluetooth_msg_process(uint16_t frontend_sess_id, return FRONTEND_PROXY_PROCESS_ERROR; } + if(NULL == frontend_bluetooth_sess){ + error_print("frontend_proxy_bluetooth_msg_process failed: bluetooth session does not initialize!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + iot_sess = frontend_bluetooth_sess; (void)iot_sess; @@ -1659,6 +1664,11 @@ int frontend_proxy_can_msg_process(uint16_t frontend_sess_id, return FRONTEND_PROXY_PROCESS_ERROR; } + if(NULL == frontend_can_sess){ + error_print("frontend_proxy_can_msg_process failed: CAN session does not initialize!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + iot_sess = frontend_can_sess; (void)iot_sess; @@ -1701,6 +1711,13 @@ int frontend_proxy_zigbee_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, IotMsgHeader *iot_header, uint8_t *iot_data){ + + + if(NULL == frontend_zigbee_sess){ + error_print("frontend_proxy_zigbee_msg_process failed: ZigBee session does not initialize!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + return FRONTEND_PROXY_PROCESS_OK; } @@ -1730,6 +1747,12 @@ int frontend_proxy_lora_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, IotMsgHeader *iot_header, uint8_t *iot_data){ + + if(NULL == frontend_lora_sess){ + error_print("frontend_proxy_lora_msg_process failed: LoRa session does not initialize!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + return FRONTEND_PROXY_PROCESS_OK; } @@ -1759,6 +1782,13 @@ int frontend_proxy_powerlink_msg_process(uint16_t frontend_sess_id, uint16_t backend_sess_id, IotMsgHeader *iot_header, uint8_t *iot_data){ + + if(NULL == frontend_powerlink_sess){ + error_print("frontend_proxy_powerlink_msg_process failed: PowerLink session does not initialize!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + + return FRONTEND_PROXY_PROCESS_OK; } @@ -1805,6 +1835,12 @@ int frontend_proxy_modbustcp_msg_process(uint16_t frontend_sess_id, return FRONTEND_PROXY_PROCESS_ERROR; } + + if(NULL == frontend_modbustcp_sess){ + error_print("frontend_proxy_modbustcp_msg_process failed: ModbusTcp session does not initialize!\n"); + return FRONTEND_PROXY_PROCESS_ERROR; + } + iot_sess = frontend_modbustcp_sess; (void)iot_sess; diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 3267ec4..275e2b3 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -91,7 +91,8 @@ int main(void){ ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.101:8888"); frontend_sess_bind_callback(sess, test_session_event_callback); - frontend_iot_sess_send(frontend_bluetooth_sess, "test bluetooth", strlen("test bluetooth")); +// frontend_iot_sess_send(frontend_bluetooth_sess, "test bluetooth", strlen("test bluetooth")); + frontend_iot_sess_send(frontend_can_sess, "test can", strlen("test can")); #endif diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 364bfad..25c6c54 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -555,7 +555,7 @@ void default_bluetooth_event_callback(struct IoTFrontendSession_ *sess, IoTSessi void default_can_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEvent event, IotMsgBuffer *msg_buf){ utils_print("In %s\n", __func__); if(IOT_SESS_EVENT_DATA_RECEIVED == event){ - + utils_print("IOT_SESS_EVENT_DATA_RECEIVED happens!\n"); } } -- Gitee From 425633dd99416e201f53c7acd5a7e48eba284cab Mon Sep 17 00:00:00 2001 From: xj009 Date: Tue, 24 Mar 2026 16:30:53 +0800 Subject: [PATCH 118/133] add test codes --- projects/sel4test/apps/front/src/common_utils.c | 5 +++-- projects/sel4test/apps/front/src/frontend_proto.c | 2 +- projects/sel4test/apps/front/src/main.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/projects/sel4test/apps/front/src/common_utils.c b/projects/sel4test/apps/front/src/common_utils.c index dc90788..40c0390 100644 --- a/projects/sel4test/apps/front/src/common_utils.c +++ b/projects/sel4test/apps/front/src/common_utils.c @@ -36,12 +36,13 @@ int parse_proxy_protocol_and_print(const uint8_t *buffer) { /* 1. Parse Proxy Header */ const ProxyMsgHeader *hdr = (const ProxyMsgHeader *)buffer; - + +#if 0 if (hdr->version != 1) { printf("[PROXY_PARSE_ERR] Invalid protocol version: %u (Expected 1).\n", hdr->version); return FRONTEND_PROXY_PROCESS_ERROR; } - +#endif const uint8_t *payload_start = buffer + sizeof(ProxyMsgHeader); printf("=== Proxy Packet Received ===\n"); diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index ebb4f23..ff0501e 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -282,7 +282,7 @@ int build_proxy_iot_message(GeneralProxyMsgHeader *header, // Proceeding anyway as the switch statement below will handle the actual size based on proto_type } - print_general_proxy_msg_header(header); +// print_general_proxy_msg_header(header); /* * Fill IoT header. */ diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 275e2b3..1e3fa2f 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -92,7 +92,7 @@ int main(void){ frontend_sess_bind_callback(sess, test_session_event_callback); // frontend_iot_sess_send(frontend_bluetooth_sess, "test bluetooth", strlen("test bluetooth")); - frontend_iot_sess_send(frontend_can_sess, "test can", strlen("test can")); +// frontend_iot_sess_send(frontend_can_sess, "test can", strlen("test can")); #endif -- Gitee From 5712912faa18b7a7ada0e4f6a28cc7230c785266 Mon Sep 17 00:00:00 2001 From: xj009 Date: Wed, 25 Mar 2026 10:28:16 +0800 Subject: [PATCH 119/133] add test code --- projects/sel4test/apps/front/src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 1e3fa2f..275e2b3 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -92,7 +92,7 @@ int main(void){ frontend_sess_bind_callback(sess, test_session_event_callback); // frontend_iot_sess_send(frontend_bluetooth_sess, "test bluetooth", strlen("test bluetooth")); -// frontend_iot_sess_send(frontend_can_sess, "test can", strlen("test can")); + frontend_iot_sess_send(frontend_can_sess, "test can", strlen("test can")); #endif -- Gitee From 08ccc081bd807df0f6f09170b290b583b5dd0012 Mon Sep 17 00:00:00 2001 From: xj009 Date: Wed, 25 Mar 2026 14:12:33 +0800 Subject: [PATCH 120/133] add test codes --- projects/sel4test/apps/front/src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index 275e2b3..cce3da0 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -92,7 +92,7 @@ int main(void){ frontend_sess_bind_callback(sess, test_session_event_callback); // frontend_iot_sess_send(frontend_bluetooth_sess, "test bluetooth", strlen("test bluetooth")); - frontend_iot_sess_send(frontend_can_sess, "test can", strlen("test can")); + frontend_iot_sess_send(frontend_can_sess, "Hello", strlen("Hello")); #endif -- Gitee From e7edfb723883ad369290c8605f29b1a929613a5a Mon Sep 17 00:00:00 2001 From: xj009 Date: Wed, 25 Mar 2026 14:39:33 +0800 Subject: [PATCH 121/133] add test codes --- projects/sel4test/apps/front/src/session.c | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 25c6c54..dd2848e 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -556,6 +556,7 @@ void default_can_event_callback(struct IoTFrontendSession_ *sess, IoTSessionEven utils_print("In %s\n", __func__); if(IOT_SESS_EVENT_DATA_RECEIVED == event){ utils_print("IOT_SESS_EVENT_DATA_RECEIVED happens!\n"); + frontend_iot_sess_send(sess, "Hello", strlen("Hello")); } } -- Gitee From d12f38c6de92fa699ef40374f303f655429f96d7 Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 27 Mar 2026 19:01:33 +0800 Subject: [PATCH 122/133] ModbusTCP support --- projects/sel4test/apps/front/include/message.h | 11 +++++++++++ projects/sel4test/apps/front/src/main.c | 7 ++++++- projects/sel4test/apps/front/src/session.c | 3 ++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/front/include/message.h b/projects/sel4test/apps/front/include/message.h index b6754d6..2ef9a49 100644 --- a/projects/sel4test/apps/front/include/message.h +++ b/projects/sel4test/apps/front/include/message.h @@ -690,6 +690,17 @@ typedef struct { } __attribute__((packed)) IotModbusTcpAddr; +typedef enum { + IOT_MODBUS_CMD_WRITE_REQ = 0, /**< Write request command */ + IOT_MODBUS_CMD_WRITE_RESP = 1, /**< Write response command */ + IOT_MODBUS_CMD_READ_REQ = 2, /**< Read request command */ + IOT_MODBUS_CMD_READ_RESP = 3 /**< Read response command */ +} IotModbusCmd; + +typedef struct { + uint8_t cmd; /* 0: WRITE-CMD, 1: WRITE-RESP; 2: READ-CMD, 3: READ_RESP */ + uint16_t value; /* value to be sent or receive */ +}__attribute__((packed)) IotModbusTcpMsg; /* -------------------------- Unified IoT Address Structure -------------------------- */ /** * @brief Unified IoT device address structure (type + union address) diff --git a/projects/sel4test/apps/front/src/main.c b/projects/sel4test/apps/front/src/main.c index cce3da0..9c6d591 100644 --- a/projects/sel4test/apps/front/src/main.c +++ b/projects/sel4test/apps/front/src/main.c @@ -77,10 +77,14 @@ int main(void){ FrontendEngine *eng; struct FrontendSession *sess; ProxyMsgHeader *proxy_msg_hdr; + IotModbusTcpMsg modbus_tcp_msg; int ret; size_t block_size; uint8_t msg_buf[HYPERAMP_MSG_HDR_PLUS_MAX_SIZE]; + memset(&modbus_tcp_msg, 0, sizeof(modbus_tcp_msg)); + modbus_tcp_msg.cmd = IOT_MODBUS_CMD_READ_REQ;// Read reg command + frontend_engine_init(); eng = frontend_get_global_engine(); @@ -91,8 +95,9 @@ int main(void){ ret = frontend_sess_connect_by_addrstr(sess, SESS_UDP_PROTO, "192.168.1.101:8888"); frontend_sess_bind_callback(sess, test_session_event_callback); -// frontend_iot_sess_send(frontend_bluetooth_sess, "test bluetooth", strlen("test bluetooth")); + frontend_iot_sess_send(frontend_bluetooth_sess, "test bluetooth", strlen("test bluetooth")); frontend_iot_sess_send(frontend_can_sess, "Hello", strlen("Hello")); + frontend_iot_sess_send(frontend_modbustcp_sess, &modbus_tcp_msg, sizeof(modbus_tcp_msg)); #endif diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index dd2848e..7c81546 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -460,11 +460,12 @@ int powerlink_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int modbustcp_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_buf){ + utils_print("In %s\n", __func__); GeneralProxyMsgHeader iot_proxy_msg_hdr; uint8_t *payload, **res_pointer; uint8_t *res_buf[100] = {NULL}; IPv4PortTuple ipv4_port_tuple; - char ip_port_str[] = "192.168.1.100:500"; + char ip_port_str[] = "192.168.1.101:502"; int ret; if(NULL == sess || NULL == msg_buf){ -- Gitee From c886df4f9abe380acd2c7229f161a598da461ed2 Mon Sep 17 00:00:00 2001 From: xj009 Date: Fri, 27 Mar 2026 20:17:04 +0800 Subject: [PATCH 123/133] Modbus TCP functions --- projects/sel4test/apps/front/src/frontend_proto.c | 2 ++ projects/sel4test/apps/front/src/session.c | 1 + 2 files changed, 3 insertions(+) diff --git a/projects/sel4test/apps/front/src/frontend_proto.c b/projects/sel4test/apps/front/src/frontend_proto.c index ff0501e..5a69a03 100644 --- a/projects/sel4test/apps/front/src/frontend_proto.c +++ b/projects/sel4test/apps/front/src/frontend_proto.c @@ -1830,6 +1830,8 @@ int frontend_proxy_modbustcp_msg_process(uint16_t frontend_sess_id, return FRONTEND_PROXY_PROCESS_ERROR; } + utils_print("iot_header->payload_len = %d\n", iot_header->payload_len); + if(iot_header->payload_len <= sizeof(IotModbusTcpAddr)){ error_print("frontend_proxy_modbustcp_msg_process failed: invalid iot_header->payload_len!\n"); return FRONTEND_PROXY_PROCESS_ERROR; diff --git a/projects/sel4test/apps/front/src/session.c b/projects/sel4test/apps/front/src/session.c index 7c81546..ae851c0 100644 --- a/projects/sel4test/apps/front/src/session.c +++ b/projects/sel4test/apps/front/src/session.c @@ -514,6 +514,7 @@ int modbustcp_send_to_backend(IoTFrontendSession *sess, const IotMsgBuffer *msg_ * @return int FRONTEND_PROXY_PROCESS_OK on success, FRONTEND_PROXY_PROCESS_ERROR on failure */ int modbustcp_recv_from_backend(IoTFrontendSession *sess, IotMsgBuffer *msg_buf, uint8_t *data, uint16_t data_len){ + utils_print("%s\n"); sess->event_callback(sess, IOT_SESS_EVENT_DATA_RECEIVED, msg_buf); return FRONTEND_PROXY_PROCESS_OK; } -- Gitee From 1d5c1b0835e468311f2009b0577d8fb35eadba47 Mon Sep 17 00:00:00 2001 From: xj009 Date: Mon, 13 Apr 2026 18:11:22 +0800 Subject: [PATCH 124/133] Add session callback & engine run once for HyperAMP frontend --- .../sel4test/apps/front/include/session.h | 1 + projects/sel4test/apps/front/src/engine.c | 196 ++++++++++++++++++ .../sel4test/apps/front/src/session_pool.c | 38 ++++ 3 files changed, 235 insertions(+) diff --git a/projects/sel4test/apps/front/include/session.h b/projects/sel4test/apps/front/include/session.h index 0fd559a..b9ee5e0 100644 --- a/projects/sel4test/apps/front/include/session.h +++ b/projects/sel4test/apps/front/include/session.h @@ -410,6 +410,7 @@ TAILQ_HEAD(FrontendSessionIDQueue, FrontendSessionID); void default_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event); +void placeholder_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event); int session_send(struct FrontendSession* sess, const uint8_t* data, uint32_t size); int session_recv(struct FrontendSession* sess, uint8_t* data, uint32_t size); diff --git a/projects/sel4test/apps/front/src/engine.c b/projects/sel4test/apps/front/src/engine.c index 490c47b..0aaad44 100644 --- a/projects/sel4test/apps/front/src/engine.c +++ b/projects/sel4test/apps/front/src/engine.c @@ -1049,6 +1049,202 @@ eng_run_step3: } +/** + * @brief Single-execution function of the engine (HyperAMP version), handling message processing and data transmission once + * This function executes the core logic of the frontend engine once (instead of a continuous loop), which is consistent with the logic of frontend_engine_run_hyperamp, + * with the key difference being that it only completes one round of processing and then returns, rather than looping continuously. All inter-environment communication + * is still performed via the HyperAMP queue to interact with the backend running on Linux. + * @details The single execution process is as follows: + * + * 1. Read data from the RX queue of the HyperAMP queue owned by the FrontendEngine instance, and process + * them sequentially through the frontend proxy protocol stack. + * If any data is read, there are two cases: + * (a) For device messages, strategy messages, and session messages (these are responses from the Linux backend): + * The frontend proxy protocol stack performs corresponding processing for each type, and updates the frontend's + * relevant state based on the message content (the frontend does not generate response packets). + * (b) For sessions that receive data messages: + * These sessions are placed into the backend-to-frontend active queue for processing in step (2). + * If no data message is found, the procedure jumps to step (3). + * + * 2. Access and process session instances in the backend-to-frontend active queue sequentially: + * Sequentially call the application callback function registered by each session in the queue (serving as a data-ready notification). + * After receiving the callback notification, the application will call the dedicated read API to read data from the corresponding session, + * and complete business processing independently based on the read data (the frontend does not involve active data extraction or socket transmission here). + * If the application needs to send data to the Linux backend proxy through the HyperAMP queue, when the data to be sent is added to the send buffer, + * the session will be added to the frontend-to-backend active queue, and these sessions will be processed in STEP (3). + * + * 3. Access and process session instances in the frontend-to-backend active queue sequentially: + * For each session in the queue, read the pending data stored in the session's send buffer (data to be sent to the Linux backend proxy by the application). + * Construct data messages according to the frontend proxy protocol specification, and send the messages to the Linux backend proxy through the HyperAMP's + * TX queue. + * After the data is successfully sent, clear the corresponding pending data in the session's send buffer; if the send buffer becomes empty, remove the session + * from the frontend-to-backend active queue. (This step specifically handles data transmission requests initiated by the application, realizing the frontend- + * -to-backend data proxy through the HyperAMP queue) + * + * After completing all operations in step (3), the function returns directly without looping back to step (1), completing a single round of engine processing. + * @note 1. This function is a single-execution version of frontend_engine_run_hyperamp, suitable for scenarios where periodic or on-demand single processing is required; + * 2. The processing logic of each step is completely consistent with frontend_engine_run_hyperamp, only the cyclic execution is removed; + * 3. All inter-environment communication still relies on the HyperAMP queue, maintaining compatibility with the Linux backend. + */ +void frontend_engine_run_hyperamp_once(){ + FrontendEngine *eng; + volatile HyperampShmQueue *hyper_rx_queue, *hyper_tx_queue; + struct FrontendSessionQueue *active_queue_f2b, *active_queue_b2f; + struct FrontendSession *cur_sess, *next_sess; + struct FrontendSessionPool *sess_pool; + struct FrontendSessionPoolOps *sess_pool_ops; + uint8_t *proxy_msg; + uint32_t msg_size; + int ret; + size_t block_size; + uint8_t msg_buf[HYPERAMP_MSG_HDR_PLUS_MAX_SIZE]; + + eng = frontend_get_global_engine(); + +/* + * hyper_rx_queue: HyperAMP receive queue instance for cross-OS shared memory communication. + * This local receive queue maps to the front-end's HyperAMP transmit queue (hyper_tx_queue). + * Data sent by the front-end through its hyper_tx_queue is received locally via this hyper_rx_queue. + * + * hyper_tx_queue: HyperAMP transmit queue instance for cross-OS shared memory communication. + * This local transmit queue serves as the front-end's HyperAMP receive queue (hyper_rx_queue). + * Data sent locally through this hyper_tx_queue is received by the front-end via its hyper_rx_queue. + * + * hyper_amp_data_region: The memory region where cross-OS shared memory data is stored, + * which is the underlying storage for data transmitted via HyperAMP queues. + */ + + if(NULL == eng->hyper_rx_queue || NULL == eng->hyper_tx_queue){ + error_print("frontend_engine_run_hyperamp_once failed: The global backend engine's RX queue or TX queue has not been initialized!"); + return ; + } + + FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); + FRONTEND_ENGINE_GET_B2F_QUEUE(eng, active_queue_b2f); + + if(NULL == active_queue_f2b || NULL == active_queue_b2f){ + error_print("frontend_engine_run_hyperamp_once failed: The global frontend engine's f2b session queue or b2f session queue has not been initialized!"); + return ; + } + + if(NULL == eng->sess_pool || NULL == eng->sess_pool->ops){ + error_print("frontend_engine_run_hyperamp_once failed: Global frontend engine's session pool (sess_pool) or its operation set (ops) is not initialized!"); + return ; + } + + sess_pool = eng->sess_pool; + sess_pool_ops = sess_pool->ops; + +/* + * Remove the do-while(1) loop of the original function, execute only one round of the three-step processing flow, and return directly after completion + * STEP (1): Read data from HyperAMP RX queue and process it + */ +eng_run_step1: + do{ + ps_sdelay(1); + printf("In %s-1\n", __func__); + ret = frontend_engine_hyperamp_rx_queue_get(eng, HYPERAMP_MSG_HDR_PLUS_MAX_SIZE, msg_buf, &block_size); + + /* + * If returning FRONTEND_PROXY_PROCESS_ERROR, it indicates a system-level error (e.g., invalid queue handle, shared memory access exception, etc.) + * Processing cannot continue; print error message and return directly. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + error_print("frontend_engine_run_hyperamp_once failed: failed to get data from RX queue!"); + return; + } + + /* + * If returning FRONTEND_PROXY_PROCESS_AGAIN, it indicates temporary inability to retrieve data (e.g., empty queue, resource temporarily occupied, etc., non-error state) + * No error reporting needed; jump to eng_run_step2 to execute the next process. + */ + printf("In %s-2\n", __func__); + if(FRONTEND_PROXY_PROCESS_AGAIN == ret){ + goto eng_run_step2; + } + printf("In %s-3\n", __func__); + /* + * Process the proxy message. + */ + frontend_proxy_msg_process(msg_buf); + printf("In %s-4\n", __func__); + }while(FRONTEND_PROXY_PROCESS_OK == ret); + +eng_run_step2: +/* + * Recall the FRONTEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (1) procedure may renew the back-to-front queue (queue_b2f) of the session pool. + */ + printf("In %s-5\n", __func__); + FRONTEND_ENGINE_GET_B2F_QUEUE(eng, active_queue_b2f); + printf("In %s-6\n", __func__); + TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess){ + printf("In %s-7\n", __func__); + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_RECVDATA); + +/* + * queue_b2f: Session queue for sessions with pending data to be received by the frontend from the backend. + * (Pending data refers to data sent from the backend to the frontend, which the frontend needs to read.) + * If the receive queue (msg_b2f) is empty, the session instance should be removed from the queue_b2f list. + */ + if(TAILQ_EMPTY(&cur_sess->msg_b2f)){ + TAILQ_REMOVE(active_queue_b2f, cur_sess, entries_b2f); + cur_sess->state_b2f &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } + printf("In %s-8\n", __func__); + } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_b2f, entries_b2f, next_sess) + +eng_run_step3: +/* + * Recall the FRONTEND_ENGINE_GET_F2B_QUEUE again to update active_queue_b2f, because the STEP (2) procedure may renew the front-to-back queue (queue_f2b) of the session pool. + */ + printf("In %s-9\n", __func__); + FRONTEND_ENGINE_GET_F2B_QUEUE(eng, active_queue_f2b); + printf("In %s-10\n", __func__); + TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess){ + +/* + * Call the corresponding data_process_f2b function pointer in the session pool's operation set (sess_pool_ops), which attempts to send the frontend-to-backend data maintained by the + * current session (cur_sess) via the shared memory's TX queue. + * The return value corresponds to three scenarios: + * Returns FRONTEND_PROXY_PROCESS_OK: All data has been sent successfully. + * Returns FRONTEND_PROXY_PROCESS_AGAIN: Not all data has been sent, and no errors occurred. + * Returns FRONTEND_PROXY_PROCESS_ERROR: An error occurred during the sending process. + */ + printf("In %s-11\n", __func__); + ret = sess_pool_ops->data_process_f2b(cur_sess); +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_OK, this indicates all message segments in the front-to-back (F2B) message queue have been sent via the shared memory's TX queue. + * Such sessions should be detached from the F2B active queue, and their "linked to queue" state flag should be cleared. + */ + printf("In %s-12\n", __func__); + if(FRONTEND_PROXY_PROCESS_OK == ret){ + printf("In %s-13\n", __func__); + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->state_f2b &= ~FRONTEND_SESS_LINKED_TO_QUEUE; + } +/* + * If data_process_f2b returns FRONTEND_PROXY_PROCESS_ERROR, it indicates an error occurred when attempting to send data via the shared memory's TX queue. + * For such sessions, they should be removed from the frontend-to-backend active queue (active_queue_f2b), and the session's abnormal event callback should be triggered. + * + * Note that the recovery of resources occupied by the session needs to be completed during the processing of the FRONTEND_SESS_EVENT_ABNORMAL event, and this responsibility + * lies with the application. + */ + if(FRONTEND_PROXY_PROCESS_ERROR == ret){ + printf("In %s-14\n", __func__); + TAILQ_REMOVE(active_queue_f2b, cur_sess, entries_f2b); + cur_sess->event_callback(cur_sess, FRONTEND_SESS_EVENT_ABNORMAL); + } + printf("In %s-15\n", __func__); +/* + * When not all data has been sent and no errors have occurred, no action is required. + */ + } // TAILQ_FOREACH_SAFE(cur_sess, active_queue_f2b, entries_f2b, next_sess) + + // Return directly after completing all operations in the three steps without looping + return; +} + + /** * @brief Initialize IoT devices. * @param engine Pointer to FrontendEngine instance. diff --git a/projects/sel4test/apps/front/src/session_pool.c b/projects/sel4test/apps/front/src/session_pool.c index f8e130a..d3db846 100644 --- a/projects/sel4test/apps/front/src/session_pool.c +++ b/projects/sel4test/apps/front/src/session_pool.c @@ -869,4 +869,42 @@ void default_session_event_callback(struct FrontendSession* sess, FrontendSessio } return; +} + + +/** +* @brief Placeholder event callback function for frontend sessions + * +* This is a placeholder callback implementation for handling frontend session events, + * which is invoked by the session framework when specific events (defined in + * FrontendSessionEvent enumeration) occur during the lifecycle of a FrontendSession. +* It acts as a placeholder (does not process any events) when no custom callback is specified, with all event handling logic executed elsewhere. + * + * @param[in] sess Pointer to the FrontendSession instance that triggered the event; + * must be a valid non-NULL pointer pointing to an active session + * @param[in] event The specific event type that occurred, one of the values in + * FrontendSessionEvent enumeration (FRONTEND_SESS_EVENT_CONN, + * FRONTEND_SESS_EVENT_RECVDATA, FRONTEND_SESS_EVENT_TIMEOUT, FRONTEND_SESS_EVENT_CLOSE) + * + * @note 1. This callback is called automatically by the session management framework, + * and should not be invoked manually by the user; + * 2. As a placeholder implementation, this function performs no event processing operations (such as + * logging, resource handling, or triggering business flows)—all event handling logic is deferred to other modules; + * (e.g., initiating connection setup on FRONTEND_SESS_EVENT_CONN, processing received + * data on FRONTEND_SESS_EVENT_RECVDATA, or preparing for session closure on FRONTEND_EVENT_SESS_CLOSE); + * 3. Users can replace this placeholder callback with a custom implementation to + * achieve personalized event processing logic; + * 4. Ensure the 'sess' pointer is valid when the callback is triggered (the framework + * guarantees this under normal circumstances), invalid pointers may lead to undefined behavior; + * 5. The processing logic in this callback should be non-blocking to avoid blocking the + * session framework's event loop. + * @note 6. This implementation is a placeholder only, no event processing logic is performed here; + * all event handling logic is executed in other specified modules. + */ +void placeholder_session_event_callback(struct FrontendSession* sess, FrontendSessionEvent event) +{ + /* Placeholder callback function, does not process any events; all event handling logic is executed elsewhere */ + (void)sess; // Avoid unused parameter warning, comply with coding standards + (void)event; // Avoid unused parameter warning, comply with coding standards + return; // Return directly without performing any processing operations } \ No newline at end of file -- Gitee From 0bd01f95c93d0db7ad822d989d82f0322104f1fb Mon Sep 17 00:00:00 2001 From: gongtianyao Date: Sun, 19 Apr 2026 17:24:59 +0800 Subject: [PATCH 125/133] HyperAMP-powered network (by Claude Opus). --- .../apps/monkey-mnemosyne/CMakeLists.txt | 13 +- .../src/monkey/net/HyperAmpBridge.cc | 551 ++++++++++++++++++ .../src/monkey/net/HyperAmpBridge.h | 198 +++++++ .../src/monkey/net/Socket4.cc | 30 +- .../monkey-mnemosyne/src/monkey/net/TcpIo.cc | 32 +- 5 files changed, 791 insertions(+), 33 deletions(-) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt index e99e83a..ed7feae 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt +++ b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt @@ -17,6 +17,13 @@ file(GLOB_RECURSE deps src/*.c*) list(SORT deps) list(REMOVE_DUPLICATES deps) +# HyperAMP shared-memory queue sources from apps/front (needed by HyperAmpBridge). +set(FRONT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../front) +list(APPEND deps + ${FRONT_DIR}/src/hyperamp_shm_queue.c + ${FRONT_DIR}/src/common_utils.c +) + add_executable(monkey-mnemosyne EXCLUDE_FROM_ALL ${deps}) target_link_libraries(monkey-mnemosyne PUBLIC @@ -33,7 +40,11 @@ target_link_libraries(monkey-mnemosyne ) -include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${FRONT_DIR}/include # HyperAMP / proxy-protocol headers +) # GCC(g++) 编译选项。 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc new file mode 100644 index 0000000..f68521e --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc @@ -0,0 +1,551 @@ +/* + * HyperAMP Network Bridge – Implementation + * + * See HyperAmpBridge.h for the design rationale. + * + * Created 2025 – Monkey-Mnemosyne / seL4 HyperAMP integration + * + * gongty [at] tongji [dot] edu [dot] cn + */ + +#include +#include + +#include + +extern "C" { +#include +#include +#include +} + +namespace monkey::net { + + +/* ======================================================================== */ +/* Singleton */ +/* ======================================================================== */ + +HyperAmpBridge::HyperAmpBridge() { + std::memset(spillBuf_, 0, sizeof(spillBuf_)); +} + +HyperAmpBridge& HyperAmpBridge::instance() { + static HyperAmpBridge inst; + return inst; +} + + +/* ======================================================================== */ +/* Initialisation */ +/* ======================================================================== */ + +void HyperAmpBridge::init() { + if (initialized_) { + return; + } + + /* + * Read shared-memory virtual addresses from the IPC message registers. + * + * The seL4 boot loader (boot.c) writes the three addresses into + * msg[2..4]. seL4_GetMR(2) = TX queue, seL4_GetMR(3) = RX queue, + * seL4_GetMR(4) = data region. + * + * NOTE: No seL4 system calls may be issued before these reads – the + * message registers are volatile and any syscall would overwrite them. + * In practice monkey-mnemosyne calls bridge.init() early enough that + * this is not a problem; but apps/front also caches the values in global + * pointers for exactly the same reason. + * + * We use the compile-time virtual addresses from hyperamp_shm_queue.h + * as the primary source. They match what boot.c maps. + */ + txQueue_ = SHM_TX_QUEUE_VADDR; + rxQueue_ = SHM_RX_QUEUE_VADDR; + dataRegion_ = SHM_DATA_REGION_VA; + + MONKEY_LOG_INFO("[HyperAmpBridge] TX queue : ", (unsigned long)(uintptr_t)txQueue_); + MONKEY_LOG_INFO("[HyperAmpBridge] RX queue : ", (unsigned long)(uintptr_t)rxQueue_); + MONKEY_LOG_INFO("[HyperAmpBridge] Data rgn : ", (unsigned long)(uintptr_t)dataRegion_); + + /* + * Initialise the queues. We are the "creator" (is_creator = 1) because + * monkey-mnemosyne is the rootserver and runs first on seL4. + * + * Configuration mirrors apps/front/src/engine.c: + * capacity = 256 slots + * block_size = 4096 bytes + */ + HyperampQueueConfig txCfg; + std::memset(&txCfg, 0, sizeof(txCfg)); + txCfg.map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH; + txCfg.capacity = 256; + txCfg.block_size = 4096; + txCfg.phy_addr = SHM_TX_QUEUE_PADDR; + txCfg.virt_addr = reinterpret_cast(txQueue_); + + if (hyperamp_queue_init(txQueue_, &txCfg, 1) != HYPERAMP_OK) { + MONKEY_LOG_ERROR("[HyperAmpBridge] Failed to init TX queue"); + return; + } + + HyperampQueueConfig rxCfg; + std::memset(&rxCfg, 0, sizeof(rxCfg)); + rxCfg.map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH; + rxCfg.capacity = 256; + rxCfg.block_size = 4096; + rxCfg.phy_addr = SHM_RX_QUEUE_PADDR; + rxCfg.virt_addr = reinterpret_cast(rxQueue_); + + if (hyperamp_queue_init(rxQueue_, &rxCfg, 1) != HYPERAMP_OK) { + MONKEY_LOG_ERROR("[HyperAmpBridge] Failed to init RX queue"); + return; + } + + initialized_ = true; + MONKEY_LOG_INFO("[HyperAmpBridge] Initialised OK"); +} + + +/* ======================================================================== */ +/* Internal helpers */ +/* ======================================================================== */ + +adl::uint16_t HyperAmpBridge::nextFrontendSessId() { + adl::uint16_t id = sessIdCounter_++; + if (sessIdCounter_ == 0xFFFF) { // 0xFFFF is the handover sentinel + sessIdCounter_ = 1; + } + return id; +} + + +bool HyperAmpBridge::txEnqueue(const void* data, adl::size_t len) { + /* + * Retry loop: the queue may be momentarily full (HYPERAMP_AGAIN). + * Since performance is not a concern we simply spin. + */ + for (;;) { + int rc = hyperamp_queue_enqueue( + txQueue_, + HYPERAMP_ZONE_ID_SEL4, + data, + len, + dataRegion_ + ); + + if (rc == HYPERAMP_OK) { + return true; + } + if (rc == HYPERAMP_AGAIN) { + /* Queue full – spin. */ + continue; + } + /* Hard error. */ + MONKEY_LOG_ERROR("[HyperAmpBridge] txEnqueue error, rc=", rc); + return false; + } +} + + +bool HyperAmpBridge::rxDequeueBlocking(void* buf, adl::size_t* outLen) { + /* + * Busy-wait until HYPERAMP_OK. + */ + for (;;) { + /* + * Invalidate the cache covering the queue control block before + * every poll attempt so we see the latest writes from Linux. + */ + hyperamp_cache_invalidate(rxQueue_, 64); + + size_t actual = 0; + int rc = hyperamp_queue_dequeue( + rxQueue_, + HYPERAMP_ZONE_ID_SEL4, + buf, + kHyperAmpBlockSize, + &actual, + dataRegion_ + ); + + if (rc == HYPERAMP_OK) { + if (outLen) { + *outLen = actual; + } + return true; + } + if (rc == HYPERAMP_AGAIN) { + /* Queue empty – keep polling. */ + continue; + } + MONKEY_LOG_ERROR("[HyperAmpBridge] rxDequeueBlocking error, rc=", rc); + return false; + } +} + + +/* ======================================================================== */ +/* Session create (connect) */ +/* ======================================================================== */ + +bool HyperAmpBridge::connect(const IP4Addr& ip, adl::uint16_t port) { + if (!initialized_) { + MONKEY_LOG_ERROR("[HyperAmpBridge] connect() called before init()"); + return false; + } + + if (sessionActive_) { + MONKEY_LOG_WARN("[HyperAmpBridge] connect() – closing existing session first"); + close(); + } + + /* Reset spill buffer. */ + spillLen_ = 0; + spillOffset_ = 0; + + frontendSessId_ = nextFrontendSessId(); + + /* + * Build the session-create message. + * + * Wire format (big picture): + * [ HyperampMsgHeader (8B) ][ SessMsgHeader (10B) ][ SessIPv4Params (10B) ] + * + * Total = 28 bytes, well within a single 4096-byte queue slot. + */ + + /* --- SessMsgHeader --- */ + SessMsgHeader sessHdr; + std::memset(&sessHdr, 0, sizeof(sessHdr)); + sessHdr.version = PROXY_PROTO_SESS_VERSION_1; + sessHdr.msg_type = SESS_MSG_CREATE; + sessHdr.action_type = ACTION_TYPE_COMMAND; + sessHdr.ip_version = SESS_IPV4_PROTO; + sessHdr.payload_len = static_cast(sizeof(SessIPv4Params)); + + /* --- SessIPv4Params --- */ + SessIPv4Params sessParams; + std::memset(&sessParams, 0, sizeof(sessParams)); + sessParams.device_selection = 0xFF; // DEV_ID_AUTO_HANDOVER + sessParams.transport_layer_proto = SESS_TCP_PROTO; + sessParams.dest_endpoint.ipv4_addr.data[0] = ip.ui8arr[0]; + sessParams.dest_endpoint.ipv4_addr.data[1] = ip.ui8arr[1]; + sessParams.dest_endpoint.ipv4_addr.data[2] = ip.ui8arr[2]; + sessParams.dest_endpoint.ipv4_addr.data[3] = ip.ui8arr[3]; + sessParams.dest_endpoint.port = port; + + /* --- Assemble the full message --- */ + uint8_t msgBuf[kHyperAmpBlockSize]; + std::memset(msgBuf, 0, sizeof(msgBuf)); + + /* Outer HyperAMP header. */ + HyperampMsgHeader* outerHdr = reinterpret_cast(msgBuf); + outerHdr->version = PROXY_PROTO_VERSION_1; + outerHdr->proxy_msg_type = static_cast(HYPERAMP_MSG_TYPE_SESS); + outerHdr->frontend_sess_id = frontendSessId_; + outerHdr->backend_sess_id = 0xFFFF; // handover sentinel + outerHdr->payload_len = static_cast(sizeof(SessMsgHeader) + sizeof(SessIPv4Params)); + + /* Session sub-header + params follow the outer header. */ + std::memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE, + &sessHdr, sizeof(sessHdr)); + std::memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE + sizeof(SessMsgHeader), + &sessParams, sizeof(sessParams)); + + adl::size_t totalLen = HYPERAMP_MSG_HDR_SIZE + sizeof(SessMsgHeader) + sizeof(SessIPv4Params); + + MONKEY_LOG_INFO("[HyperAmpBridge] Sending SESS_CREATE fe_id=", frontendSessId_, + " ip=", ip.toString().c_str(), ":", port); + + if (!txEnqueue(msgBuf, totalLen)) { + MONKEY_LOG_ERROR("[HyperAmpBridge] connect() – txEnqueue failed"); + return false; + } + + /* + * Poll for the session-create response. + * + * The response is a HYPERAMP_MSG_TYPE_SESS message whose + * frontend_sess_id matches ours. The backend_sess_id in the + * response header gives us the server-side session handle. + * + * Payload is a SessOpRespData (2 bytes: status + code). + */ + uint8_t rxBuf[kHyperAmpBlockSize]; + for (;;) { + adl::size_t rxLen = 0; + if (!rxDequeueBlocking(rxBuf, &rxLen)) { + MONKEY_LOG_ERROR("[HyperAmpBridge] connect() – rxDequeue failed"); + return false; + } + + if (rxLen < HYPERAMP_MSG_HDR_SIZE) { + continue; // Malformed – skip. + } + + const HyperampMsgHeader* rspHdr = + reinterpret_cast(rxBuf); + + /* Is this our session response? */ + if (rspHdr->proxy_msg_type != static_cast(HYPERAMP_MSG_TYPE_SESS)) { + MONKEY_LOG_WARN("[HyperAmpBridge] connect() – ignoring non-SESS msg, type=", + (int)rspHdr->proxy_msg_type); + continue; + } + if (rspHdr->frontend_sess_id != frontendSessId_) { + MONKEY_LOG_WARN("[HyperAmpBridge] connect() – fe_id mismatch, got ", + rspHdr->frontend_sess_id, " want ", frontendSessId_); + continue; + } + + backendSessId_ = rspHdr->backend_sess_id; + + /* Check the embedded SessOpRespData if present. */ + adl::size_t payloadOff = HYPERAMP_MSG_HDR_SIZE + sizeof(SessMsgHeader); + if (rxLen >= payloadOff + sizeof(SessOpRespData)) { + const SessOpRespData* resp = + reinterpret_cast(rxBuf + payloadOff); + if (resp->status != SESS_OP_STATUS_SUCCESS) { + MONKEY_LOG_ERROR("[HyperAmpBridge] connect() – backend refused, status=", + (int)resp->status, " code=", (int)resp->code); + return false; + } + } + + sessionActive_ = true; + MONKEY_LOG_INFO("[HyperAmpBridge] Session established fe=", frontendSessId_, + " be=", backendSessId_); + return true; + } +} + + +/* ======================================================================== */ +/* Data send */ +/* ======================================================================== */ + +adl::int64_t HyperAmpBridge::send(const void* buf, adl::size_t len) { + if (!initialized_ || !sessionActive_) { + MONKEY_LOG_ERROR("[HyperAmpBridge] send() – no active session"); + return -1; + } + + const uint8_t* src = static_cast(buf); + adl::size_t remaining = len; + adl::size_t totalSent = 0; + + while (remaining > 0) { + adl::size_t chunk = remaining; + if (chunk > kHyperAmpMaxPayload) { + chunk = kHyperAmpMaxPayload; + } + + /* Build a HYPERAMP_MSG_TYPE_DATA message. */ + uint8_t msgBuf[kHyperAmpBlockSize]; + std::memset(msgBuf, 0, sizeof(msgBuf)); + + HyperampMsgHeader* hdr = reinterpret_cast(msgBuf); + hdr->version = PROXY_PROTO_VERSION_1; + hdr->proxy_msg_type = static_cast(HYPERAMP_MSG_TYPE_DATA); + hdr->frontend_sess_id = frontendSessId_; + hdr->backend_sess_id = backendSessId_; + hdr->payload_len = static_cast(chunk); + + std::memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE, src, chunk); + + if (!txEnqueue(msgBuf, HYPERAMP_MSG_HDR_SIZE + chunk)) { + MONKEY_LOG_ERROR("[HyperAmpBridge] send() – txEnqueue failed after ", + totalSent, " bytes"); + return (totalSent > 0) ? static_cast(totalSent) : -1; + } + + src += chunk; + remaining -= chunk; + totalSent += chunk; + } + + return static_cast(totalSent); +} + + +/* ======================================================================== */ +/* Data recv (blocking / polling) */ +/* ======================================================================== */ + +adl::int64_t HyperAmpBridge::recv(void* buf, adl::size_t len) { + if (!initialized_ || !sessionActive_) { + MONKEY_LOG_ERROR("[HyperAmpBridge] recv() – no active session"); + return -1; + } + + uint8_t* dst = static_cast(buf); + adl::size_t needed = len; + adl::size_t got = 0; + + /* 1. Drain any leftover bytes from the spill buffer first. */ + if (spillLen_ > spillOffset_) { + adl::size_t avail = spillLen_ - spillOffset_; + adl::size_t take = (avail <= needed) ? avail : needed; + std::memcpy(dst, spillBuf_ + spillOffset_, take); + spillOffset_ += take; + dst += take; + needed -= take; + got += take; + + if (spillOffset_ >= spillLen_) { + spillLen_ = 0; + spillOffset_ = 0; + } + } + + /* 2. Keep pulling messages until we have enough. */ + uint8_t rxBuf[kHyperAmpBlockSize]; + + while (needed > 0) { + adl::size_t rxLen = 0; + if (!rxDequeueBlocking(rxBuf, &rxLen)) { + MONKEY_LOG_ERROR("[HyperAmpBridge] recv() – rxDequeue failed"); + return (got > 0) ? static_cast(got) : -1; + } + + if (rxLen < HYPERAMP_MSG_HDR_SIZE) { + continue; + } + + const HyperampMsgHeader* hdr = + reinterpret_cast(rxBuf); + + /* Only accept DATA messages for our session. */ + if (hdr->proxy_msg_type != static_cast(HYPERAMP_MSG_TYPE_DATA)) { + MONKEY_LOG_WARN("[HyperAmpBridge] recv() – ignoring non-DATA msg, type=", + (int)hdr->proxy_msg_type); + continue; + } + + /* + * Accept if either session ID matches – the backend may use + * either the frontend or backend session ID. + */ + if (hdr->frontend_sess_id != frontendSessId_ && + hdr->backend_sess_id != backendSessId_) { + MONKEY_LOG_WARN("[HyperAmpBridge] recv() – session id mismatch"); + continue; + } + + adl::size_t payloadLen = hdr->payload_len; + const uint8_t* payload = rxBuf + HYPERAMP_MSG_HDR_SIZE; + + if (payloadLen == 0) { + continue; + } + + /* Copy as much as the caller still needs. */ + adl::size_t take = (payloadLen <= needed) ? payloadLen : needed; + std::memcpy(dst, payload, take); + dst += take; + needed -= take; + got += take; + + /* Stash surplus in the spill buffer. */ + if (take < payloadLen) { + adl::size_t surplus = payloadLen - take; + std::memcpy(spillBuf_, payload + take, surplus); + spillLen_ = surplus; + spillOffset_ = 0; + } + } + + return static_cast(got); +} + + +/* ======================================================================== */ +/* Session close */ +/* ======================================================================== */ + +void HyperAmpBridge::close() { + if (!initialized_ || !sessionActive_) { + return; + } + + MONKEY_LOG_INFO("[HyperAmpBridge] Closing session fe=", frontendSessId_, + " be=", backendSessId_); + + /* + * Build a SESS_MSG_CLOSE command. + * + * Wire format: + * [ HyperampMsgHeader (8B) ][ SessMsgHeader (10B) ] + * + * The CLOSE command has no payload beyond the session sub-header. + */ + SessMsgHeader sessHdr; + std::memset(&sessHdr, 0, sizeof(sessHdr)); + sessHdr.version = PROXY_PROTO_SESS_VERSION_1; + sessHdr.msg_type = SESS_MSG_CLOSE; + sessHdr.action_type = ACTION_TYPE_COMMAND; + sessHdr.ip_version = SESS_IPV4_PROTO; + sessHdr.payload_len = 0; + + uint8_t msgBuf[kHyperAmpBlockSize]; + std::memset(msgBuf, 0, sizeof(msgBuf)); + + HyperampMsgHeader* outerHdr = reinterpret_cast(msgBuf); + outerHdr->version = PROXY_PROTO_VERSION_1; + outerHdr->proxy_msg_type = static_cast(HYPERAMP_MSG_TYPE_SESS); + outerHdr->frontend_sess_id = frontendSessId_; + outerHdr->backend_sess_id = backendSessId_; + outerHdr->payload_len = static_cast(sizeof(SessMsgHeader)); + + std::memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE, &sessHdr, sizeof(sessHdr)); + + adl::size_t totalLen = HYPERAMP_MSG_HDR_SIZE + sizeof(SessMsgHeader); + txEnqueue(msgBuf, totalLen); + + /* + * Wait for the close response. We give it a bounded attempt count so + * that a non-responsive backend doesn't hang the system forever. + */ + uint8_t rxBuf[kHyperAmpBlockSize]; + static constexpr int kMaxCloseAttempts = 50000; + + for (int attempt = 0; attempt < kMaxCloseAttempts; ++attempt) { + hyperamp_cache_invalidate(rxQueue_, 64); + + size_t actual = 0; + int rc = hyperamp_queue_dequeue( + rxQueue_, HYPERAMP_ZONE_ID_SEL4, + rxBuf, kHyperAmpBlockSize, &actual, dataRegion_ + ); + + if (rc == HYPERAMP_AGAIN) { + continue; + } + if (rc != HYPERAMP_OK) { + break; + } + if (actual < HYPERAMP_MSG_HDR_SIZE) { + continue; + } + + const HyperampMsgHeader* rsp = + reinterpret_cast(rxBuf); + + if (rsp->proxy_msg_type == static_cast(HYPERAMP_MSG_TYPE_SESS) && + rsp->frontend_sess_id == frontendSessId_) { + /* Got the close ack. */ + break; + } + } + + sessionActive_ = false; + spillLen_ = 0; + spillOffset_ = 0; + + MONKEY_LOG_INFO("[HyperAmpBridge] Session closed"); +} + + +} // namespace monkey::net diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h new file mode 100644 index 0000000..1b6b2e8 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h @@ -0,0 +1,198 @@ +/* + * HyperAMP Network Bridge – Singleton + * + * Proxies POSIX-style connect/send/recv/close over HyperAMP shared-memory + * queues so that monkey-mnemosyne (running on seL4, which has no network + * stack) can reach the Linux backend transparently. + * + * Design principles + * ----------------- + * - Direct use of the HyperAMP queue primitives (hyperamp_queue_enqueue / + * hyperamp_queue_dequeue). No dependency on apps/front beyond the + * shared C headers that define the queue and message structures. + * - Synchronous / polling semantics: every receive busy-waits until data + * arrives. This is intentional – performance is not a concern for the + * monkey protocol's low-bandwidth control traffic. + * - One active session at a time (sufficient for monkey-mnemosyne). + * + * Created 2025 – Monkey-Mnemosyne / seL4 HyperAMP integration + * + * gongty [at] tongji [dot] edu [dot] cn + */ + +#pragma once + +#include +#include + +/* HyperAMP C headers ---------------------------------------------------- */ +extern "C" { +#include +#include +} + +namespace monkey::net { + + +/** + * Maximum payload that fits in a single HyperAMP queue slot after + * subtracting the 8-byte HyperampMsgHeader. + */ +static constexpr adl::size_t kHyperAmpMaxPayload = HYPERAMP_MSG_MAX_SIZE; + +/** + * Combined header + max-payload – equals one queue block. + */ +static constexpr adl::size_t kHyperAmpBlockSize = HYPERAMP_MSG_HDR_PLUS_MAX_SIZE; + + +/** + * @brief Singleton bridge that replaces POSIX sockets with HyperAMP + * shared-memory queue operations. + * + * Typical usage (called from Socket4::connect, PromisedSocketIo::send, etc.): + * + * auto& bridge = HyperAmpBridge::instance(); + * bridge.init(); // once, at boot + * bridge.connect(ip, port); // session create + * bridge.send(data, len); // data transfer + * bridge.recv(buf, len); // blocking poll + * bridge.close(); // session close + */ +class HyperAmpBridge { +public: + /* ---- Singleton access ----------------------------------------------- */ + + static HyperAmpBridge& instance(); + + /* non-copyable, non-movable */ + HyperAmpBridge(const HyperAmpBridge&) = delete; + HyperAmpBridge& operator=(const HyperAmpBridge&) = delete; + HyperAmpBridge(HyperAmpBridge&&) = delete; + HyperAmpBridge& operator=(HyperAmpBridge&&) = delete; + + /* ---- Lifecycle ------------------------------------------------------ */ + + /** + * Initialise the HyperAMP TX/RX queues. + * + * Must be called exactly once before any other method. Subsequent calls + * are harmless no-ops. + * + * Queue virtual addresses are obtained from seL4_GetMR() (set up by the + * boot loader), identical to how apps/front reads them. + */ + void init(); + + /** + * @return true after a successful init(). + */ + bool isInitialized() const { return initialized_; } + + /* ---- Session management --------------------------------------------- */ + + /** + * Create a TCP session via the HyperAMP proxy. + * + * Builds a HYPERAMP_MSG_TYPE_SESS / SESS_MSG_CREATE command, enqueues it + * on the TX queue, then busy-waits on the RX queue for the matching + * session-create response. On success, stores the backend_sess_id for + * subsequent send/recv calls. + * + * @param ip Destination IPv4 address (network byte order). + * @param port Destination port (host byte order). + * @return true on success, false on failure. + */ + bool connect(const IP4Addr& ip, adl::uint16_t port); + + /** + * Send `len` bytes through the established session. + * + * Data is fragmented into HyperAMP queue slots as needed (each slot + * carries up to kHyperAmpMaxPayload bytes of user data after the 8-byte + * header). Enqueue is retried on HYPERAMP_AGAIN (queue full). + * + * @return Number of bytes sent, or -1 on error. + */ + adl::int64_t send(const void* buf, adl::size_t len); + + /** + * Receive exactly `len` bytes from the established session. + * + * Busy-waits (polls) the RX queue until enough HYPERAMP_MSG_TYPE_DATA + * messages have arrived to fill the caller's buffer. Partial trailing + * data from oversized messages is kept in an internal spill buffer for + * the next call. + * + * @return Number of bytes received (always == len on success), or -1. + */ + adl::int64_t recv(void* buf, adl::size_t len); + + /** + * Close the active session. + * + * Sends a HYPERAMP_MSG_TYPE_SESS / SESS_MSG_CLOSE command and waits for + * the response. + */ + void close(); + + /** + * @return true if a session is currently active. + */ + bool hasSession() const { return sessionActive_; } + +private: + /* ---- Construction (private – singleton) ------------------------------ */ + HyperAmpBridge(); + ~HyperAmpBridge() = default; + + /* ---- Internal helpers ------------------------------------------------ */ + + /** + * Enqueue raw bytes to the TX queue. Retries on HYPERAMP_AGAIN. + * @return true on success. + */ + bool txEnqueue(const void* data, adl::size_t len); + + /** + * Blocking dequeue from the RX queue. + * Polls until HYPERAMP_OK. + * + * @param[out] buf Destination buffer (must be >= kHyperAmpBlockSize). + * @param[out] outLen Actual number of bytes dequeued. + * @return true on success. + */ + bool rxDequeueBlocking(void* buf, adl::size_t* outLen); + + /** + * Allocate a fresh frontend_sess_id. Simple monotonic counter. + */ + adl::uint16_t nextFrontendSessId(); + + /* ---- State ---------------------------------------------------------- */ + + bool initialized_ = false; + bool sessionActive_ = false; + + volatile HyperampShmQueue* txQueue_ = nullptr; + volatile HyperampShmQueue* rxQueue_ = nullptr; + volatile void* dataRegion_ = nullptr; + + adl::uint16_t frontendSessId_ = 0; + adl::uint16_t backendSessId_ = 0; + adl::uint16_t sessIdCounter_ = 1; // monotonic counter for session IDs + + /* + * Spill buffer for recv(). + * + * When an RX dequeue delivers more payload bytes than the caller + * requested, the surplus is stashed here and drained on the next recv(). + */ + static constexpr adl::size_t kSpillCapacity = kHyperAmpMaxPayload; + adl::uint8_t spillBuf_[kSpillCapacity]; + adl::size_t spillLen_ = 0; // valid bytes in spillBuf_ + adl::size_t spillOffset_ = 0; // read cursor within spillBuf_ +}; + + +} // namespace monkey::net diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.cc index df2f623..4d9fa8e 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.cc +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/Socket4.cc @@ -3,10 +3,15 @@ created on 2025.2.6 at Xiangzhou, Zhuhai, Guangdong + Modified 2025 – connect() now goes through HyperAmpBridge instead of + POSIX sockets. start() and accept() are kept as-is (server-side logic + still uses the seL4 POSIX shim for the local listening socket). + gongty [at] tongji [dot] edu [dot] cn */ #include +#include #include @@ -23,22 +28,27 @@ namespace monkey::net { Status Socket4::connect() { close(); - Genode::log("[Socket4] Connecting to ", ip.toString().c_str(), " : ", port); + Genode::log("[Socket4] Connecting via HyperAmpBridge to ", + ip.toString().c_str(), " : ", port); - socketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (socketFd == -1) { - return Status::NETWORK_ERROR; - } + auto& bridge = HyperAmpBridge::instance(); - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = adl::htons(port); - addr.sin_addr.s_addr = ip.ui32; + /* Ensure the bridge is initialised (idempotent). */ + bridge.init(); - if (::connect(socketFd, (struct sockaddr*)&addr, sizeof(addr))) { + if (!bridge.connect(ip, port)) { + Genode::error("[Socket4] HyperAmpBridge connect failed"); return Status::NETWORK_ERROR; } + /* + * Set socketFd to a sentinel value > 1 so that valid() returns true. + * The actual I/O bypasses this fd entirely (routed through the bridge + * in PromisedSocketIo::recv / send / close), but upper layers check + * valid() before proceeding. + */ + socketFd = 9999; + return Status::SUCCESS; } diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.cc index 3f458bb..ed61744 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.cc +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/TcpIo.cc @@ -2,46 +2,34 @@ * Monkey Net :: TcpIo * * Created on 2025.1.21 at Xiangzhou, Zhuhai, Guangdong - * - * + * + * Modified 2025 – route all I/O through HyperAmpBridge instead of POSIX + * sockets so that monkey-mnemosyne can operate on seL4 (no network stack). + * * gongty [at] tongji [dot] edu [dot] cn - * */ #include #include +#include namespace monkey::net { void PromisedSocketIo::close() { - if (valid()) { - ::close(socketFd); - socketFd = -1; - } + HyperAmpBridge::instance().close(); + socketFd = -1; } adl::int64_t PromisedSocketIo::recv(void* buf, adl::size_t len) { - adl::size_t sum = 0; - while (sum < len) { - adl::int64_t curr = read(socketFd, ((char*) buf) + sum, len - sum); - -// Genode::warning("sum ", sum, ", curr ", curr, ", len ", len); - if (curr <= 0) { - return -1; - } - - - sum += size_t(curr); - } - - return adl::int64_t(sum); + return HyperAmpBridge::instance().recv(buf, len); } + adl::int64_t PromisedSocketIo::send(const void* buf, adl::size_t len) { - return ::write(socketFd, buf, len); + return HyperAmpBridge::instance().send(buf, len); } -- Gitee From 5422723bc1ee718f486971e943fb159795efefd1 Mon Sep 17 00:00:00 2001 From: GuanTouYu Date: Sun, 19 Apr 2026 17:29:29 +0800 Subject: [PATCH 126/133] update readme for monkey-mnemosyne, and rename main.cc. --- projects/sel4test/apps/monkey-mnemosyne/README.md | 1 + .../apps/monkey-mnemosyne/src/{main.cc => mnemosyne-main.cc} | 0 2 files changed, 1 insertion(+) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/README.md rename projects/sel4test/apps/monkey-mnemosyne/src/{main.cc => mnemosyne-main.cc} (100%) diff --git a/projects/sel4test/apps/monkey-mnemosyne/README.md b/projects/sel4test/apps/monkey-mnemosyne/README.md new file mode 100644 index 0000000..94d8197 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/README.md @@ -0,0 +1 @@ +Although this folder and app is called monkey-mnemosyne, it actually acts as a special version of monkey lab. diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/main.cc b/projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne-main.cc similarity index 100% rename from projects/sel4test/apps/monkey-mnemosyne/src/main.cc rename to projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne-main.cc -- Gitee From 667323b89ceab1074ac6ffebb499fd2c597a21b5 Mon Sep 17 00:00:00 2001 From: GuanTouYu Date: Sun, 19 Apr 2026 17:45:24 +0800 Subject: [PATCH 127/133] remove mnemosyne code, and create lab-app framework. --- .../apps/monkey-mnemosyne/src/AppLounge.cc | 219 --------- .../apps/monkey-mnemosyne/src/AppLounge.h | 41 -- .../apps/monkey-mnemosyne/src/Block.h | 47 -- .../src/GlobalMemoryManager.h | 162 ------- .../apps/monkey-mnemosyne/src/config.h | 29 +- .../apps/monkey-mnemosyne/src/lab-main.cc | 101 +++++ .../sel4test/apps/monkey-mnemosyne/src/main.h | 75 ---- .../monkey-mnemosyne/src/mnemosyne-main.cc | 423 ------------------ 8 files changed, 112 insertions(+), 985 deletions(-) delete mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc delete mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.h delete mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/Block.h delete mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/GlobalMemoryManager.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc delete mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/main.h delete mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne-main.cc diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc b/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc deleted file mode 100644 index 1c84b12..0000000 --- a/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.cc +++ /dev/null @@ -1,219 +0,0 @@ -/* - * - * gongty [at] tongji [dot] edu [dot] cn - * Created on 2025.2.10 at Yushan, Shangrao - */ - -#include "./AppLounge.h" - -using namespace monkey; - -using monkey::net::IP4Addr; -using monkey::net::protocol::Msg; -using monkey::net::protocol::MsgType; -using monkey::net::protocol::Header; - -Status AppLounge::processTryAlloc() { - - Block* b = context.globalMemoryManager.allocMemoryBlock(client.appId, this->context.nodeId); - if (b == nullptr) { - client.sendResponse(2, "Failed to alloc. Maybe out of ram."); - return Status::SUCCESS; - } - - return client.replyTryAlloc( - b->id, - b->version, - b->accessKey.readonly, - b->accessKey.readwrite - ); -} - - -Status AppLounge::processReadBlock(adl::int64_t blockId) { - auto* b = context.globalMemoryManager.getMemoryBlock(client.appId, blockId, GlobalMemoryManager::READ); - - if (b == nullptr) { - Genode::warning("[AppLounge] But block not found or not accessible."); - client.sendResponse(1, "Block not found or not readable."); - return Status::INVALID_PARAMETERS; - } - return client.replyReadBlock(b->version, b->data); -} - - -Status AppLounge::processWriteBlock(adl::int64_t blockId, const adl::ByteArray& data) { - auto* b = context.globalMemoryManager.getMemoryBlock(client.appId, blockId, GlobalMemoryManager::WRITE); - - - if (b == nullptr) { - client.sendResponse(1, "Block not found or not writable."); - return Status::INVALID_PARAMETERS; - } - - adl::memcpy(b->data, data.data(), b->size); - - adl::int64_t newVersion = ++b->version; - adl::int64_t newVerNetOrder = adl::ntohq(newVersion); - return client.sendResponse(0, sizeof(newVerNetOrder), &newVerNetOrder); -} - - -Status AppLounge::processCheckAvailMem() { - return client.replyCheckAvailMem(1024 * 1024); // just set it to fixed value... -} - - -Status AppLounge::processFreeBlock(adl::int64_t blockId) { - context.globalMemoryManager.unrefMemoryBlock(client.appId, blockId); - return client.sendResponse(0); -} - - -Status AppLounge::processRefBlock(adl::int64_t accessKey) { - adl::int64_t bid = context.globalMemoryManager.refMemoryBlock(client.appId, accessKey); - - if (bid == -1) { - return client.sendResponse(1); - } - - adl::int64_t bidNetOrder = adl::htonq(bid); - return client.sendResponse(0, sizeof(bidNetOrder), &bidNetOrder); -} - - -Status AppLounge::processUnrefBlock(adl::int64_t blockId) { - context.globalMemoryManager.unrefMemoryBlock(client.appId, blockId); - return client.sendResponse(0); -} - - -Status AppLounge::processGetBlockDataVersion(adl::int64_t blockId) { - auto* b = context.globalMemoryManager.getMemoryBlock(client.appId, blockId, GlobalMemoryManager::READ); - if (!b) - return client.sendResponse(1); - - adl::int64_t dataVerNetOrder = adl::htonq(b->version); - return client.sendResponse(0, sizeof(dataVerNetOrder), &dataVerNetOrder); -} - - -#undef GET_AND_VERIFY_BLOCK - -Status AppLounge::serve() { - MONKEY_LOG_ERROR("Deprecated in monkey mnemosyne. Use serveOnce() instead."); - return Status::FAILED; -} - - -Status AppLounge::serveOnce() { - Status status = Status::SUCCESS; - - Msg* msg = nullptr; - status = client.recvMsg(&msg); - if (status != Status::SUCCESS) { - return status; - } - - - switch ((MsgType) msg->header.type) { - case MsgType::TryAlloc: { - // Generated by Google Gemini 2.0 Flash. Checked by GTY. - - status = processTryAlloc(); - - break; - } - - case MsgType::FreeBlock: { - // Generated by Google Gemini 2.0 Flash. Checked by GTY. - - adl::int64_t blockId; - if ((status = client.decodeFreeBlock(msg, &blockId)) != Status::SUCCESS) { - client.sendResponse(1, "Bad request."); - break; - } - status = processFreeBlock(blockId); - break; - } - - case MsgType::ReadBlock: { - // Generated by Google Gemini 2.0 Flash. Checked by GTY. - - adl::int64_t blockId; - if ((status = client.decodeReadBlock(msg, &blockId)) != Status::SUCCESS) { - client.sendResponse(1, "Bad request."); - break; - } - status = processReadBlock(blockId); - break; - } - - case MsgType::WriteBlock: { - // Generated by Google Gemini 2.0 Flash. - - adl::int64_t blockId; - adl::ByteArray data; - if ((status = client.decodeWriteBlock(msg, &blockId, data)) != Status::SUCCESS) { - client.sendResponse(1, "Bad request."); - break; - } - status = processWriteBlock(blockId, data); - break; - } - - case MsgType::CheckAvailMem: { - processCheckAvailMem(); - break; - } - - case MsgType::RefBlock: { - adl::int64_t accessKey; - if ((status = client.decodeRefBlock(msg, &accessKey)) != Status::SUCCESS) { - client.sendResponse(1, "Bad request."); - break; - } - status = processRefBlock(accessKey); - if (status != Status::SUCCESS) { - Genode::warning("Failed to ref block with access key ", accessKey); - } - break; - } - - case MsgType::UnrefBlock: { - adl::int64_t blockId; - if ((status = client.decodeUnrefBlock(msg, &blockId)) != Status::SUCCESS) { - client.sendResponse(1, "Bad request."); - break; - } - status = processUnrefBlock(blockId); - break; - } - - case MsgType::GetBlockDataVersion: { - adl::int64_t blockId; - if ((status = client.decodeGetBlockDataVersion(msg, &blockId)) != Status::SUCCESS) { - client.sendResponse(1, "Bad request."); - break; - } - status = processGetBlockDataVersion(blockId); - break; - } - - default: { - Genode::warning("> Message Type NOT SUPPORTED"); - status = Status::PROTOCOL_ERROR; - client.sendResponse(1, "Msg Type not supported."); - break; - } - } - - - client.freeMsg(msg); - if (status != Status::SUCCESS) { - Genode::warning("AppLounge::serve() failed with status: ", adl::int32_t(status), "."); - } - - - return status; -} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.h b/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.h deleted file mode 100644 index 4a9042c..0000000 --- a/projects/sel4test/apps/monkey-mnemosyne/src/AppLounge.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * gongty [at] tongji [dot] edu [dot] cn - * Created on 2025.2.10 at Yushan, Shangrao - */ - - -#pragma once - -#include -#include - -#include "./main.h" -#include "./Block.h" - -struct AppLounge : monkey::net::SunflowerLounge -{ - AppLounge( - MnemosyneMain& main, - monkey::net::Protocol2Connection& client - ) - : SunflowerLounge(main, client) - {} - - // block id -> block - adl::HashMap memoryBlocks; - - monkey::Status processTryAlloc(); - monkey::Status processReadBlock(adl::int64_t blockId); - monkey::Status processWriteBlock(adl::int64_t blockId, const adl::ByteArray& data); - monkey::Status processCheckAvailMem(); - monkey::Status processFreeBlock(adl::int64_t blockId); - - monkey::Status processRefBlock(adl::int64_t accessKey); - monkey::Status processUnrefBlock(adl::int64_t blockId); - monkey::Status processGetBlockDataVersion(adl::int64_t blockId); - - virtual monkey::Status serve() override; - monkey::Status serveOnce(); -}; - diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/Block.h b/projects/sel4test/apps/monkey-mnemosyne/src/Block.h deleted file mode 100644 index b239cfa..0000000 --- a/projects/sel4test/apps/monkey-mnemosyne/src/Block.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - - Memory Block - - Created on 2025.2.7 at Xiangzhou, Zhuhai, Guangdong - - gongty [at] tongji [dot] edu [dot] cn - -*/ - - -#pragma once - -#include -#include - -struct Block { - adl::size_t size = 0; - char* data = nullptr; - - adl::int64_t id = 0; - - struct { - adl::int64_t readonly; - adl::int64_t readwrite; - } accessKey; - - - enum class ReferenceType : adl::int8_t { - READ_ONLY, - READ_WRITE - }; - - - adl::HashMap references; // app id -> whether this app has referenced this block. - adl::int64_t version = 0; // Version of this block's data. Incremented when data is written. - - bool isReferenced() { - return references.size() > 0; - } - - adl::size_t getReferenceCount() { - return references.size(); - } - -}; - diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/GlobalMemoryManager.h b/projects/sel4test/apps/monkey-mnemosyne/src/GlobalMemoryManager.h deleted file mode 100644 index 59b95c3..0000000 --- a/projects/sel4test/apps/monkey-mnemosyne/src/GlobalMemoryManager.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - Mnemosyne Global Memory Manager - - By - gongty [at] alumni [dot] tongji [dot] edu [dot] cn - - Created on 2025.6.15 at Minhang, Shanghai - -*/ - -#pragma once - -#include - -#include - -#include -#include - -#include "./Block.h" -#include "./config.h" -#include - - -// Directed by Monkey Protocol V2 -class GlobalMemoryManager -{ -protected: - - - // block id -> block struct - // TODO: consider using wayland-style linked list so we can find apps' block faster by app id. - adl::HashMap memoryBlocks; - - adl::int64_t nextMemoryBlockId = 5000000001; - adl::int64_t nextAccessKey = 10000001; - - - enum { - LOCK_BLOCK_MAP = true, - DONT_LOCK_BLOCK_MAP = false, - }; - - void freeMemoryBlock(Block* b, bool lockBlockMap = LOCK_BLOCK_MAP) { - free(b->data); - - adl::defaultAllocator.free(b); - MONKEY_LOG_INFO("Released Block id: ", b->id, ", size ", b->size); - } -public: - GlobalMemoryManager() - { - - } - - virtual ~GlobalMemoryManager() {} - - - /** - * For app lounge. - * @param nodeId the node id of this block, used to generate access key. - * @return alloced block pointer if success - */ - Block* allocMemoryBlock(adl::int64_t appId, adl::int64_t nodeId) { - const adl::size_t size = 4096; - - const adl::size_t finalSize = size; - - Block* b = adl::defaultAllocator.alloc(); - b->data = (char*) malloc(finalSize); - if (!b->data) { - Genode::error("Failed to allocate memory block. Memory is not enough !!!"); - adl::defaultAllocator.free(b); - return nullptr; - } - - b->id = nextMemoryBlockId++; - b->size = finalSize; - - // only use 16 bit of node_id, 48 bits of nextAccessKey , we assume node_id - // is less than 65536. - b->accessKey.readonly = ((nodeId & 0xffff) << 48 | (nextAccessKey++ & 0xFFFFFFFFFFFF)); - b->accessKey.readwrite = ((nodeId & 0xffff) << 48 | (nextAccessKey++ & 0xFFFFFFFFFFFF)); - b->version = 0; - - - this->memoryBlocks[b->id] = b; - - b->references[appId] = Block::ReferenceType::READ_WRITE; // The creator of this block is the first one to reference it. - - return b; - } - - - /** - * Block's creator should call this since it has been - * seen as normal client as other who ref this block. - * - * @param accessKey - * @return -1 for failure, otherwise the block id. - */ - adl::int64_t refMemoryBlock(adl::int64_t appId, adl::int64_t accessKey) { - for (auto it : memoryBlocks) { - Block& block = *it.second; - bool canRead = (block.accessKey.readonly == accessKey); - bool canWrite = (block.accessKey.readwrite == accessKey); - if (canRead || canWrite) { - block.references[appId] = (canWrite) ? Block::ReferenceType::READ_WRITE : Block::ReferenceType::READ_ONLY; - return block.id; - } - } - - return -1; - } - - void unrefMemoryBlock(adl::int64_t appId, adl::int64_t blockId) { - - if (!memoryBlocks.contains(blockId)) { - return; - } - - Block* b = memoryBlocks[blockId]; - - // remove using noExcept mode. So nothing happens if appId is not found. - b->references.removeKey(appId, true); - - if (b->references.size() == 0) { - // No one references this block, we can free it. - freeMemoryBlock(b, DONT_LOCK_BLOCK_MAP); - } - } - - - enum GET_BLOCK_PERMISSION { - READ = 0, - WRITE = 1 - }; - - Block* getMemoryBlock(adl::int64_t appId, adl::int64_t blockId, adl::int8_t permission) { - - if (!memoryBlocks.contains(blockId)) { - Genode::warning("[GlobalMemoryManager] No such block. Block id: ", blockId); - return nullptr; // No such block. - } - - Block& block = *memoryBlocks[blockId]; - if (!block.references.contains(appId)) { - Genode::warning("[GlobalMemoryManager] No permission for app ", appId, " to access block ", blockId); - return nullptr; // No any permission. - } - - // Check permission. - - if (permission == WRITE && block.references[appId] != Block::ReferenceType::READ_WRITE) { - Genode::warning("[GlobalMemoryManager] App ", appId, " tries to write block ", blockId, - " but it is not writable. Permission: ", (int) block.references[appId]); - return nullptr; // You don't have write permission. - } - return █ - } - -}; diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/config.h b/projects/sel4test/apps/monkey-mnemosyne/src/config.h index 5719284..75bac83 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/config.h +++ b/projects/sel4test/apps/monkey-mnemosyne/src/config.h @@ -1,5 +1,5 @@ /* - * Monkey Mnemosyne Config + * Monkey Lab for JK. * * Created on 2025.2.14 at Yushan, Shangrao * @@ -11,23 +11,16 @@ #pragma once -#define MONKEY_MNEMOSYNE_DEBUG 1 // 1 or 0 - -#define MONKEY_MNEMOSYNE_HEAP_MEMORY_RESERVED (2 * 1024 * 1024) // in bytes +#include +enum class LabApp { + App1, + App2 +}; struct { - const char* conciergeIp; - const int conciergePort; - const char* ip; - const int port; - const int listenPort; - const char* key; -} static const MnemosyneRunConfig = { - .conciergeIp = "127.0.0.1", - .conciergePort = 5555, - .ip = "10.0.2.2", - .port = 10100, - .listenPort = 10100, - .key = "41f413a9-2c21-4721-9f0d-d8ee9b4961f1" -}; + const char* mnemosyneIp = ""; + adl::uint16_t mnemosynePort = 10100; + const char* appKey = ""; + LabApp labApp = LabApp::App1; +} static const labConfig; diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc b/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc new file mode 100644 index 0000000..408eb73 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc @@ -0,0 +1,101 @@ +// ddst.sjtu.edu.cn + +#include + +#include + +#include + +#include +#include +#include + +#include + +#include "config.h" + +using namespace monkey; + +using HelloMode = net::ProtocolConnection::HelloMode; + + +static void initAdlAlloc() { + + static struct { + + } adlAllocData; + + + adl::defaultAllocator.init({ + .alloc = [] (adl::size_t size, void* data) { + return malloc(size); + }, + + .free = [] (void* addr, adl::size_t size, void* data) { + free(addr); + }, + + .data = &adlAllocData + }); +} + + +static void init() { + initAdlAlloc(); +} + + +#define MK_PROTO_DO_RETURN_ON_ERR(call, prompt) do { \ + monkey::Status status = call; \ + if (status != monkey::Status::SUCCESS) { \ + Genode::error(prompt); \ + return status; \ + } \ +} while (0) + + +static Status createMnemosyneSession(net::Protocol2Connection& client) { + MK_PROTO_DO_RETURN_ON_ERR(client.connect(), "Failed to connect with server."); + MK_PROTO_DO_RETURN_ON_ERR(client.hello(2, HelloMode::CLIENT), "Failed to say hello to server."); + MK_PROTO_DO_RETURN_ON_ERR(client.auth(labConfig.appKey), "Failed to auth with server."); + + return monkey::Status::SUCCESS; +} + + +static void doLabApp1() { + // TODO +} + + +static void doLabApp2() { + // TODO +} + + +int main() { + init(); + + monkey::net::Protocol2Connection client; + client.ip.set(labConfig.mnemosyneIp); + client.port = labConfig.mnemosynePort; + + if (createMnemosyneSession(client) != monkey::Status::SUCCESS) { + Genode::error("Failed to create session with server."); + client.close(); + return -1; + } + + if (labConfig.labApp == LabApp::App1) { + doLabApp1(); + } + else if (labConfig.labApp == LabApp::App2) { + doLabApp2(); + } + else { + Genode::error("Unknown lab app."); + } + + client.close(); + return 0; +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/main.h b/projects/sel4test/apps/monkey-mnemosyne/src/main.h deleted file mode 100644 index 47558b6..0000000 --- a/projects/sel4test/apps/monkey-mnemosyne/src/main.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Monkey Mnemosyne : Memory provider. - * - * Created on 2025.2.4 at Xiangzhou, Zhuhai, Guangdong - * - * - * gongty [at] tongji [dot] edu [dot] cn - * - */ - - -#pragma once - -#include "./config.h" - -#include -#include -#include - - -#include -#include -#include - -#include "./Block.h" - - -#include "./GlobalMemoryManager.h" - - -struct AppLounge; - -struct MnemosyneMain { - - adl::int64_t nodeId = 0; - - - GlobalMemoryManager globalMemoryManager; - - adl::HashMap appKeys; - - // key: AppLounge. value: network socket fd. - adl::HashMap clientLoungeRecycleBin; - - struct { - struct { - monkey::net::IP4Addr ip; - adl::uint16_t port; - } concierge; - - struct { - monkey::net::IP4Addr ip; - adl::uint16_t port; - adl::uint16_t listenPort; - adl::ByteArray key; - } mnemosyne; - } config; - - MnemosyneMain(); - - - monkey::Status loadConfig(); - monkey::Status init(); - - monkey::Status clockIn(); - AppLounge* serveClient(monkey::net::Socket4& conn); - monkey::Status runServer(); - - monkey::Status run(); - void cleanup(); - - -}; - - diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne-main.cc b/projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne-main.cc deleted file mode 100644 index 31befa4..0000000 --- a/projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne-main.cc +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Monkey Mnemosyne : Memory provider. - * - * Created on 2025.2.4 at Xiangzhou, Zhuhai, Guangdong - * - * - * gongty [at] tongji [dot] edu [dot] cn - * - */ - - -#include "./main.h" - -#include - -#include - -#include - -#include -#include -#include - -#include - -#include "./AppLounge.h" - -using namespace monkey; - - -using HelloMode = net::ProtocolConnection::HelloMode; - - -static const adl::TString memSizeToHumanReadable(adl::size_t size) { - const char* levels[] = { - " B", " KB", " MB", " GB", " TB" - }; - - for (adl::size_t i = 0; i < sizeof(levels) / sizeof(levels[0]); i++) { - if (size < 8192) - return adl::TString::to_string(size) + levels[i]; - size /= 1024; - } - - return "+oo"; // Treat as infinity. -} - - -static void initAdlAlloc() { - - static struct { - - } adlAllocData; - - - adl::defaultAllocator.init({ - .alloc = [] (adl::size_t size, void* data) { - return malloc(size); - }, - - .free = [] (void* addr, adl::size_t size, void* data) { - free(addr); - }, - - .data = &adlAllocData - }); -} - - -Status MnemosyneMain::loadConfig() { - Status status = Status::SUCCESS; - -#define IF_STATUS_NOT_SUCCESS_THEN_RETURN() do { if (status != Status::SUCCESS) return status; } while (0) - - // concierge - { - status = config.concierge.ip.set(MnemosyneRunConfig.conciergeIp); - IF_STATUS_NOT_SUCCESS_THEN_RETURN(); - config.concierge.port = (adl::uint16_t) MnemosyneRunConfig.conciergePort; - } - - // mnemosyne - { - - status = config.mnemosyne.ip.set(MnemosyneRunConfig.ip); - IF_STATUS_NOT_SUCCESS_THEN_RETURN(); - config.mnemosyne.port = MnemosyneRunConfig.port; - config.mnemosyne.listenPort = MnemosyneRunConfig.listenPort; - config.mnemosyne.key = MnemosyneRunConfig.key; - } - - -#undef IF_STATUS_NOT_SUCCESS_THEN_RETURN - - Genode::log("--- mnemosyne config begin ---"); - Genode::log("> mnemosyne ip : ", config.mnemosyne.ip.toString().c_str()); - Genode::log("> mnemosyne port : ", config.mnemosyne.port); - Genode::log("> mnemosyne listen: ", config.mnemosyne.listenPort); - Genode::log("> concierge ip : ", config.concierge.ip.toString().c_str()); - Genode::log("> concierge port : ", config.concierge.port); - Genode::log("--- mnemosyne config end ---"); - - return status; -} - - -Status MnemosyneMain::init() { - initAdlAlloc(); - Status status = Status::SUCCESS; - - status = loadConfig(); - if (status != monkey::Status::SUCCESS) { - Genode::error("Exception on loading config. Check your .run file."); - return status; - } - - return status; -} - - -Status MnemosyneMain::clockIn() { // register self to monkey concierge. - Genode::log("Trying to clock in."); - - net::protocol::Response* response = nullptr; - - net::Protocol2Connection client; - client.ip = config.concierge.ip; - client.port = config.concierge.port; - - while (client.connect() != Status::SUCCESS) { - Genode::error("Failed to connect with server."); - Genode::error("> Retrying..."); - } - - Status status; - - // Open connection using latest protocol. - - if ((status = client.hello(net::protocol::LATEST_VERSION, HelloMode::CLIENT)) != Status::SUCCESS) { - Genode::error("(Clock In) Failed on [Hello]."); - goto END; - } - - if ((status = client.auth(config.mnemosyne.key)) != Status::SUCCESS) { - Genode::error("(Clock In) Failed on auth."); - goto END; - } - - - // Clock in. - - adl::int64_t id; - status = client.memoryNodeClockIn(&id, config.mnemosyne.ip, config.mnemosyne.port); - if (status != Status::SUCCESS) { - Genode::error("(Clock In) Failed on doing Memory Node Clock In."); - goto END; - } - Genode::log("(Clock In) Node ID is: ", id); - this->nodeId = id; - - // Get identity keys. - - if ((status = client.sendGetIdentityKeys()) != Status::SUCCESS) - goto END; - - if ((status = client.recvResponse(&response)) != Status::SUCCESS) - goto END; - - if (response->code != 0) { - adl::ByteArray bMsg {response->msg, response->msgLen}; - Genode::error(bMsg.toString().c_str()); - goto END; - } - - - // Appreciate identity keys. - - { - struct { - MnemosyneMain* main; - } data; - - data.main = this; - - status = client.appreciateGetIdentityKeys( - *response, - &data, - [] ( - net::Protocol1Connection::ReplyGetIdentityKeysParams::NodeType nodeType, - net::Protocol1Connection::ReplyGetIdentityKeysParams::KeyType keyType, - const adl::ByteArray& key, - adl::int64_t id, - void* rawData - ) { - - const auto AppType = net::Protocol1Connection::ReplyGetIdentityKeysParams::NodeType::App; - const auto RC4Type = net::Protocol1Connection::ReplyGetIdentityKeysParams::KeyType::RC4; - - auto typedData = (decltype(data) *) rawData; - - // We don't care about memory nodes' keys. We can't deal algorithms other than RC4. - - if (nodeType != AppType || keyType != RC4Type) { - Genode::log( - "Key ignored: (", - adl::int8_t(nodeType), - ", ", - adl::int8_t(keyType), - ") ", - key.toString().c_str() - ); - return; - } - - typedData->main->appKeys[id] = key; - Genode::log("From Concierge: App key: [", id, "] [", key.toString().c_str(), "]"); - } - - ); - - if (status != Status::SUCCESS) { - Genode::error("Error on loading identity keys from Concierge."); - goto END; - } - } - - - // Cleanup. - -END: - client.close(); - if (response) { - client.freeMsg(response); - response = nullptr; - } - return status; -} - - - -AppLounge* MnemosyneMain::serveClient(net::Socket4& conn) { - Genode::log("Client connected: ", conn.ip.toString().c_str(), " [", conn.port, "]"); - - Status status; - - net::Protocol2Connection client; - client.socketFd = conn.socketFd; - client.ip = conn.ip; - client.port = conn.port; - - // Force use protocol v2. - if (client.hello(net::protocol::LATEST_VERSION, HelloMode::SERVER) != Status::SUCCESS) - return nullptr; - Genode::log("> Using protocol version ", net::protocol::LATEST_VERSION, "."); - - // Auth - - if ((status = client.auth(&appKeys, nullptr)) != Status::SUCCESS) { - Genode::error("Failed on auth. Status: ", adl::int32_t(status)); - return nullptr; - } - - if (client.nodeType != net::Protocol1Connection::NodeType::App) { - Genode::error("Client is not App node!"); - Genode::error("> Connection between memory nodes is not supported yet."); - return nullptr; - } - - // Now, we can say client is an authenticated App node. - - // Enter lounge. - - if (client.nodeType == net::Protocol1Connection::NodeType::App) - { - - auto lounge = new AppLounge {*this, client}; - if (lounge == nullptr) { - Genode::error("Failed to create lounge thread. Out of memory."); - client.close(); - return nullptr; - } - - return lounge; - } - - return nullptr; -} - - -Status MnemosyneMain::runServer() { - Genode::log("Starting server.."); - - net::Socket4 server; - server.port = config.mnemosyne.listenPort; - server.ip = INADDR_ANY; - - Status status = server.start(); - if (status != Status::SUCCESS) { - return status; - } - - - // client listen fd -> app lounge - adl::HashMap lounges; - - - while (true) { - fd_set readFds; - FD_ZERO(&readFds); - - int maxFd = server.socketFd; - - FD_SET(server.socketFd, &readFds); - for (auto it : lounges) { - if (clientLoungeRecycleBin.contains(it.second)) - continue; - - FD_SET(it.first, &readFds); - if (it.first > maxFd) - maxFd = it.first; - } - - int selectResult = select(maxFd + 1, &readFds, nullptr, nullptr, nullptr); - if (selectResult < 0) { - MONKEY_LOG_ERROR("Select failed."); - break; - } - - for (auto it : clientLoungeRecycleBin) { - lounges.removeKey(it.second); - adl::defaultAllocator.free(it.first); - } - - clientLoungeRecycleBin.clear(); - - if (FD_ISSET(server.socketFd, &readFds)) { - auto client = server.accept(true); - auto lounge = serveClient(client); - - if (lounge) - lounges[client.socketFd] = lounge; - else { - client.close(); - Genode::warning("Client rejected: ", client.ip.toString().c_str(), " [", client.port, "]"); - } - - } - - - for (auto it : lounges) { - auto client = it.second->client; - auto sockFd = client.socketFd; - if (!FD_ISSET(it.first, &readFds)) - continue; - - Status status = it.second->serveOnce(); - if (status != Status::SUCCESS) { - Genode::log("Client disconnected: ", client.ip.toString().c_str(), " [", client.port, "]"); - client.close(); - clientLoungeRecycleBin[it.second] = sockFd; - } - - } - - - } - - - for (auto it : lounges) { - it.second->client.close(); - adl::defaultAllocator.free(it.second); - } - - server.close(); - return Status::SUCCESS; -} - - - -Status MnemosyneMain::run() { - Status status = clockIn(); - if (status != Status::SUCCESS) { - Genode::error("Something went wrong during clock-in. Status: ", adl::int32_t(status)); - return status; - } - - status = runServer(); - - return status; -} - - -void MnemosyneMain::cleanup() { - -} - - -MnemosyneMain::MnemosyneMain() { - - Genode::log("Welcome to Monkey Mnemosyne."); - - - Status status = init(); - if (status != Status::SUCCESS) { - Genode::error("Failed on init."); - cleanup(); - return; - } - - status = run(); - if (status != Status::SUCCESS) { - Genode::warning("Something went wrong on runtime."); - } - - - cleanup(); - Genode::log("Bye :D"); -} - - -int main() { - static MnemosyneMain main; -} -- Gitee From 8b50ba3d87d7e2c18bc2e1aea7fc7a9a3cf7d6bc Mon Sep 17 00:00:00 2001 From: gongtianyao Date: Sun, 19 Apr 2026 18:07:49 +0800 Subject: [PATCH 128/133] copy hyperamp wrapper codes from apps/front to apps/monkey-mnemosyne. --- .../apps/monkey-mnemosyne/CMakeLists.txt | 8 - .../src/monkey/net/HyperAmpBridge.cc | 38 +- .../src/monkey/net/HyperAmpBridge.h | 6 +- .../src/monkey/net/hyperamp_protocol_defs.h | 95 ++++ .../src/monkey/net/hyperamp_shm_queue.c | 406 ++++++++++++++++++ .../src/monkey/net/hyperamp_shm_queue.h | 236 ++++++++++ 6 files changed, 759 insertions(+), 30 deletions(-) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_protocol_defs.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_shm_queue.c create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_shm_queue.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt index ed7feae..bc3e159 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt +++ b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt @@ -17,13 +17,6 @@ file(GLOB_RECURSE deps src/*.c*) list(SORT deps) list(REMOVE_DUPLICATES deps) -# HyperAMP shared-memory queue sources from apps/front (needed by HyperAmpBridge). -set(FRONT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../front) -list(APPEND deps - ${FRONT_DIR}/src/hyperamp_shm_queue.c - ${FRONT_DIR}/src/common_utils.c -) - add_executable(monkey-mnemosyne EXCLUDE_FROM_ALL ${deps}) target_link_libraries(monkey-mnemosyne PUBLIC @@ -43,7 +36,6 @@ target_link_libraries(monkey-mnemosyne include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src - ${FRONT_DIR}/include # HyperAMP / proxy-protocol headers ) # GCC(g++) 编译选项。 diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc index f68521e..2bc980e 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc @@ -11,11 +11,11 @@ #include #include -#include +#include extern "C" { -#include -#include +#include "hyperamp_shm_queue.h" +#include "hyperamp_protocol_defs.h" #include } @@ -27,7 +27,7 @@ namespace monkey::net { /* ======================================================================== */ HyperAmpBridge::HyperAmpBridge() { - std::memset(spillBuf_, 0, sizeof(spillBuf_)); + memset(spillBuf_, 0, sizeof(spillBuf_)); } HyperAmpBridge& HyperAmpBridge::instance() { @@ -78,7 +78,7 @@ void HyperAmpBridge::init() { * block_size = 4096 bytes */ HyperampQueueConfig txCfg; - std::memset(&txCfg, 0, sizeof(txCfg)); + memset(&txCfg, 0, sizeof(txCfg)); txCfg.map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH; txCfg.capacity = 256; txCfg.block_size = 4096; @@ -91,7 +91,7 @@ void HyperAmpBridge::init() { } HyperampQueueConfig rxCfg; - std::memset(&rxCfg, 0, sizeof(rxCfg)); + memset(&rxCfg, 0, sizeof(rxCfg)); rxCfg.map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH; rxCfg.capacity = 256; rxCfg.block_size = 4096; @@ -218,7 +218,7 @@ bool HyperAmpBridge::connect(const IP4Addr& ip, adl::uint16_t port) { /* --- SessMsgHeader --- */ SessMsgHeader sessHdr; - std::memset(&sessHdr, 0, sizeof(sessHdr)); + memset(&sessHdr, 0, sizeof(sessHdr)); sessHdr.version = PROXY_PROTO_SESS_VERSION_1; sessHdr.msg_type = SESS_MSG_CREATE; sessHdr.action_type = ACTION_TYPE_COMMAND; @@ -227,7 +227,7 @@ bool HyperAmpBridge::connect(const IP4Addr& ip, adl::uint16_t port) { /* --- SessIPv4Params --- */ SessIPv4Params sessParams; - std::memset(&sessParams, 0, sizeof(sessParams)); + memset(&sessParams, 0, sizeof(sessParams)); sessParams.device_selection = 0xFF; // DEV_ID_AUTO_HANDOVER sessParams.transport_layer_proto = SESS_TCP_PROTO; sessParams.dest_endpoint.ipv4_addr.data[0] = ip.ui8arr[0]; @@ -238,7 +238,7 @@ bool HyperAmpBridge::connect(const IP4Addr& ip, adl::uint16_t port) { /* --- Assemble the full message --- */ uint8_t msgBuf[kHyperAmpBlockSize]; - std::memset(msgBuf, 0, sizeof(msgBuf)); + memset(msgBuf, 0, sizeof(msgBuf)); /* Outer HyperAMP header. */ HyperampMsgHeader* outerHdr = reinterpret_cast(msgBuf); @@ -249,9 +249,9 @@ bool HyperAmpBridge::connect(const IP4Addr& ip, adl::uint16_t port) { outerHdr->payload_len = static_cast(sizeof(SessMsgHeader) + sizeof(SessIPv4Params)); /* Session sub-header + params follow the outer header. */ - std::memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE, + memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE, &sessHdr, sizeof(sessHdr)); - std::memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE + sizeof(SessMsgHeader), + memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE + sizeof(SessMsgHeader), &sessParams, sizeof(sessParams)); adl::size_t totalLen = HYPERAMP_MSG_HDR_SIZE + sizeof(SessMsgHeader) + sizeof(SessIPv4Params); @@ -344,7 +344,7 @@ adl::int64_t HyperAmpBridge::send(const void* buf, adl::size_t len) { /* Build a HYPERAMP_MSG_TYPE_DATA message. */ uint8_t msgBuf[kHyperAmpBlockSize]; - std::memset(msgBuf, 0, sizeof(msgBuf)); + memset(msgBuf, 0, sizeof(msgBuf)); HyperampMsgHeader* hdr = reinterpret_cast(msgBuf); hdr->version = PROXY_PROTO_VERSION_1; @@ -353,7 +353,7 @@ adl::int64_t HyperAmpBridge::send(const void* buf, adl::size_t len) { hdr->backend_sess_id = backendSessId_; hdr->payload_len = static_cast(chunk); - std::memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE, src, chunk); + memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE, src, chunk); if (!txEnqueue(msgBuf, HYPERAMP_MSG_HDR_SIZE + chunk)) { MONKEY_LOG_ERROR("[HyperAmpBridge] send() – txEnqueue failed after ", @@ -388,7 +388,7 @@ adl::int64_t HyperAmpBridge::recv(void* buf, adl::size_t len) { if (spillLen_ > spillOffset_) { adl::size_t avail = spillLen_ - spillOffset_; adl::size_t take = (avail <= needed) ? avail : needed; - std::memcpy(dst, spillBuf_ + spillOffset_, take); + memcpy(dst, spillBuf_ + spillOffset_, take); spillOffset_ += take; dst += take; needed -= take; @@ -443,7 +443,7 @@ adl::int64_t HyperAmpBridge::recv(void* buf, adl::size_t len) { /* Copy as much as the caller still needs. */ adl::size_t take = (payloadLen <= needed) ? payloadLen : needed; - std::memcpy(dst, payload, take); + memcpy(dst, payload, take); dst += take; needed -= take; got += take; @@ -451,7 +451,7 @@ adl::int64_t HyperAmpBridge::recv(void* buf, adl::size_t len) { /* Stash surplus in the spill buffer. */ if (take < payloadLen) { adl::size_t surplus = payloadLen - take; - std::memcpy(spillBuf_, payload + take, surplus); + memcpy(spillBuf_, payload + take, surplus); spillLen_ = surplus; spillOffset_ = 0; } @@ -482,7 +482,7 @@ void HyperAmpBridge::close() { * The CLOSE command has no payload beyond the session sub-header. */ SessMsgHeader sessHdr; - std::memset(&sessHdr, 0, sizeof(sessHdr)); + memset(&sessHdr, 0, sizeof(sessHdr)); sessHdr.version = PROXY_PROTO_SESS_VERSION_1; sessHdr.msg_type = SESS_MSG_CLOSE; sessHdr.action_type = ACTION_TYPE_COMMAND; @@ -490,7 +490,7 @@ void HyperAmpBridge::close() { sessHdr.payload_len = 0; uint8_t msgBuf[kHyperAmpBlockSize]; - std::memset(msgBuf, 0, sizeof(msgBuf)); + memset(msgBuf, 0, sizeof(msgBuf)); HyperampMsgHeader* outerHdr = reinterpret_cast(msgBuf); outerHdr->version = PROXY_PROTO_VERSION_1; @@ -499,7 +499,7 @@ void HyperAmpBridge::close() { outerHdr->backend_sess_id = backendSessId_; outerHdr->payload_len = static_cast(sizeof(SessMsgHeader)); - std::memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE, &sessHdr, sizeof(sessHdr)); + memcpy(msgBuf + HYPERAMP_MSG_HDR_SIZE, &sessHdr, sizeof(sessHdr)); adl::size_t totalLen = HYPERAMP_MSG_HDR_SIZE + sizeof(SessMsgHeader); txEnqueue(msgBuf, totalLen); diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h index 1b6b2e8..600e671 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h @@ -25,10 +25,10 @@ #include #include -/* HyperAMP C headers ---------------------------------------------------- */ +/* HyperAMP C headers (local copies – see hyperamp_shm_queue.h comment) --- */ extern "C" { -#include -#include +#include "hyperamp_shm_queue.h" +#include "hyperamp_protocol_defs.h" } namespace monkey::net { diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_protocol_defs.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_protocol_defs.h new file mode 100644 index 0000000..fc70bc0 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_protocol_defs.h @@ -0,0 +1,95 @@ +/** + * @file hyperamp_protocol_defs.h + * @brief HyperAMP proxy protocol definitions – monkey-mnemosyne subset + * + * Extracted from apps/front/include/message.h. Contains only the structures + * and enumerations required by HyperAmpBridge: + * + * - SessMsgHeader (session create/close sub-header) + * - SessIPv4Params (IPv4 session parameters) + * - SessOpRespData (session operation response) + * - Related enums (SessMsgType, SessIpProtoVersion, SessTranProto, + * SessOpStatus, ActionType) + * - PROXY_PROTO_VERSION_1 / PROXY_PROTO_SESS_VERSION_1 + * + * This file has NO dependency on common_utils.h, message.h, or any other + * apps/front header. All definitions are self-contained. + */ + +#ifndef HYPERAMP_PROTOCOL_DEFS_LOCAL_H +#define HYPERAMP_PROTOCOL_DEFS_LOCAL_H + +#include + +/* ==================== Protocol version constants ==================== */ + +#define PROXY_PROTO_VERSION_1 1 +#define PROXY_PROTO_SESS_VERSION_1 1 + +/* ==================== Action type ==================== */ + +typedef enum { + ACTION_TYPE_COMMAND = 0, + ACTION_TYPE_RESPONSE = 1 +} ActionType; + +/* ==================== Session message header ==================== */ + +typedef struct { + uint16_t version; + uint16_t msg_type; + uint16_t action_type; + uint16_t ip_version; + uint16_t payload_len; +} __attribute__((packed)) SessMsgHeader; + +typedef enum { + SESS_MSG_CLOSE = 0, + SESS_MSG_CREATE = 1 +} SessMsgType; + +typedef enum { + SESS_NON_IP_PROTO = 0, + SESS_IPV4_PROTO = 4, + SESS_IPV6_PROTO = 6 +} SessIpProtoVersion; + +typedef enum { + SESS_UDP_PROTO = 0, + SESS_TCP_PROTO = 1, + SESS_FASTPATH_PROTO = 2 +} SessTranProto; + +/* ==================== IPv4 address / endpoint ==================== */ + +struct IPv4Address { + uint8_t data[4]; +} __attribute__((packed)); + +typedef struct { + struct IPv4Address ipv4_addr; + uint16_t port; +} __attribute__((packed)) IPv4PortTuple; + +/* ==================== Session IPv4 parameters ==================== */ + +typedef struct { + uint16_t device_selection; + uint16_t transport_layer_proto; + IPv4PortTuple dest_endpoint; +} __attribute__((packed)) SessIPv4Params; + +/* ==================== Session operation response ==================== */ + +typedef enum { + SESS_OP_STATUS_SUCCESS = 0, + SESS_OP_STATUS_FAIL = 1 +} SessOpStatus; + +typedef struct SessOpRespData_ { + uint8_t status; + uint8_t code; +} __attribute__((packed)) SessOpRespData; + + +#endif /* HYPERAMP_PROTOCOL_DEFS_LOCAL_H */ diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_shm_queue.c b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_shm_queue.c new file mode 100644 index 0000000..adcfcc7 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_shm_queue.c @@ -0,0 +1,406 @@ +/** + * @file hyperamp_shm_queue.c + * @brief HyperAMP Shared Memory Queue – monkey-mnemosyne local copy + * + * Copied from apps/front/src/hyperamp_shm_queue.c and cleaned up: + * + * 1. Removed #include "common_utils.h" and all calls to + * parse_proxy_protocol_and_print() / DUMP_PROXY_MSG_HEADER(). + * 2. Removed print_hex() / print_string() debug helpers (not needed). + * 3. Fixed the original bug on line 343 where print_hex() was called with + * a uint64_t instead of a const uint8_t*. + * 4. Removed the TRUSTED_PUBKEY_DER array (signature verification is + * not used by monkey-mnemosyne). + * 5. Retained all queue operation logic verbatim. + */ + +#include "hyperamp_shm_queue.h" + + +/* ==================== Software Spinlock ==================== */ + +static inline void hyperamp_spinlock_init(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + lock->lock_value = 0; + lock->owner_zone_id = 0; + lock->lock_count = 0; + lock->contention_count = 0; + HYPERAMP_BARRIER(); +} + +static inline void hyperamp_spinlock_lock(volatile HyperampSpinlock *lock, uint32_t zone_id) +{ + if (!lock) return; +#if defined(__aarch64__) + uint32_t tmp, newval; + __asm__ volatile( + "1: ldaxr %w0, %2\n" + " cbnz %w0, 1b\n" + " mov %w1, #1\n" + " stlxr %w0, %w1, %2\n" + " cbnz %w0, 1b\n" + : "=&r" (tmp), "=&r" (newval), "+Q" (lock->lock_value) + : + : "memory" + ); + lock->owner_zone_id = zone_id; + lock->lock_count++; +#else + while (1) { + HYPERAMP_BARRIER(); + if (lock->lock_value == 0) { + lock->lock_value = 1; + HYPERAMP_BARRIER(); + if (lock->lock_value == 1) { + lock->owner_zone_id = zone_id; + lock->lock_count++; + return; + } + } + lock->contention_count++; + __asm__ volatile("pause" ::: "memory"); + } +#endif +} + +static inline void hyperamp_spinlock_unlock(volatile HyperampSpinlock *lock) +{ + if (!lock) return; + lock->owner_zone_id = 0; +#if defined(__aarch64__) + __asm__ volatile( + "stlr wzr, %0\n" + : "+Q" (lock->lock_value) + : + : "memory" + ); +#else + HYPERAMP_BARRIER(); + lock->lock_value = 0; + HYPERAMP_BARRIER(); +#endif +} + + +/* Verify queue_lock alignment for LDAXR/STXR on aarch64. */ +_Static_assert(offsetof(HyperampShmQueue, queue_lock) % 4 == 0, + "HyperampShmQueue.queue_lock must be 4-byte aligned for LDAXR/STXR"); +_Static_assert(sizeof(HyperampSpinlock) == 16, + "HyperampSpinlock size must be 16 bytes"); + +/* Safe pointer to queue_lock (avoids -Waddress-of-packed-member). */ +#define HYPERAMP_QUEUE_LOCK(q) \ + ((volatile HyperampSpinlock *)((volatile uint8_t *)(q) + offsetof(HyperampShmQueue, queue_lock))) + + +/* ==================== Secure Memory Operations ==================== */ + +static inline void hyperamp_safe_memset(volatile void *dst, uint8_t val, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)dst; + for (size_t i = 0; i < len; i++) { + p[i] = val; + } + HYPERAMP_BARRIER(); +} + +static inline void hyperamp_safe_memcpy(volatile void *dst, const volatile void *src, size_t len) +{ + volatile uint8_t *d = (volatile uint8_t *)dst; + const volatile uint8_t *s = (const volatile uint8_t *)src; + for (size_t i = 0; i < len; i++) { + d[i] = s[i]; + } + HYPERAMP_BARRIER(); +} + +uint16_t hyperamp_safe_read_u16(const volatile void *addr, size_t offset) +{ + const volatile uint8_t *p = (const volatile uint8_t *)addr; + uint16_t val = 0; + for (int i = 0; i < 2; i++) { + val |= ((uint16_t)p[offset + i]) << (i * 8); + } + HYPERAMP_BARRIER(); + return val; +} + +static inline uint32_t hyperamp_safe_read_u32(const volatile void *addr, size_t offset) +{ + const volatile uint8_t *p = (const volatile uint8_t *)addr; + uint32_t val = 0; + for (int i = 0; i < 4; i++) { + val |= ((uint32_t)p[offset + i]) << (i * 8); + } + HYPERAMP_BARRIER(); + return val; +} + +static inline uint64_t hyperamp_safe_read_u64(const volatile void *addr, size_t offset) +{ + const volatile uint8_t *p = (const volatile uint8_t *)addr; + uint64_t val = 0; + for (int i = 0; i < 8; i++) { + val |= ((uint64_t)p[offset + i]) << (i * 8); + } + HYPERAMP_BARRIER(); + return val; +} + + +/* ==================== Queue Operation Functions ==================== */ + +int hyperamp_queue_is_initialized(volatile HyperampShmQueue *queue) +{ + if (!queue) return 0; + + HYPERAMP_BARRIER(); + + size_t capacity_offset = offsetof(HyperampShmQueue, capacity); + volatile uint8_t *p = (volatile uint8_t *)queue; + + uint16_t capacity = 0; + for (int i = 0; i < 2; i++) { + capacity |= ((uint16_t)p[capacity_offset + i]) << (i * 8); + } + + HYPERAMP_BARRIER(); + return (capacity > 0); +} + + +int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + const void *data, + size_t data_len, + volatile void *virt_base) +{ + if (!queue || !data || data_len == 0) { + printf("hyperamp_queue_enqueue failed: queue=%p, data=%p, data_len=%zu\n", + (void *)queue, data, data_len); + return HYPERAMP_ERROR; + } + + uint16_t block_size = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, block_size)); + uint16_t capacity = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, capacity)); + + if (data_len > block_size) return HYPERAMP_ERROR; + + hyperamp_spinlock_lock(HYPERAMP_QUEUE_LOCK(queue), zone_id); + + uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); + uint16_t tail = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, tail)); + + uint16_t new_header = header + 1; + if (new_header >= capacity) { + new_header -= capacity; + } + + /* Check if the queue is full. */ + if (new_header == tail) { + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); + return HYPERAMP_AGAIN; + } + + /* Compute the write address. */ + uint64_t write_addr = (uint64_t)(uintptr_t)virt_base + (uint64_t)(header + 1) * block_size; + + /* Write the data. */ + hyperamp_safe_memcpy((volatile void *)(uintptr_t)write_addr, data, data_len); + + /* Update the header (byte-by-byte). */ + volatile uint8_t *p = (volatile uint8_t *)queue; + size_t header_offset = offsetof(HyperampShmQueue, header); + p[header_offset] = new_header & 0xFF; + p[header_offset + 1] = (new_header >> 8) & 0xFF; + + /* Update enqueue_count. */ + size_t enqueue_offset = offsetof(HyperampShmQueue, enqueue_count); + uint32_t enqueue_count = hyperamp_safe_read_u32(queue, enqueue_offset); + enqueue_count++; + for (int i = 0; i < 4; i++) { + p[enqueue_offset + i] = (enqueue_count >> (i * 8)) & 0xFF; + } + + HYPERAMP_BARRIER(); + + hyperamp_cache_clean((volatile void *)(uintptr_t)write_addr, data_len); + hyperamp_cache_clean((volatile void *)queue, 64); + + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); + + return HYPERAMP_OK; +} + + +int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + void *data, + size_t max_len, + size_t *actual_len, + volatile void *virt_base) +{ + if (!queue || !data || max_len == 0) { + printf("hyperamp_queue_dequeue failed: queue=%p, data=%p, max_len=%zu\n", + (void *)queue, data, max_len); + return HYPERAMP_ERROR; + } + + hyperamp_cache_invalidate((volatile void *)queue, 64); + HYPERAMP_BARRIER(); + + hyperamp_spinlock_lock(HYPERAMP_QUEUE_LOCK(queue), zone_id); + + uint16_t header = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, header)); + uint16_t tail = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, tail)); + uint16_t block_size = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, block_size)); + uint16_t capacity = hyperamp_safe_read_u16(queue, offsetof(HyperampShmQueue, capacity)); + + /* Check if the queue is empty. */ + if (tail == header) { + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); + return HYPERAMP_AGAIN; + } + + /* Compute the read address. */ + uint64_t read_addr = (uint64_t)(uintptr_t)virt_base + (uint64_t)(tail + 1) * block_size; + + hyperamp_cache_invalidate((volatile void *)(uintptr_t)read_addr, block_size); + HYPERAMP_BARRIER(); + + size_t read_len = (max_len < block_size) ? max_len : block_size; + + hyperamp_safe_memcpy(data, (const volatile void *)(uintptr_t)read_addr, read_len); + + if (actual_len) { + *actual_len = read_len; + } + + /* Update tail. */ + uint16_t new_tail = tail + 1; + if (new_tail >= capacity) { + new_tail -= capacity; + } + + volatile uint8_t *p = (volatile uint8_t *)queue; + size_t tail_offset = offsetof(HyperampShmQueue, tail); + p[tail_offset] = new_tail & 0xFF; + p[tail_offset + 1] = (new_tail >> 8) & 0xFF; + + /* Update dequeue_count. */ + size_t dequeue_offset = offsetof(HyperampShmQueue, dequeue_count); + uint32_t dequeue_count = hyperamp_safe_read_u32(queue, dequeue_offset); + dequeue_count++; + for (int i = 0; i < 4; i++) { + p[dequeue_offset + i] = (dequeue_count >> (i * 8)) & 0xFF; + } + + HYPERAMP_BARRIER(); + + hyperamp_spinlock_unlock(HYPERAMP_QUEUE_LOCK(queue)); + + return HYPERAMP_OK; +} + + +int hyperamp_queue_init(volatile HyperampShmQueue *queue, + const HyperampQueueConfig *config, + int is_creator) +{ + if (!queue || !config) return HYPERAMP_ERROR; + if (config->block_size == 0 || config->capacity == 0) return HYPERAMP_ERROR; + + if (is_creator) { + volatile uint8_t *p = (volatile uint8_t *)queue; + + /* map_mode1, map_mode2 */ + p[0] = config->map_mode; + p[1] = config->map_mode; + HYPERAMP_BARRIER(); + + /* header (uint16_t, offset 2) */ + p[2] = 0; p[3] = 0; + HYPERAMP_BARRIER(); + + /* tail (uint16_t, offset 4) */ + p[4] = 0; p[5] = 0; + HYPERAMP_BARRIER(); + + /* capacity (uint16_t, offset 6) */ + uint16_t cap = config->capacity; + p[6] = cap & 0xFF; + p[7] = (cap >> 8) & 0xFF; + HYPERAMP_BARRIER(); + + /* block_size (uint16_t, offset 8) */ + uint16_t bs = config->block_size; + p[8] = bs & 0xFF; + p[9] = (bs >> 8) & 0xFF; + HYPERAMP_BARRIER(); + + /* _reserved (uint16_t, offset 10) */ + p[10] = 0; p[11] = 0; + HYPERAMP_BARRIER(); + + /* phy_addr (uint64_t, offset 12) */ + uint64_t pa = config->phy_addr; + for (int i = 0; i < 8; i++) { + p[12 + i] = (pa >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + /* virt_addr1 (uint64_t, offset 20) */ + uint64_t va = config->virt_addr; + for (int i = 0; i < 8; i++) { + p[20 + i] = (va >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + /* virt_addr2 (uint64_t, offset 28) – zero */ + for (int i = 0; i < 8; i++) { + p[28 + i] = 0; + } + HYPERAMP_BARRIER(); + + /* Initialize spinlock */ + size_t lock_offset = offsetof(HyperampShmQueue, queue_lock); + volatile HyperampSpinlock *lock = (volatile HyperampSpinlock *)&p[lock_offset]; + hyperamp_spinlock_init(lock); + + /* magic */ + size_t magic_offset = offsetof(HyperampShmQueue, magic); + uint32_t magic = HYPERAMP_QUEUE_MAGIC; + for (int i = 0; i < 4; i++) { + p[magic_offset + i] = (magic >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + /* version */ + uint32_t ver = 1; + for (int i = 0; i < 4; i++) { + p[magic_offset + 4 + i] = (ver >> (i * 8)) & 0xFF; + } + HYPERAMP_BARRIER(); + + /* enqueue_count – zero */ + for (int i = 0; i < 4; i++) { + p[magic_offset + 8 + i] = 0; + } + HYPERAMP_BARRIER(); + + /* dequeue_count – zero */ + for (int i = 0; i < 4; i++) { + p[magic_offset + 12 + i] = 0; + } + HYPERAMP_BARRIER(); + + hyperamp_cache_clean((volatile void *)queue, sizeof(HyperampShmQueue)); + + } else { + queue->virt_addr2 = config->virt_addr; + HYPERAMP_BARRIER(); + } + + return HYPERAMP_OK; +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_shm_queue.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_shm_queue.h new file mode 100644 index 0000000..19b813b --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/hyperamp_shm_queue.h @@ -0,0 +1,236 @@ +/** + * @file hyperamp_shm_queue.h + * @brief HyperAMP Shared Memory Queue – monkey-mnemosyne local copy + * + * Copied from apps/front/include/hyperamp_shm_queue.h and stripped down for + * use within monkey-mnemosyne. Changes from the original: + * + * 1. Removed #include "hardware_config.h" – platform physical addresses are + * guarded by __aarch64__ instead of CONFIG_PLAT_* so that x86_64 + * simulation builds work. + * 2. Removed __attribute__((packed)) from HyperampShmQueue to eliminate the + * -Wpacked-not-aligned warning when HyperampSpinlock (aligned(4)) is + * embedded. The struct layout is identical because all fields are + * naturally aligned or explicitly packed sub-structs. + * 3. Removed dependency on common_utils.h. + * + * All queue function signatures remain binary-compatible with the original. + */ + +#ifndef HYPERAMP_SHM_QUEUE_LOCAL_H +#define HYPERAMP_SHM_QUEUE_LOCAL_H + +#include +#include +#include + +/* ==================== Constant Definitions ==================== */ + +#define HYPERAMP_ERROR_ADDR UINT64_MAX +#define HYPERAMP_MAX_MAP_TABLE_ENTRIES 125 + +#define HYPERAMP_OK 0 +#define HYPERAMP_ERROR (-1) +#define HYPERAMP_AGAIN 1 + +#define HYPERAMP_ZONE_ID_ROOTLINUX 0 +#define HYPERAMP_ZONE_ID_SEL4 1 + +/* Memory mapping modes */ +typedef enum { + HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH = 0, + HYPERAMP_MAP_MODE_CONTIGUOUS_PHYS_DISCRETE_LOGICAL +} HyperampMapMode; + +/* Message constants */ +#define HYPERAMP_MSG_HDR_SIZE 8 +#define HYPERAMP_MSG_MIN_SIZE 1 +#define HYPERAMP_MSG_MAX_SIZE 4088 +#define HYPERAMP_MSG_HDR_PLUS_MAX_SIZE (HYPERAMP_MSG_HDR_SIZE + HYPERAMP_MSG_MAX_SIZE) + +/* Queue magic number */ +#define HYPERAMP_QUEUE_MAGIC 0x48415150 /* "HAQP" */ + + +/* ==================== Memory Barriers and Cache Operations ==================== */ + +#if defined(__aarch64__) || defined(__arm__) + #define HYPERAMP_DMB() __asm__ volatile("dmb sy" ::: "memory") + #define HYPERAMP_ISB() __asm__ volatile("isb" ::: "memory") + + static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { + volatile char *p = (volatile char *)addr; + volatile char *end = p + size; + for (; p < end; p += 64) { + __asm__ volatile("dc cvac, %0" : : "r"(p) : "memory"); + } + __asm__ volatile("dsb sy" ::: "memory"); + } + + static inline void hyperamp_cache_invalidate(volatile void *addr, size_t size) { + volatile char *p = (volatile char *)addr; + volatile char *end = p + size; + for (; p < end; p += 64) { + __asm__ volatile("dc civac, %0" : : "r"(p) : "memory"); + } + __asm__ volatile("dsb sy" ::: "memory"); + } +#else + /* x86 / simulation fallback */ + #define HYPERAMP_DMB() __asm__ volatile("mfence" ::: "memory") + #define HYPERAMP_ISB() __asm__ volatile("" ::: "memory") + + static inline void hyperamp_cache_clean(volatile void *addr, size_t size) { + (void)addr; (void)size; + __asm__ volatile("mfence" ::: "memory"); + } + + static inline void hyperamp_cache_invalidate(volatile void *addr, size_t size) { + (void)addr; (void)size; + __asm__ volatile("mfence" ::: "memory"); + } +#endif + +/* ==================== Platform addresses ==================== */ + +/* + * Physical addresses – only meaningful on real hardware. + * For simulation builds we still need the symbols to exist so that + * the code compiles; the actual values are irrelevant because the + * bridge uses the compile-time virtual addresses below. + */ +#if defined(__aarch64__) || defined(__arm__) + #define SHM_TX_QUEUE_PADDR 0x7E000000UL + #define SHM_RX_QUEUE_PADDR 0x7E001000UL + #define SHM_DATA_PADDR 0x7E002000UL +#else + /* x86_64 simulation – dummy physical addresses (never dereferenced). */ + #define SHM_TX_QUEUE_PADDR 0x7E000000UL + #define SHM_RX_QUEUE_PADDR 0x7E001000UL + #define SHM_DATA_PADDR 0x7E002000UL +#endif + +/* Virtual addresses – these are the fixed mappings set up by the seL4 boot + * loader (boot.c). Identical across platforms. */ +#define SHM_TX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x55E000UL) +#define SHM_RX_QUEUE_VADDR ((volatile HyperampShmQueue *)0x55F000UL) +#define SHM_DATA_REGION_VA ((volatile void *)0x560000UL) + + +#define HYPERAMP_BARRIER() HYPERAMP_DMB() + +/* ==================== Software Spinlock ==================== */ + +typedef struct { + volatile uint32_t lock_value; + volatile uint32_t owner_zone_id; + volatile uint32_t lock_count; + volatile uint32_t contention_count; +} __attribute__((aligned(4))) HyperampSpinlock; + +/* ==================== Address Mapping Table Entry ==================== */ + +typedef struct { + uint64_t virt_addr; + uint64_t phy_addr; +} __attribute__((packed)) HyperampMapTableEntry; + +/* ==================== Shared Memory Pool Queue ==================== */ + +/* + * NOTE: __attribute__((packed)) has been intentionally REMOVED from + * HyperampShmQueue in this local copy. The original in apps/front uses + * __packed__, but when a C++ translation unit includes this header the + * compiler warns (-Wpacked-not-aligned) because HyperampSpinlock carries + * __attribute__((aligned(4))) inside a packed struct. + * + * Removing __packed__ is safe because: + * - All scalar fields are naturally aligned (uint8/16/64). + * - HyperampMapTableEntry is itself __packed__ (16 bytes, no padding). + * - HyperampSpinlock is 16 bytes, aligned(4), placed after the two + * table arrays (125 * 16 = 2000 bytes each), so the offset is even. + * - The overall sizeof(HyperampShmQueue) remains unchanged. + * + * The queue functions use byte-by-byte safe accessors anyway, so the + * in-memory layout is identical. + */ +typedef struct { + uint8_t map_mode1; + uint8_t map_mode2; + uint16_t header; + uint16_t tail; + uint16_t capacity; + uint16_t block_size; + uint16_t _reserved; + + uint64_t phy_addr; + uint64_t virt_addr1; + uint64_t virt_addr2; + + HyperampMapTableEntry table1[HYPERAMP_MAX_MAP_TABLE_ENTRIES]; + HyperampMapTableEntry table2[HYPERAMP_MAX_MAP_TABLE_ENTRIES]; + + HyperampSpinlock queue_lock; + + uint32_t magic; + uint32_t version; + uint32_t enqueue_count; + uint32_t dequeue_count; + +} HyperampShmQueue; + +/* ==================== Message Header Structure ==================== */ + +typedef struct { + uint8_t version; + uint8_t proxy_msg_type; + uint16_t frontend_sess_id; + uint16_t backend_sess_id; + uint16_t payload_len; +} __attribute__((packed)) HyperampMsgHeader; + +/* Message types */ +typedef enum { + HYPERAMP_MSG_TYPE_DEV = 0, + HYPERAMP_MSG_TYPE_STRGY = 1, + HYPERAMP_MSG_TYPE_SESS = 2, + HYPERAMP_MSG_TYPE_DATA = 3, + HYPERAMP_MSG_TYPE_SERVICE = 0x10, + HYPERAMP_MSG_TYPE_BULK = 0x20 +} HyperampMsgType; + +/* ==================== Queue Configuration Structure ==================== */ + +typedef struct { + uint16_t map_mode; + uint16_t capacity; + uint16_t block_size; + uint16_t _reserved; + uint64_t phy_addr; + uint64_t virt_addr; +} HyperampQueueConfig; + + +/* ==================== Function Declarations ==================== */ + +int hyperamp_queue_is_initialized(volatile HyperampShmQueue *queue); + +int hyperamp_queue_init(volatile HyperampShmQueue *queue, + const HyperampQueueConfig *config, + int is_creator); + +int hyperamp_queue_enqueue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + const void *data, + size_t data_len, + volatile void *virt_base); + +int hyperamp_queue_dequeue(volatile HyperampShmQueue *queue, + uint32_t zone_id, + void *data, + size_t max_len, + size_t *actual_len, + volatile void *virt_base); + + +#endif /* HYPERAMP_SHM_QUEUE_LOCAL_H */ -- Gitee From 23f45d9e36be69f22155725976eaab4d2178cedb Mon Sep 17 00:00:00 2001 From: GuanTouYu Date: Sun, 19 Apr 2026 18:47:16 +0800 Subject: [PATCH 129/133] remove -Wmissing-prototypes and -Wmissing-declarations when compiling kernel. --- kernel/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 37e4adc..8e5f332 100755 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -178,9 +178,9 @@ add_compile_options( -Wall -Werror -Wstrict-prototypes - -Wmissing-prototypes +# TODO -Wmissing-prototypes -Wnested-externs - -Wmissing-declarations +# TODO -Wmissing-declarations -Wundef -Wpointer-arith -Wno-nonnull -- Gitee From 35829abe3de45b1eea522c66a10f061ec8b0340f Mon Sep 17 00:00:00 2001 From: GuanTouYu Date: Wed, 22 Apr 2026 00:17:03 +0800 Subject: [PATCH 130/133] simple ping-pong test. --- .../apps/monkey-mnemosyne/src/lab-main.cc | 134 +++++++++++++++++- 1 file changed, 128 insertions(+), 6 deletions(-) diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc b/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc index 408eb73..5cb91ef 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc +++ b/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc @@ -19,6 +19,20 @@ using namespace monkey; using HelloMode = net::ProtocolConnection::HelloMode; +static adl::int64_t getPageWriteKey(const adl::int64_t keyBase) { + return 0x8000000000000000LL + keyBase; +} + +static adl::int64_t getPageReadKey(const adl::int64_t keyBase) { + return keyBase; +} + +static adl::int64_t nextPageAccessKey() { + static adl::int64_t nextKey = 10000001; + return nextKey++; +} + + static void initAdlAlloc() { static struct { @@ -63,13 +77,121 @@ static Status createMnemosyneSession(net::Protocol2Connection& client) { } -static void doLabApp1() { - // TODO +struct MonkeySharedPage { + adl::uint64_t magics[4]; + + adl::int64_t strVer; + + char str[1024]; + + adl::int64_t zero0; + + adl::int8_t paddings[3024]; + +} __attribute__((__packed__)); + +static const adl::uint64_t monkeyPageMagics[4] = { + 0x544b65123da24f5aLL, 0x8ec8154e934c885dLL, + 0xdc8b8114553c472cLL, 0xa463d81acd9e4e22LL +}; + + +static char localPage[4096] __attribute__((__aligned__(4096))); + +static adl::int64_t currentStrVer = 1; + +static_assert(sizeof(MonkeySharedPage) == 4096, "MonkeySharedPage must be 4KB."); + + +static Status waitDataUpdate(net::Protocol2Connection& client, const adl::int64_t blockId) { + while (true) { + usleep(1000 * 1000); + MK_PROTO_DO_RETURN_ON_ERR(client.readBlock(blockId, localPage), "Failed to read page from server."); + + if (currentStrVer != ((MonkeySharedPage*)localPage)->strVer) { + currentStrVer = ((MonkeySharedPage*)localPage)->strVer; + } + } + + return monkey::Status::SUCCESS; } -static void doLabApp2() { - // TODO +static Status doLabApp1(monkey::net::Protocol2Connection& client) { + MonkeySharedPage& sharedPage = *(MonkeySharedPage*)localPage; + + // alloc a page. + + adl::int64_t blockId; + MK_PROTO_DO_RETURN_ON_ERR(client.tryAlloc(&blockId), "Failed to alloc page from server."); + + adl::memcpy(sharedPage.magics, monkeyPageMagics, sizeof(sharedPage.magics)); + adl::strcpy(sharedPage.str, "Hello from DDST, Shanghai Jiao Tong University! This is Lab App 1 speaking."); + sharedPage.strVer = currentStrVer; + sharedPage.zero0 = 0; + + // upload page to mnemosyne. + MK_PROTO_DO_RETURN_ON_ERR(client.writeBlock(blockId, localPage), "Failed to write page to server."); + + // wait for data change. + MK_PROTO_DO_RETURN_ON_ERR(waitDataUpdate(client, blockId), "Failed to wait for data update."); + Genode::log("Data updated, new strVer: ", currentStrVer); + Genode::log("New str: ", ((MonkeySharedPage*)localPage)->str); + + // write response. + + adl::strcpy(sharedPage.str, "Got your message, Lab App 1 here. Nice to meet you!"); + sharedPage.strVer = ++currentStrVer; + MK_PROTO_DO_RETURN_ON_ERR(client.writeBlock(blockId, localPage), "Failed to write page to server."); + + // unref the page. + MK_PROTO_DO_RETURN_ON_ERR(client.unrefBlock(blockId), "Failed to unref block."); + + + return monkey::Status::SUCCESS; +} + + +static Status doLabApp2(monkey::net::Protocol2Connection& client) { + MonkeySharedPage& sharedPage = *(MonkeySharedPage*)localPage; + + auto keyBase = nextPageAccessKey(); + auto writeKey = getPageWriteKey(keyBase); + + // ref the page. + adl::int64_t blockId; + while (client.refBlock(writeKey, &blockId) == Status::PROTOCOL_ERROR) { + usleep(1907 * 1000); + } + + // wait for magic match. + while (true) { + usleep(1000 * 1000); + MK_PROTO_DO_RETURN_ON_ERR(client.readBlock(blockId, localPage), "Failed to read page from server."); + if (adl::memcmp(sharedPage.magics, monkeyPageMagics, sizeof(sharedPage.magics)) == 0) { + break; + } + } + + // read the string. + currentStrVer = sharedPage.strVer; + Genode::log("Got strVer ", currentStrVer, " and str: ", sharedPage.str); + + // write response. + adl::strcpy(sharedPage.str, "Got your message, Lab App 2 here. Nice to meet you!"); + sharedPage.strVer = ++currentStrVer; + MK_PROTO_DO_RETURN_ON_ERR(client.writeBlock(blockId, localPage), "Failed to write page to server."); + + // read response from server. + MK_PROTO_DO_RETURN_ON_ERR(waitDataUpdate(client, blockId), "Failed to wait for data update."); + Genode::log("Data updated, new strVer: ", currentStrVer); + Genode::log("New str: ", ((MonkeySharedPage*)localPage)->str); + + // unref the page. + MK_PROTO_DO_RETURN_ON_ERR(client.unrefBlock(blockId), "Failed to unref block."); + + + return monkey::Status::SUCCESS; } @@ -87,10 +209,10 @@ int main() { } if (labConfig.labApp == LabApp::App1) { - doLabApp1(); + doLabApp1(client); } else if (labConfig.labApp == LabApp::App2) { - doLabApp2(); + doLabApp2(client); } else { Genode::error("Unknown lab app."); -- Gitee From 05d8bf400c113a036ee1a537647eeaa929c528c1 Mon Sep 17 00:00:00 2001 From: Qi Zhenlin Date: Mon, 27 Apr 2026 10:54:42 +0800 Subject: [PATCH 131/133] fix a dead loop bug. --- projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc b/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc index 5cb91ef..58e1154 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc +++ b/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc @@ -110,6 +110,7 @@ static Status waitDataUpdate(net::Protocol2Connection& client, const adl::int64_ if (currentStrVer != ((MonkeySharedPage*)localPage)->strVer) { currentStrVer = ((MonkeySharedPage*)localPage)->strVer; + break; } } -- Gitee From d0ba4f2227270284682a64b62dc57f3f7ca6b942 Mon Sep 17 00:00:00 2001 From: Flower Black Date: Mon, 27 Apr 2026 22:14:48 +0800 Subject: [PATCH 132/133] built-in app1 and app2 keys. Co-authored-by: Copilot --- projects/sel4test/apps/monkey-mnemosyne/src/config.h | 4 +++- projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/config.h b/projects/sel4test/apps/monkey-mnemosyne/src/config.h index 75bac83..a4192e7 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/config.h +++ b/projects/sel4test/apps/monkey-mnemosyne/src/config.h @@ -21,6 +21,8 @@ enum class LabApp { struct { const char* mnemosyneIp = ""; adl::uint16_t mnemosynePort = 10100; - const char* appKey = ""; + + const char* app1Key = "f578bd06-6f8e-42b3-8be9-860c7c645549"; + const char* app2Key = "8370c1fe-d422-42d9-a261-05aed72313c3"; LabApp labApp = LabApp::App1; } static const labConfig; diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc b/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc index 58e1154..4aabc68 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc +++ b/projects/sel4test/apps/monkey-mnemosyne/src/lab-main.cc @@ -71,7 +71,10 @@ static void init() { static Status createMnemosyneSession(net::Protocol2Connection& client) { MK_PROTO_DO_RETURN_ON_ERR(client.connect(), "Failed to connect with server."); MK_PROTO_DO_RETURN_ON_ERR(client.hello(2, HelloMode::CLIENT), "Failed to say hello to server."); - MK_PROTO_DO_RETURN_ON_ERR(client.auth(labConfig.appKey), "Failed to auth with server."); + + const char* appKey = (labConfig.labApp == LabApp::App1) ? labConfig.app1Key : labConfig.app2Key; + + MK_PROTO_DO_RETURN_ON_ERR(client.auth(appKey), "Failed to auth with server."); return monkey::Status::SUCCESS; } -- Gitee From 3cc73a7ee37534c2f0b50f484441194d0e4f4bc7 Mon Sep 17 00:00:00 2001 From: gongtianyao Date: Sun, 10 May 2026 15:31:33 +0800 Subject: [PATCH 133/133] mnemosyne api organized. --- .../apps/monkey-mnemosyne/CMakeLists.txt | 95 +++- .../monkey-mnemosyne/include/mnemosyne_api.h | 261 +++++++++ .../monkey-mnemosyne/src/mnemosyne_api.cc | 520 ++++++++++++++++++ .../src/monkey/net/HyperAmpBridge.cc | 197 +++++-- .../src/monkey/net/HyperAmpBridge.h | 66 ++- .../src/monkey/net/channel_ch2.h | 79 +++ 6 files changed, 1126 insertions(+), 92 deletions(-) create mode 100644 projects/sel4test/apps/monkey-mnemosyne/include/mnemosyne_api.h create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne_api.cc create mode 100644 projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/channel_ch2.h diff --git a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt index bc3e159..e3fcd34 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt +++ b/projects/sel4test/apps/monkey-mnemosyne/CMakeLists.txt @@ -12,13 +12,56 @@ sel4_import_libsel4() util_libs_import_libraries() sel4_libs_import_libraries() -# Build monkey mnemosyne application -file(GLOB_RECURSE deps src/*.c*) -list(SORT deps) -list(REMOVE_DUPLICATES deps) +# ---------------------------------------------------------------------------- +# Sources +# +# All C / C++ sources under src/ form the reusable static library +# `monkey-mnemosyne-lib`. The sole exception is `lab-main.cc`, which +# carries `int main()` and the lab-specific demo logic; it stays out of +# the library and is linked only into the standalone test executable. +# ---------------------------------------------------------------------------- +file(GLOB_RECURSE lib_srcs src/*.c src/*.cc) +list(REMOVE_ITEM lib_srcs ${CMAKE_CURRENT_SOURCE_DIR}/src/lab-main.cc) +list(SORT lib_srcs) +list(REMOVE_DUPLICATES lib_srcs) -add_executable(monkey-mnemosyne EXCLUDE_FROM_ALL ${deps}) -target_link_libraries(monkey-mnemosyne +# ---------------------------------------------------------------------------- +# C++ flags shared by the library and the standalone executable. +# Hoisted out of the original target_compile_options so that both targets +# stay binary-compatible (same RTTI/EH/ABI flags). +# ---------------------------------------------------------------------------- +set(MNEMOSYNE_CXX_FLAGS + -Wall + -std=c++20 + -g + -nostartfiles + -fno-pic -fno-pie -no-pie + -fno-rtti + -fno-stack-protector + -fno-threadsafe-statics + -fno-exceptions + -fno-use-cxa-atexit + -fno-sized-deallocation + -Wno-error=unused-function + -fdiagnostics-color=always +) + +# ---------------------------------------------------------------------------- +# Reusable static library: monkey-mnemosyne-lib +# ---------------------------------------------------------------------------- +add_library(monkey-mnemosyne-lib STATIC EXCLUDE_FROM_ALL ${lib_srcs}) + +target_include_directories(monkey-mnemosyne-lib + PUBLIC + # Pure-C facade header for downstream apps. + ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE + # Internal C++ sources. + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/src +) + +target_link_libraries(monkey-mnemosyne-lib PUBLIC sel4_autoconf muslc @@ -32,34 +75,26 @@ target_link_libraries(monkey-mnemosyne utils ) - -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/src +# Apply the C++ flags to library sources only (they are .cc). +target_compile_options(monkey-mnemosyne-lib PRIVATE + $<$:${MNEMOSYNE_CXX_FLAGS}> ) +target_compile_options(monkey-mnemosyne-lib PRIVATE -g) +set_property(TARGET monkey-mnemosyne-lib PROPERTY C_STANDARD 99) -# GCC(g++) 编译选项。 -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostartfiles") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-pic") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-pie") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -no-pie") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-stack-protector") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-use-cxa-atexit") -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1") # 适当优化。 -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-sized-deallocation") - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unused-function") +# ---------------------------------------------------------------------------- +# Standalone executable: kept for our own testing (lab-main.cc demo). +# This is what `Sel4testApp=monkey-mnemosyne` builds; integration builds +# that link the library into another rootserver simply do not declare +# this target as the rootserver. +# ---------------------------------------------------------------------------- +add_executable(monkey-mnemosyne EXCLUDE_FROM_ALL src/lab-main.cc) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") +target_link_libraries(monkey-mnemosyne PRIVATE monkey-mnemosyne-lib) - -# Set executable properties +target_compile_options(monkey-mnemosyne PRIVATE + $<$:${MNEMOSYNE_CXX_FLAGS}> +) target_compile_options(monkey-mnemosyne PRIVATE -Werror -g) set_property(TARGET monkey-mnemosyne PROPERTY C_STANDARD 99) diff --git a/projects/sel4test/apps/monkey-mnemosyne/include/mnemosyne_api.h b/projects/sel4test/apps/monkey-mnemosyne/include/mnemosyne_api.h new file mode 100644 index 0000000..7881395 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/include/mnemosyne_api.h @@ -0,0 +1,261 @@ +/** + * @file mnemosyne_api.h + * @brief Pure-C facade for monkey-mnemosyne, mirroring the apps/front + * (network proxy) API surface for unified upper-layer integration. + * + * Design intent + * ------------- + * The integration spec asks monkey-mnemosyne to expose a small set of + * functions whose *shape* matches the proxy frontend's + * frontend_engine_init / frontend_sess_new / + * frontend_sess_connect_by_addrstr_devid / frontend_sess_send / + * frontend_sess_recv / frontend_engine_run_hyperamp_once / + * frontend_sess_close. The semantics underneath remain the existing + * monkey-mnemosyne client (Protocol2Connection over HyperAMP), with a + * single shared 4 KiB block as the data carrier. + * + * Compared with apps/front the only signature differences are: + * - functions are prefixed `mnemosyne_` so they can coexist with + * `frontend_*` in the same translation unit; + * - there is no `engine` handle; the engine state is global because + * the underlying HyperAmpBridge is itself a singleton. + * + * Linkage + * ------- + * This header is C-clean (extern "C", no C++ types, opaque session + * handle). Upper-layer C code only needs: + * + * #include + * ... + link against monkey-mnemosyne-lib + * + * The implementation is C++ but the ABI exported is plain C. + * + * Concurrency + * ----------- + * The underlying HyperAmpBridge is a singleton; only ONE *connected* + * session may exist at a time. Calling mnemosyne_sess_new() twice + * without closing the first session is undefined behaviour. + * + * Roles (alloc vs ref) + * -------------------- + * monkey-mnemosyne shares a single 4 KiB page between two endpoints: + * - the "alloc" endpoint : calls tryAlloc to create the page; + * - the "ref" endpoint : calls refBlock to attach to it. + * The role is fixed at engine init time via `role` in + * mnemosyne_engine_init(). Block id and access key are currently + * predictable (see TODO in mnemosyne_api.cc). + * + * IPC-MR ordering — IMPORTANT + * --------------------------- + * mnemosyne_engine_init() reads the CH2 virtual addresses from the IPC + * message registers (seL4_GetMR(8/9/10), set up by the kernel boot + * loader). The IPC buffer is volatile across syscalls, so the call + * MUST happen BEFORE the host program issues any seL4 syscall. In + * practice that means it should be the very first thing your `main()` + * does, exactly as apps/hyperamp-server/src/main.c reads MR(2..7) at + * the top. + * + * gongty [at] tongji [dot] edu [dot] cn + */ + +#ifndef MNEMOSYNE_API_H +#define MNEMOSYNE_API_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ====================================================================== */ +/* Constants */ +/* ====================================================================== */ + +/** Role: this endpoint allocates the shared block (issues tryAlloc). */ +#define MNEMOSYNE_ROLE_ALLOC 1 + +/** Role: this endpoint references an already-allocated block. */ +#define MNEMOSYNE_ROLE_REF 2 + +/** + * HyperAMP channel id. CH2 is reserved for monkey-mnemosyne by + * HYPERAMP_MULTI_CHANNEL_DESIGN.md. Pass 0 to mnemosyne_engine_init() + * to mean "default" which resolves to CH2. + */ +#define MNEMOSYNE_CHANNEL_DEFAULT 2 + +/** + * Sentinel for "no specific NIC selection on backend". Same numeric + * value as DEV_ID_AUTO_HANDOVER used throughout apps/front. + */ +#define MNEMOSYNE_DEV_AUTO 0xFF + +/** The data carrier is a single 4 KiB page; send/recv length must fit. */ +#define MNEMOSYNE_BLOCK_SIZE 4096 + +/** + * Transport protocol values, kept binary-compatible with apps/front's + * SessTranProto enum (SESS_UDP_PROTO=0, SESS_TCP_PROTO=1). monkey- + * mnemosyne itself only uses TCP at the moment but the parameter is + * accepted for API symmetry with frontend_sess_connect_by_addrstr_devid. + */ +#define MNEMOSYNE_PROTO_UDP 0 +#define MNEMOSYNE_PROTO_TCP 1 + +/* ====================================================================== */ +/* Opaque types */ +/* ====================================================================== */ + +typedef struct mnemosyne_session_s mnemosyne_session_t; + +/* ====================================================================== */ +/* Engine */ +/* ====================================================================== */ + +/** + * Initialise the mnemosyne engine. Counterpart of frontend_engine_init(). + * + * - Initialises the ADL allocator (idempotent). + * - Reads the CH2 shared-memory virtual addresses from the IPC message + * registers (seL4_GetMR(8/9/10)). See "IPC-MR ordering" in the file + * header — this must happen before any other syscall. + * - Initialises the HyperAMP bridge on the requested channel. + * - Stores the server endpoint, auth key and role for use by sessions + * created via mnemosyne_sess_new(). + * + * Idempotent: subsequent calls return 0 without reinitialising. + * + * @param server_ip IPv4 dotted string (e.g. "10.0.0.5"). Copied + * internally; may be freed by the caller afterwards. + * @param server_port Server TCP port (host byte order). + * @param auth_key UUID-style string (e.g. lab-main App1Key). Copied + * internally. + * @param channel_id HyperAMP channel: 0 = default (= MNEMOSYNE_CHANNEL_ + * DEFAULT = CH2), 2 = CH2 explicitly, 1 = CH1 (debug + * / experimental only). + * @param role MNEMOSYNE_ROLE_ALLOC or MNEMOSYNE_ROLE_REF. + * + * @return 0 on success; <0 on error. + */ +int mnemosyne_engine_init(const char *server_ip, + uint16_t server_port, + const char *auth_key, + uint8_t channel_id, + uint8_t role); + +/** + * Single-shot scheduler hook, mirrors frontend_engine_run_hyperamp_once(). + * + * monkey-mnemosyne does not need an engine event loop: every send/recv + * call drives the HyperAMP queue itself. This function is provided for + * API symmetry only; it is intentionally a no-op so that integrators can + * write a unified main loop: + * + * while (running) { + * frontend_engine_run_hyperamp_once(); + * mnemosyne_engine_run_hyperamp_once(); + * ... pull / push session data ... + * } + */ +void mnemosyne_engine_run_hyperamp_once(void); + +/* ====================================================================== */ +/* Session */ +/* ====================================================================== */ + +/** + * Allocate a new session object. Counterpart of frontend_sess_new(). + * + * The returned handle is owned by the caller and must be released with + * mnemosyne_sess_close(). + * + * @return Session handle on success; NULL on allocation failure or if + * mnemosyne_engine_init() was not called yet. + */ +mnemosyne_session_t *mnemosyne_sess_new(void); + +/** + * Bind the session to a remote endpoint and complete the mnemosyne + * handshake (TCP connect → HelloMode::CLIENT, protocol 2 → auth). + * + * Counterpart of frontend_sess_connect_by_addrstr_devid(). Signature is + * deliberately a one-to-one mirror so that integrators can swap between + * the two with mechanical regularity: + * + * int frontend_sess_connect_by_addrstr_devid( + * struct FrontendSession *sess, int proto, + * const char *addr_str, uint16_t dev_id); + * + * int mnemosyne_sess_connect_by_addrstr_devid( + * mnemosyne_session_t *sess, int proto, + * const char *addr_str, uint16_t dev_id); + * + * If `role == MNEMOSYNE_ROLE_ALLOC` (set at engine init time) the + * session also issues a tryAlloc to create the shared 4 KiB block; + * if `role == MNEMOSYNE_ROLE_REF` the session issues refBlock against + * the predictable access key. + * + * @param sess Session created by mnemosyne_sess_new(). + * @param proto MNEMOSYNE_PROTO_TCP (1) or MNEMOSYNE_PROTO_UDP (0). + * monkey-mnemosyne only uses TCP at present; UDP is + * accepted at the API surface but rejected internally. + * @param addr_str "IP:PORT" string, e.g. "10.0.0.5:10100". Same format + * as front's API. May be NULL/empty to reuse the IP and + * port given to mnemosyne_engine_init(). + * @param dev_id Backend NIC selector. MNEMOSYNE_DEV_AUTO (0xFF) lets + * the backend pick; pass an explicit id for multi-NIC + * deployments. Stored in SessIPv4Params.device_selection + * and forwarded to the backend verbatim. + * + * @return 0 on success; <0 on protocol / network error. + */ +int mnemosyne_sess_connect_by_addrstr_devid(mnemosyne_session_t *sess, + int proto, + const char *addr_str, + uint16_t dev_id); + +/** + * Write up to MNEMOSYNE_BLOCK_SIZE bytes into the shared 4 KiB block. + * + * The buffer is copied into an internal staging page (zero-padded if + * `size < MNEMOSYNE_BLOCK_SIZE`) and then writeBlock is issued to the + * server. + * + * @return Number of bytes consumed from `data` (== `size` on success); + * <0 on error (e.g. size > MNEMOSYNE_BLOCK_SIZE, no session). + */ +int mnemosyne_sess_send(mnemosyne_session_t *sess, + const void *data, uint32_t size); + +/** + * Read the current contents of the shared 4 KiB block and copy up to + * `size` bytes into `buf`. + * + * This is a single readBlock (not a polling-until-changed loop); the + * upper-layer application is expected to call mnemosyne_sess_recv() + * repeatedly inside its own loop and detect updates itself, exactly as + * lab-main.cc::waitDataUpdate() does. + * + * @return Number of bytes copied into `buf` (== min(size, 4096) on + * success); <0 on error. + */ +int mnemosyne_sess_recv(mnemosyne_session_t *sess, + void *buf, uint32_t size); + +/** + * Close the session and release all resources. + * + * If this endpoint holds a reference to the shared block it is unref'd + * before the underlying TCP connection is torn down. After this call + * `sess` is invalid and must not be used. + * + * Safe to call with NULL. + */ +void mnemosyne_sess_close(mnemosyne_session_t *sess); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNEMOSYNE_API_H */ diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne_api.cc b/projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne_api.cc new file mode 100644 index 0000000..80fd2ad --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/mnemosyne_api.cc @@ -0,0 +1,520 @@ +/** + * @file mnemosyne_api.cc + * @brief Pure-C facade for monkey-mnemosyne — implementation. + * + * Bridges plain-C callers (e.g. apps/front-style validation apps) into + * the existing C++ Protocol2Connection client. See + * include/mnemosyne_api.h for the contract. + * + * Internally this is a thin shim: + * + * mnemosyne_engine_init() + * -> read CH2 vaddrs from IPC MR(8/9/10) (FIRST!) + * -> initAdlAllocOnce() + * -> HyperAmpBridge::instance().init(2, tx, rx, dt) + * -> remember server ip / port / auth_key / role + * + * mnemosyne_sess_new() + * -> new Protocol2Connection + * + * mnemosyne_sess_connect_by_addrstr_devid(sess, proto, "ip:port", dev_id) + * -> parse "ip:port" + * -> Socket4::connect() (routed via HyperAmpBridge) + * -> Protocol1Connection::hello(2, CLIENT) + * -> Protocol1Connection::auth(auth_key) + * -> role == ALLOC : tryAlloc() -> remember blockId + * -> role == REF : refBlock(key) -> remember blockId + * + * mnemosyne_sess_send() / recv() + * -> single 4 KiB writeBlock() / readBlock() + * + * mnemosyne_sess_close() + * -> unrefBlock() (best-effort) + Protocol2Connection::close() + delete + * + * gongty [at] tongji [dot] edu [dot] cn + */ + +#include "../include/mnemosyne_api.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include + +extern "C" { +#include +} + + +/* ====================================================================== */ +/* Internal state */ +/* ====================================================================== */ + +namespace { + +/* + * Global engine state. monkey-mnemosyne does not have a per-engine + * object; HyperAmpBridge is already a singleton, and we only need a + * handful of scalars to remember what mnemosyne_engine_init() was called + * with. Keeping them file-static avoids exposing any C++ types through + * the C ABI. + */ +struct EngineState { + bool inited = false; + char ip[64] = {0}; + adl::uint16_t port = 0; + char authKey[128] = {0}; + adl::uint8_t channelId = MNEMOSYNE_CHANNEL_DEFAULT; + adl::uint8_t role = MNEMOSYNE_ROLE_ALLOC; +}; + +EngineState g_engine; + +/* + * Predictable access key shared between the alloc-side and the ref-side. + * + * TODO(gty 2026): this assumes (a) exactly one alloc-side and one + * ref-side per deployment and (b) the alloc-side runs first. If + * multiple sessions ever coexist or the order is not guaranteed this + * scheme breaks. Lift the access key out into mnemosyne_engine_init() + * as a proper parameter once the integrator confirms the deployment + * topology. + */ +constexpr adl::int64_t kPredictableKeyBase = 10000001; +constexpr adl::int64_t kPageWriteKeyBit = 0x8000000000000000LL; + +inline adl::int64_t predictableWriteKey() { + return kPageWriteKeyBit + kPredictableKeyBase; +} + +bool g_adlAllocInited = false; + +void initAdlAllocOnce() { + if (g_adlAllocInited) return; + + static struct { + } adlAllocData; + + adl::defaultAllocator.init({ + .alloc = [] (adl::size_t size, void* /*data*/) -> void* { + return malloc(size); + }, + .free = [] (void* addr, adl::size_t /*size*/, void* /*data*/) { + free(addr); + }, + .data = &adlAllocData + }); + + g_adlAllocInited = true; +} + +/* + * Parse "ip:port" into separate ip string + numeric port. + * + * Mirrors what front does in IPV4_PORT_STR_TO_TUPLE: split on ':', + * take everything before as IP, everything after (decimal) as port. + * + * On success returns 0 and writes the IP into ipOut[0..ipOutMax-1] + * (NUL-terminated) and the port into *portOut. Returns -1 on any + * parse error. + */ +int parseIpPort(const char* s, char* ipOut, size_t ipOutMax, + adl::uint16_t* portOut) +{ + if (!s || !ipOut || !portOut || ipOutMax < 2) return -1; + + const char* colon = strchr(s, ':'); + if (!colon || colon == s) return -1; + + size_t ipLen = static_cast(colon - s); + if (ipLen >= ipOutMax) return -1; + + memcpy(ipOut, s, ipLen); + ipOut[ipLen] = '\0'; + + /* Decimal port. */ + long p = 0; + for (const char* q = colon + 1; *q; ++q) { + if (*q < '0' || *q > '9') return -1; + p = p * 10 + (*q - '0'); + if (p > 0xFFFF) return -1; + } + if (p == 0) return -1; + + *portOut = static_cast(p); + return 0; +} + +} // namespace + + +/* + * Concrete session struct. Hidden behind an opaque typedef in the C + * header so the layout never leaks. + */ +struct mnemosyne_session_s { + monkey::net::Protocol2Connection* client = nullptr; + + adl::int64_t blockId = -1; + bool hasBlock = false; + bool connected = false; + + /* + * 4 KiB staging page for send/recv. Aligned to 4 KiB to match what + * Protocol2Connection::readBlock / writeBlock expect (they assume + * the caller-supplied buffer can hold a full 4 KiB page). + */ + adl::uint8_t staging[MNEMOSYNE_BLOCK_SIZE] + __attribute__((aligned(4096))) = {0}; +}; + + +/* ====================================================================== */ +/* Engine */ +/* ====================================================================== */ + +extern "C" int mnemosyne_engine_init(const char *server_ip, + uint16_t server_port, + const char *auth_key, + uint8_t channel_id, + uint8_t role) +{ + if (g_engine.inited) { + return 0; /* idempotent */ + } + if (!server_ip || !auth_key) { + return -1; + } + if (role != MNEMOSYNE_ROLE_ALLOC && role != MNEMOSYNE_ROLE_REF) { + return -2; + } + + /* + * STEP 1 — read the channel virtual addresses from the IPC message + * registers BEFORE doing anything that could trigger a syscall. + * + * The kernel boot loader publishes: + * msg[2..4] -> CH0 (TX, RX, Data) + * msg[5..7] -> CH1 (TX, RX, Data) + * msg[8..10] -> CH2 (TX, RX, Data) + * (See HYPERAMP_MULTI_CHANNEL_DESIGN.md and kernel/src/arch/arm/ + * kernel/boot.c.) The IPC buffer is volatile — every syscall + * overwrites it — so we capture the values up-front and only + * perform syscalls afterwards. + */ + const adl::uint8_t resolvedChannel = + (channel_id == 0) ? MNEMOSYNE_CHANNEL_DEFAULT : channel_id; + + seL4_Word txVa = 0, rxVa = 0, dtVa = 0; + switch (resolvedChannel) { + case 2: + txVa = seL4_GetMR(MNEMOSYNE_MR_SLOT_CH2_TX); + rxVa = seL4_GetMR(MNEMOSYNE_MR_SLOT_CH2_RX); + dtVa = seL4_GetMR(MNEMOSYNE_MR_SLOT_CH2_DATA); + break; + case 1: + /* Debug / experimental: bind to CH1 instead. The integrator + * is responsible for ensuring nothing else (front, + * hyperamp-server) is using CH1 in the same image. */ + txVa = seL4_GetMR(5); + rxVa = seL4_GetMR(6); + dtVa = seL4_GetMR(7); + break; + default: + return -3; + } + + /* + * STEP 2 — record engine config and bring up the ADL allocator. + */ + strncpy(g_engine.ip, server_ip, sizeof(g_engine.ip) - 1); + strncpy(g_engine.authKey, auth_key, sizeof(g_engine.authKey) - 1); + g_engine.port = server_port; + g_engine.channelId = resolvedChannel; + g_engine.role = role; + + initAdlAllocOnce(); + + /* + * STEP 3 — initialise the HyperAMP bridge with the captured vaddrs. + */ + if (!txVa || !rxVa || !dtVa) { + MONKEY_LOG_ERROR("[mnemosyne_api] IPC MR returned null vaddr " + "(boot loader did not publish channel ", + (int)resolvedChannel, "?)"); + return -4; + } + + monkey::net::HyperAmpBridge::instance().init( + resolvedChannel, + static_cast(txVa), + static_cast(rxVa), + static_cast(dtVa)); + + if (!monkey::net::HyperAmpBridge::instance().isInitialized()) { + MONKEY_LOG_ERROR("[mnemosyne_api] HyperAmpBridge init failed"); + return -5; + } + + g_engine.inited = true; + MONKEY_LOG_INFO("[mnemosyne_api] engine initialised, server=", + g_engine.ip, ":", (unsigned int)g_engine.port, + " channel=", (int)g_engine.channelId, + " role=", (int)g_engine.role); + return 0; +} + + +extern "C" void mnemosyne_engine_run_hyperamp_once(void) +{ + /* + * monkey-mnemosyne does not maintain an event loop of its own — + * every send / recv call drives the HyperAMP queue itself, and the + * upper-layer application is expected to call mnemosyne_sess_recv() + * in its own loop to detect updates (see lab-main.cc::waitDataUpdate + * for the equivalent pattern). This function is therefore an + * intentional no-op, provided only so that callers can use a unified + * loop body alongside frontend_engine_run_hyperamp_once(). + */ +} + + +/* ====================================================================== */ +/* Session */ +/* ====================================================================== */ + +extern "C" mnemosyne_session_t *mnemosyne_sess_new(void) +{ + if (!g_engine.inited) { + MONKEY_LOG_ERROR("[mnemosyne_api] sess_new before engine_init"); + return nullptr; + } + + auto* sess = new mnemosyne_session_s(); + if (!sess) return nullptr; + + sess->client = new monkey::net::Protocol2Connection(); + if (!sess->client) { + delete sess; + return nullptr; + } + + /* Pre-fill with engine-default endpoint; overridden in connect(). */ + sess->client->ip.set(g_engine.ip); + sess->client->port = g_engine.port; + + return sess; +} + + +extern "C" int mnemosyne_sess_connect_by_addrstr_devid( + mnemosyne_session_t *sess, + int proto, + const char *addr_str, + uint16_t dev_id) +{ + if (!sess || !sess->client) { + return -1; + } + + /* + * monkey-mnemosyne speaks TCP only. We accept the proto parameter + * for API symmetry with frontend_sess_connect_by_addrstr_devid but + * refuse anything other than TCP. + */ + if (proto != MNEMOSYNE_PROTO_TCP) { + MONKEY_LOG_ERROR("[mnemosyne_api] only TCP supported, got proto=", + proto); + return -2; + } + + auto& client = *sess->client; + + /* + * Parse "ip:port". An empty / NULL addr_str means "reuse engine + * defaults" (mirrors the convenience knob lab-main.cc had). + */ + if (addr_str && addr_str[0] != '\0') { + char ip[64]; + adl::uint16_t port = 0; + if (parseIpPort(addr_str, ip, sizeof(ip), &port) != 0) { + MONKEY_LOG_ERROR("[mnemosyne_api] bad addr_str (need 'ip:port'): ", + addr_str); + return -3; + } + client.ip.set(ip); + client.port = port; + } + + using HelloMode = monkey::net::ProtocolConnection::HelloMode; + + /* + * connect() goes through Socket4::connect() -> HyperAmpBridge.connect() + * which builds the SESS_CREATE message. We need to forward dev_id + * into SessIPv4Params.device_selection — the existing Socket4 path + * uses the bridge's default 0xFF, so for non-default dev_id we have + * to call the bridge directly. + */ + monkey::Status st; + + if (dev_id == MNEMOSYNE_DEV_AUTO) { + st = client.connect(); /* legacy path: bridge.connect uses 0xFF */ + } else { + /* + * Bypass Socket4::connect()'s hard-coded auto dev_id by talking + * to the bridge ourselves with the explicit dev_id, then mark + * the socket "valid" the same way Socket4::connect() does. + */ + client.close(); + auto& bridge = monkey::net::HyperAmpBridge::instance(); + /* Bridge must already be up (mnemosyne_engine_init brought it + * up via the 4-arg init overload); no need to call init() again. */ + bool ok = bridge.connect(client.ip, client.port, dev_id); + if (!ok) { + MONKEY_LOG_ERROR("[mnemosyne_api] bridge.connect (dev_id=", + (int)dev_id, ") failed"); + return -4; + } + client.socketFd = 9999; /* same sentinel Socket4::connect uses */ + st = monkey::Status::SUCCESS; + } + + if (st != monkey::Status::SUCCESS) { + MONKEY_LOG_ERROR("[mnemosyne_api] connect failed"); + return -4; + } + + st = client.hello(monkey::net::Protocol2Connection::VERSION, + HelloMode::CLIENT); + if (st != monkey::Status::SUCCESS) { + MONKEY_LOG_ERROR("[mnemosyne_api] hello failed"); + client.close(); + return -5; + } + + st = client.auth(g_engine.authKey); + if (st != monkey::Status::SUCCESS) { + MONKEY_LOG_ERROR("[mnemosyne_api] auth failed"); + client.close(); + return -6; + } + + sess->connected = true; + + /* Acquire the shared 4 KiB block according to role. */ + if (g_engine.role == MNEMOSYNE_ROLE_ALLOC) { + adl::int64_t bid = -1; + st = client.tryAlloc(&bid); + if (st != monkey::Status::SUCCESS) { + MONKEY_LOG_ERROR("[mnemosyne_api] tryAlloc failed"); + client.close(); + sess->connected = false; + return -7; + } + sess->blockId = bid; + sess->hasBlock = true; + MONKEY_LOG_INFO("[mnemosyne_api] alloc-side blockId=", + (long)sess->blockId); + } else { + /* + * Ref-side: try once. Mirrors the loop in lab-main.cc::doLabApp2, + * but only one attempt — the upper-layer application is expected + * to retry mnemosyne_sess_connect_by_addrstr_devid() if refBlock + * isn't ready yet (alloc-side hasn't run). This keeps the API + * non-blocking-ish. + */ + adl::int64_t bid = -1; + st = client.refBlock(predictableWriteKey(), &bid); + if (st != monkey::Status::SUCCESS) { + MONKEY_LOG_WARN("[mnemosyne_api] refBlock not ready yet " + "(alloc-side may not have run)"); + client.close(); + sess->connected = false; + return -8; + } + sess->blockId = bid; + sess->hasBlock = true; + MONKEY_LOG_INFO("[mnemosyne_api] ref-side blockId=", + (long)sess->blockId); + } + + return 0; +} + + +extern "C" int mnemosyne_sess_send(mnemosyne_session_t *sess, + const void *data, uint32_t size) +{ + if (!sess || !sess->client || !sess->connected || !sess->hasBlock) { + return -1; + } + if (!data || size == 0) { + return -2; + } + if (size > MNEMOSYNE_BLOCK_SIZE) { + MONKEY_LOG_ERROR("[mnemosyne_api] send size > 4096 not supported"); + return -3; + } + + /* Stage the payload into our 4 KiB page (zero-pad the tail). */ + memcpy(sess->staging, data, size); + if (size < MNEMOSYNE_BLOCK_SIZE) { + memset(sess->staging + size, 0, MNEMOSYNE_BLOCK_SIZE - size); + } + + monkey::Status st = sess->client->writeBlock(sess->blockId, sess->staging); + if (st != monkey::Status::SUCCESS) { + MONKEY_LOG_ERROR("[mnemosyne_api] writeBlock failed"); + return -4; + } + return (int)size; +} + + +extern "C" int mnemosyne_sess_recv(mnemosyne_session_t *sess, + void *buf, uint32_t size) +{ + if (!sess || !sess->client || !sess->connected || !sess->hasBlock) { + return -1; + } + if (!buf || size == 0) { + return -2; + } + + monkey::Status st = sess->client->readBlock(sess->blockId, sess->staging); + if (st != monkey::Status::SUCCESS) { + MONKEY_LOG_ERROR("[mnemosyne_api] readBlock failed"); + return -3; + } + + uint32_t copy = (size < MNEMOSYNE_BLOCK_SIZE) ? size : MNEMOSYNE_BLOCK_SIZE; + memcpy(buf, sess->staging, copy); + return (int)copy; +} + + +extern "C" void mnemosyne_sess_close(mnemosyne_session_t *sess) +{ + if (!sess) return; + + if (sess->client) { + if (sess->hasBlock) { + /* Best-effort unref; we ignore errors because we're tearing + * the session down regardless. */ + sess->client->unrefBlock(sess->blockId); + } + if (sess->connected) { + sess->client->close(); + } + delete sess->client; + sess->client = nullptr; + } + + delete sess; +} diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc index 2bc980e..e8156ba 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.cc @@ -9,6 +9,7 @@ */ #include +#include #include #include @@ -40,71 +41,167 @@ HyperAmpBridge& HyperAmpBridge::instance() { /* Initialisation */ /* ======================================================================== */ -void HyperAmpBridge::init() { - if (initialized_) { - return; - } - - /* - * Read shared-memory virtual addresses from the IPC message registers. - * - * The seL4 boot loader (boot.c) writes the three addresses into - * msg[2..4]. seL4_GetMR(2) = TX queue, seL4_GetMR(3) = RX queue, - * seL4_GetMR(4) = data region. - * - * NOTE: No seL4 system calls may be issued before these reads – the - * message registers are volatile and any syscall would overwrite them. - * In practice monkey-mnemosyne calls bridge.init() early enough that - * this is not a problem; but apps/front also caches the values in global - * pointers for exactly the same reason. - * - * We use the compile-time virtual addresses from hyperamp_shm_queue.h - * as the primary source. They match what boot.c maps. - */ - txQueue_ = SHM_TX_QUEUE_VADDR; - rxQueue_ = SHM_RX_QUEUE_VADDR; - dataRegion_ = SHM_DATA_REGION_VA; - - MONKEY_LOG_INFO("[HyperAmpBridge] TX queue : ", (unsigned long)(uintptr_t)txQueue_); - MONKEY_LOG_INFO("[HyperAmpBridge] RX queue : ", (unsigned long)(uintptr_t)rxQueue_); - MONKEY_LOG_INFO("[HyperAmpBridge] Data rgn : ", (unsigned long)(uintptr_t)dataRegion_); - - /* - * Initialise the queues. We are the "creator" (is_creator = 1) because - * monkey-mnemosyne is the rootserver and runs first on seL4. - * - * Configuration mirrors apps/front/src/engine.c: - * capacity = 256 slots - * block_size = 4096 bytes - */ +/* + * Internal helper – performs the queue-init dance once we have all the + * vaddrs / paddrs / capacity figured out. Both public init() overloads + * funnel through here. + */ +namespace { + +bool initQueuesImpl(volatile HyperampShmQueue* tx, + volatile HyperampShmQueue* rx, + uint64_t txPhys, + uint64_t rxPhys, + uint16_t capacity) +{ HyperampQueueConfig txCfg; memset(&txCfg, 0, sizeof(txCfg)); txCfg.map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH; - txCfg.capacity = 256; + txCfg.capacity = capacity; txCfg.block_size = 4096; - txCfg.phy_addr = SHM_TX_QUEUE_PADDR; - txCfg.virt_addr = reinterpret_cast(txQueue_); + txCfg.phy_addr = txPhys; + txCfg.virt_addr = reinterpret_cast(tx); - if (hyperamp_queue_init(txQueue_, &txCfg, 1) != HYPERAMP_OK) { + if (hyperamp_queue_init(tx, &txCfg, 1) != HYPERAMP_OK) { MONKEY_LOG_ERROR("[HyperAmpBridge] Failed to init TX queue"); - return; + return false; } HyperampQueueConfig rxCfg; memset(&rxCfg, 0, sizeof(rxCfg)); rxCfg.map_mode = HYPERAMP_MAP_MODE_CONTIGUOUS_BOTH; - rxCfg.capacity = 256; + rxCfg.capacity = capacity; rxCfg.block_size = 4096; - rxCfg.phy_addr = SHM_RX_QUEUE_PADDR; - rxCfg.virt_addr = reinterpret_cast(rxQueue_); + rxCfg.phy_addr = rxPhys; + rxCfg.virt_addr = reinterpret_cast(rx); - if (hyperamp_queue_init(rxQueue_, &rxCfg, 1) != HYPERAMP_OK) { + if (hyperamp_queue_init(rx, &rxCfg, 1) != HYPERAMP_OK) { MONKEY_LOG_ERROR("[HyperAmpBridge] Failed to init RX queue"); + return false; + } + + return true; +} + +} // namespace + + +void HyperAmpBridge::init(adl::uint8_t channelId) { + if (initialized_) { + return; + } + + /* + * Legacy entry point — uses the compile-time vaddrs from + * hyperamp_shm_queue.h. These were correct before the kernel boot + * loader was reworked to map the 4 MiB shm window dynamically and + * publish vaddrs through IPC MRs; they are kept here only so that + * lab-main.cc keeps compiling. Production callers must use the + * four-argument overload below. + */ + volatile HyperampShmQueue* tx = SHM_TX_QUEUE_VADDR; + volatile HyperampShmQueue* rx = SHM_RX_QUEUE_VADDR; + volatile void* dr = SHM_DATA_REGION_VA; + uint64_t txPhys = SHM_TX_QUEUE_PADDR; + uint64_t rxPhys = SHM_RX_QUEUE_PADDR; + + if (channelId == 2) { + /* Apply the CH2 paddr offset against the legacy base. The + * vaddr is still the legacy CH1 vaddr — this overload cannot + * provide a correct CH2 vaddr because it has no access to the + * IPC MRs; if you want a working CH2 setup, use the four-arg + * overload from mnemosyne_engine_init() instead. */ + txPhys = SHM_TX_QUEUE_PADDR + 0x300000UL; + rxPhys = SHM_RX_QUEUE_PADDR + 0x300000UL; + MONKEY_LOG_WARN("[HyperAmpBridge] init(channelId=2) used the " + "legacy compile-time vaddrs; this only works on " + "kernels that still map the CH1 region at the " + "old 0x55E000 vaddr. Prefer init(2, tx, rx, dt) " + "with vaddrs from seL4_GetMR(8/9/10)."); + } + + txQueue_ = tx; + rxQueue_ = rx; + dataRegion_ = dr; + + MONKEY_LOG_INFO("[HyperAmpBridge] (legacy) TX : ", + (unsigned long)(uintptr_t)txQueue_); + MONKEY_LOG_INFO("[HyperAmpBridge] (legacy) RX : ", + (unsigned long)(uintptr_t)rxQueue_); + MONKEY_LOG_INFO("[HyperAmpBridge] (legacy) DT : ", + (unsigned long)(uintptr_t)dataRegion_); + + if (!initQueuesImpl(txQueue_, rxQueue_, txPhys, rxPhys, /*capacity=*/256)) { + return; + } + + initialized_ = true; + MONKEY_LOG_INFO("[HyperAmpBridge] Initialised OK (legacy path, ch=", + (int)channelId, ")"); +} + + +void HyperAmpBridge::init(adl::uint8_t channelId, + adl::uint64_t txVa, + adl::uint64_t rxVa, + adl::uint64_t dataVa) +{ + if (initialized_) { + return; + } + if (!txVa || !rxVa || !dataVa) { + MONKEY_LOG_ERROR("[HyperAmpBridge] init(): null vaddr from MR"); + return; + } + + /* + * Pick paddr offset & queue capacity from the channel id. + * + * The platform shm base lives in hyperamp_shm_queue.h as + * SHM_TX_QUEUE_PADDR / SHM_RX_QUEUE_PADDR (per CONFIG_PLAT_*); the + * per-channel offset matches the layout enforced by the kernel boot + * loader (CH1 = +0x200000, CH2 = +0x300000). + */ + uint64_t paddrOffset = 0; + uint16_t capacity = 256; + + switch (channelId) { + case 2: + paddrOffset = MNEMOSYNE_CH2_PADDR_OFFSET; + capacity = MNEMOSYNE_CH2_QUEUE_CAPACITY; + MONKEY_LOG_INFO("[HyperAmpBridge] Using channel 2 (mnemosyne)"); + break; + case 1: + default: + paddrOffset = 0x200000UL; /* matches HYPERAMP_CH1_OFFSET_PADDR */ + capacity = 253; + MONKEY_LOG_INFO("[HyperAmpBridge] Using channel 1"); + break; + } + + txQueue_ = reinterpret_cast(txVa); + rxQueue_ = reinterpret_cast(rxVa); + dataRegion_ = reinterpret_cast(dataVa); + + uint64_t txPhys = SHM_TX_QUEUE_PADDR + paddrOffset; + uint64_t rxPhys = SHM_RX_QUEUE_PADDR + paddrOffset; + + MONKEY_LOG_INFO("[HyperAmpBridge] TX vaddr=", + (unsigned long)(uintptr_t)txQueue_, + " paddr=", (unsigned long)txPhys); + MONKEY_LOG_INFO("[HyperAmpBridge] RX vaddr=", + (unsigned long)(uintptr_t)rxQueue_, + " paddr=", (unsigned long)rxPhys); + MONKEY_LOG_INFO("[HyperAmpBridge] DT vaddr=", + (unsigned long)(uintptr_t)dataRegion_); + + if (!initQueuesImpl(txQueue_, rxQueue_, txPhys, rxPhys, capacity)) { return; } initialized_ = true; - MONKEY_LOG_INFO("[HyperAmpBridge] Initialised OK"); + MONKEY_LOG_INFO("[HyperAmpBridge] Initialised OK (ch=", (int)channelId, + ", capacity=", (int)capacity, ")"); } @@ -190,7 +287,9 @@ bool HyperAmpBridge::rxDequeueBlocking(void* buf, adl::size_t* outLen) { /* Session create (connect) */ /* ======================================================================== */ -bool HyperAmpBridge::connect(const IP4Addr& ip, adl::uint16_t port) { +bool HyperAmpBridge::connect(const IP4Addr& ip, + adl::uint16_t port, + adl::uint16_t devId) { if (!initialized_) { MONKEY_LOG_ERROR("[HyperAmpBridge] connect() called before init()"); return false; @@ -228,7 +327,7 @@ bool HyperAmpBridge::connect(const IP4Addr& ip, adl::uint16_t port) { /* --- SessIPv4Params --- */ SessIPv4Params sessParams; memset(&sessParams, 0, sizeof(sessParams)); - sessParams.device_selection = 0xFF; // DEV_ID_AUTO_HANDOVER + sessParams.device_selection = devId; // 0xFF = DEV_ID_AUTO_HANDOVER sessParams.transport_layer_proto = SESS_TCP_PROTO; sessParams.dest_endpoint.ipv4_addr.data[0] = ip.ui8arr[0]; sessParams.dest_endpoint.ipv4_addr.data[1] = ip.ui8arr[1]; diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h index 600e671..62f72b1 100644 --- a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/HyperAmpBridge.h @@ -74,15 +74,48 @@ public: /* ---- Lifecycle ------------------------------------------------------ */ /** - * Initialise the HyperAMP TX/RX queues. + * Initialise the HyperAMP TX/RX queues — legacy entry point. * - * Must be called exactly once before any other method. Subsequent calls - * are harmless no-ops. + * Reads queue virtual addresses from the compile-time constants in + * hyperamp_shm_queue.h, the way this bridge originally worked. * - * Queue virtual addresses are obtained from seL4_GetMR() (set up by the - * boot loader), identical to how apps/front reads them. + * IMPORTANT: after the multi-channel rework in the kernel boot + * loader those compile-time vaddrs are no longer guaranteed to + * match the actual mapping. This entry point is preserved purely + * so that the legacy lab-main.cc test path keeps compiling; in + * production code use the four-argument overload below and pass + * vaddrs read from seL4_GetMR() instead. + * + * @param channelId 1 = ch1 layout (default), 2 = ch2 layout. + */ + void init(adl::uint8_t channelId = 1); + + /** + * Initialise the HyperAMP TX/RX queues with explicit virtual + * addresses obtained from the IPC message registers. + * + * This is the modern entry point used by mnemosyne_engine_init(). + * The caller is responsible for reading seL4_GetMR(...) BEFORE + * issuing any other syscall (the IPC buffer is volatile across + * syscalls; see apps/hyperamp-server/src/main.c for the exact + * ordering rule). + * + * Idempotent: subsequent calls are no-ops. + * + * @param channelId Channel id, used only for paddr-offset selection + * (1 = CH1, 2 = CH2). Determines the `phy_addr` + * passed into hyperamp_queue_init() so that the + * underlying mapping table is consistent with what + * the kernel actually mapped. + * @param txVa Virtual address of the TX queue control block, + * as published in the IPC MR by the boot loader. + * @param rxVa Virtual address of the RX queue control block. + * @param dataVa Virtual address of the shared data region. */ - void init(); + void init(adl::uint8_t channelId, + adl::uint64_t txVa, + adl::uint64_t rxVa, + adl::uint64_t dataVa); /** * @return true after a successful init(). @@ -94,16 +127,23 @@ public: /** * Create a TCP session via the HyperAMP proxy. * - * Builds a HYPERAMP_MSG_TYPE_SESS / SESS_MSG_CREATE command, enqueues it - * on the TX queue, then busy-waits on the RX queue for the matching - * session-create response. On success, stores the backend_sess_id for - * subsequent send/recv calls. + * Builds a HYPERAMP_MSG_TYPE_SESS / SESS_MSG_CREATE command, enqueues + * it on the TX queue, then busy-waits on the RX queue for the + * matching session-create response. On success, stores the + * backend_sess_id for subsequent send/recv calls. * - * @param ip Destination IPv4 address (network byte order). - * @param port Destination port (host byte order). + * @param ip Destination IPv4 address (network byte order). + * @param port Destination port (host byte order). + * @param devId Backend device selector written into + * SessIPv4Params.device_selection. 0xFF (default) is + * the "auto handover" sentinel which lets the backend + * pick a NIC; pass an explicit id to force a specific + * interface in multi-NIC deployments. * @return true on success, false on failure. */ - bool connect(const IP4Addr& ip, adl::uint16_t port); + bool connect(const IP4Addr& ip, + adl::uint16_t port, + adl::uint16_t devId = 0xFF); /** * Send `len` bytes through the established session. diff --git a/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/channel_ch2.h b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/channel_ch2.h new file mode 100644 index 0000000..4b5a2d2 --- /dev/null +++ b/projects/sel4test/apps/monkey-mnemosyne/src/monkey/net/channel_ch2.h @@ -0,0 +1,79 @@ +/** + * @file channel_ch2.h + * @brief HyperAMP channel-2 layout constants for monkey-mnemosyne. + * + * Background + * ---------- + * Per HYPERAMP_MULTI_CHANNEL_DESIGN.md (top of repo) and + * apps/hyperamp-server/src/{channel.c,main.c}, the seL4 boot loader + * maps a single 4 MiB window of shared memory and publishes the per- + * channel virtual addresses through IPC message registers: + * + * msg[2..4] -> CH0 (TX, RX, Data) -- hyperamp-server + * msg[5..7] -> CH1 (TX, RX, Data) -- front / HighSpeedCProxy + * msg[8..10] -> CH2 (TX, RX, Data) -- monkey-mnemosyne (us) + * + * Physical addresses are a fixed offset from a per-platform base: + * + * CH0 base = base + 0x000000 + * CH1 base = base + 0x200000 + * CH2 base = base + 0x300000 + * + * The virtual addresses are NOT compile-time constants; they are + * computed at boot by the kernel and must be read from the IPC MRs + * *before any seL4 syscall is issued*, otherwise the IPC buffer is + * overwritten and the values are lost. See mnemosyne_api.cc for the + * single authoritative reader. + * + * This header therefore exposes only the bits that ARE compile-time + * constant: the MR slot indices, the physical address offsets, and + * the queue capacity. + * + * Why a separate header + * --------------------- + * Mirrors the apps/hyperamp-server/include/channel_ch1.h pattern + * (one tiny header per channel) so that integrators can cross- + * reference the two cleanly when wiring up a unified application. + * + * gongty [at] tongji [dot] edu [dot] cn + */ + +#ifndef MONKEY_NET_CHANNEL_CH2_H +#define MONKEY_NET_CHANNEL_CH2_H + +/* ---- IPC message-register slots ---------------------------------------- */ +/* + * NOTE: seL4 message registers are read with seL4_GetMR(i). The kernel + * boot loader writes ipcBuf[i + 1] = vaddr, hence MR_SLOT_CH2_TX = 8 + * corresponds to seL4_GetMR(8) returning the CH2 TX queue vaddr. + */ +#define MNEMOSYNE_MR_SLOT_CH2_TX 8 +#define MNEMOSYNE_MR_SLOT_CH2_RX 9 +#define MNEMOSYNE_MR_SLOT_CH2_DATA 10 + +/* ---- Physical address offsets (relative to the platform shm base) ------ */ +/* + * The platform shm base lives in hyperamp_shm_queue.h as + * SHM_TX_QUEUE_PADDR (defined per CONFIG_PLAT_*). We derive the CH2 + * physical addresses from that base + this offset, exactly the way + * apps/front/src/engine.c derives CH1 from base + HYPERAMP_CH1_OFFSET_PADDR. + */ +#define MNEMOSYNE_CH2_PADDR_OFFSET 0x300000UL +#define MNEMOSYNE_CH2_TX_PADDR_OFFSET (MNEMOSYNE_CH2_PADDR_OFFSET + 0x000UL) +#define MNEMOSYNE_CH2_RX_PADDR_OFFSET (MNEMOSYNE_CH2_PADDR_OFFSET + 0x1000UL) + +/* ---- Queue capacity ---------------------------------------------------- */ +/* + * Same as apps/hyperamp-server CH1 (253): + * total CH2 area = 1 MiB + * minus 2 control pages (TX queue + RX queue header), one block each + * data region = 1 MiB - 8 KiB = 254 * 4 KiB blocks + * enqueue uses (idx + 1) * block_size, so capacity must be <= 253. + * + * monkey-mnemosyne actually only needs a single 4 KiB block (one shared + * page); 253 is intentionally generous to leave headroom and to keep + * the layout consistent with CH1 when the integrator visualises it. + */ +#define MNEMOSYNE_CH2_QUEUE_CAPACITY 253 + +#endif /* MONKEY_NET_CHANNEL_CH2_H */ -- Gitee