From 34319442aaf0b303fcb5b8733591bacb6e011345 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Wed, 16 Aug 2023 14:45:04 -0400 Subject: [PATCH] Implement shmat and shmdt --- mentos/inc/mem/paging.h | 21 ++++-- mentos/src/ipc/shm.c | 93 ++++++++++++++++--------- mentos/src/mem/kheap.c | 2 +- mentos/src/mem/paging.c | 143 +++++++++++++++++++------------------- programs/tests/t_shmget.c | 42 ++++++----- 5 files changed, 171 insertions(+), 130 deletions(-) diff --git a/mentos/inc/mem/paging.h b/mentos/inc/mem/paging.h index e77b0cf..922f6e3 100644 --- a/mentos/inc/mem/paging.h +++ b/mentos/inc/mem/paging.h @@ -236,13 +236,6 @@ vm_area_struct_t *create_vm_area(mm_struct_t *mm, uint32_t pgflags, uint32_t gfpflags); -vm_area_struct_t *create_vm_area_with_phy(mm_struct_t *mm, - uint32_t virt_start, - size_t size, - uint32_t pgflags, - uint32_t gfpflags, - uint32_t phy_vm_start); - /// @brief Clone a virtual memory area, using copy on write if specified /// @param mm the memory descriptor which will contain the new segment. /// @param area the area to clone @@ -266,6 +259,20 @@ int destroy_vm_area(mm_struct_t *mm, vm_area_struct_t *area); /// @return a pointer to the area if we found it, NULL otherwise. vm_area_struct_t *find_vm_area(mm_struct_t *mm, uint32_t vm_start); +/// @brief Checks if the given virtual memory area range is valid. +/// @param mm the memory descriptor which we use to check the range. +/// @param vm_start the starting address of the area. +/// @param vm_end the ending address of the area. +/// @return 1 if it's valid, 0 if it's occupied, -1 if it's outside the memory. +int is_valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end); + +/// @brief Searches for an empty spot for a new virtual memory area. +/// @param mm the memory descriptor which should contain the new area. +/// @param length the size of the empty spot. +/// @param vm_start where we save the starting address for the new area. +/// @return 0 on success, 1 on failure. +int find_free_vm_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start); + /// @brief Creates the main memory descriptor. /// @param stack_size The size of the stack in byte. /// @return The Memory Descriptor created. diff --git a/mentos/src/ipc/shm.c b/mentos/src/ipc/shm.c index e903dd4..013dfb2 100644 --- a/mentos/src/ipc/shm.c +++ b/mentos/src/ipc/shm.c @@ -73,6 +73,7 @@ static inline shm_info_t *__shm_info_alloc(key_t key, size_t size, int shmflg) // Allocate the memory. uint32_t order = find_nearest_order_greater(0, size); shm_info->shm_location = _alloc_pages(GFP_KERNEL, order); + pr_crit("page: %p\n", shm_info->shm_location); // Return the shared memory structure. return shm_info; } @@ -130,6 +131,25 @@ static inline shm_info_t *__list_find_shm_info_by_key(key_t key) return NULL; } +/// @brief Searches for the shared memory with the given page. +/// @param page the page we are searching. +/// @return the shared memory with the given page. +static inline shm_info_t *__list_find_shm_info_by_page(page_t *page) +{ + shm_info_t *shm_info; + // Iterate through the list of shared memories. + list_for_each_decl(it, &shm_list) + { + // Get the current entry. + shm_info = list_entry(it, shm_info_t, list); + // If shared memories is valid, check the id. + if (shm_info && (shm_info->shm_location == page)) { + return shm_info; + } + } + return NULL; +} + static inline void __list_add_shm_info(shm_info_t *shm_info) { assert(shm_info && "Received a NULL pointer."); @@ -198,34 +218,12 @@ long sys_shmget(key_t key, size_t size, int shmflg) return shm_info->id; } -static inline int __find_vm_free_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start) -{ - // Get the stack. - vm_area_struct_t *area, *prev_area; - list_for_each_prev_decl(it, &mm->mmap_list) - { - area = list_entry(it, vm_area_struct_t, vm_list); - assert(area && "There is a NULL area in the list."); - // Check the previous segment. - if (area->vm_list.prev != &mm->mmap_list) { - prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list); - assert(prev_area && "There is a NULL area in the list."); - // Compute the available space. - unsigned available_space = area->vm_start - prev_area->vm_end; - // If the space is enough, return the address. - if (available_space >= length) { - *vm_start = area->vm_start - length; - return 0; - } - } - } - return 1; -} - void *sys_shmat(int shmid, const void *shmaddr, int shmflg) { shm_info_t *shm_info = NULL; task_struct *task = NULL; + uint32_t vm_start, phy_start; + // The id is less than zero. if (shmid < 0) { pr_err("The id is less than zero.\n"); @@ -251,26 +249,55 @@ void *sys_shmat(int shmid, const void *shmaddr, int shmflg) // Get the calling task. task = scheduler_get_current_process(); assert(task && "Failed to get the current running process."); - - uint32_t phy_start = get_physical_address_from_page(shm_info->shm_location); - uint32_t vm_start; - __find_vm_free_area( - task->mm, - shm_info->shmid.shm_segsz, - &vm_start); - + // Get the phyisical address from the allocated pages. + phy_start = get_physical_address_from_page(shm_info->shm_location); + // Find the virtual address for the new area. + if (find_free_vm_area(task->mm, shm_info->shmid.shm_segsz, &vm_start)) { + pr_err("We failed to find space for the new virtual memory area.\n"); + return (void *)-EACCES; + } mem_upd_vm_area( task->mm->pgd, vm_start, phy_start, shm_info->shmid.shm_segsz, MM_RW | MM_PRESENT | MM_USER | MM_UPDADDR); - return (void *)vm_start; } long sys_shmdt(const void *shmaddr) { + shm_info_t *shm_info = NULL; + task_struct *task = NULL; + uint32_t phy_start; + size_t size; + page_t *page; + + // Get the calling task. + task = scheduler_get_current_process(); + assert(task && "Failed to get the current running process."); + // Get the page. + page = mem_virtual_to_page(task->mm->pgd, (uint32_t)shmaddr, &size); + // Check if we can find the page. + if (page == NULL) { + pr_err("Cannot retrieve the page from the give address.\n"); + return -ENOENT; + } + shm_info = __list_find_shm_info_by_page(page); + // Check if no shared memory exists for the given address. + if (!shm_info) { + pr_err("No shared memory exists for the given address.\n"); + return -ENOENT; + } + // Get the phyisical address from the allocated pages. + phy_start = get_physical_address_from_page(shm_info->shm_location); + // Set all virtual pages as not present. + mem_upd_vm_area( + task->mm->pgd, + (uint32_t)shmaddr, + phy_start, + shm_info->shmid.shm_segsz, + MM_GLOBAL); return 0; } diff --git a/mentos/src/mem/kheap.c b/mentos/src/mem/kheap.c index d4c8acd..9e8d91b 100644 --- a/mentos/src/mem/kheap.c +++ b/mentos/src/mem/kheap.c @@ -4,7 +4,7 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. +#include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[KHEAP ]" ///< Change header. #define __DEBUG_LEVEL__ LOGLEVEL_INFO ///< Set log level. #include "io/debug.h" // Include debugging functions. diff --git a/mentos/src/mem/paging.c b/mentos/src/mem/paging.c index 35122ee..a1477e4 100644 --- a/mentos/src/mem/paging.c +++ b/mentos/src/mem/paging.c @@ -75,98 +75,46 @@ void paging_flush_tlb_single(unsigned long addr) : "memory"); } -/// @brief -/// @param mm -/// @param vm_start -/// @param vm_end -/// @return int -static inline int __valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end) -{ - if (vm_end <= vm_start) - return -1; - // Get the stack. - vm_area_struct_t *area; - list_for_each_prev_decl(it, &mm->mmap_list) - { - area = list_entry(it, vm_area_struct_t, vm_list); - assert(area && "There is a NULL area in the list."); - if ((vm_start >= area->vm_start) && (vm_start <= area->vm_end)) - return 0; - if ((vm_end >= area->vm_start) && (vm_end <= area->vm_end)) - return 0; - if ((vm_start <= area->vm_start) && (vm_end >= area->vm_end)) - return 0; - } - return 1; -} - -/// @brief Searches for an empty spot for a new virtual memory area. -/// @param mm the memory descriptor which should contain the new area. -/// @param length the size of the empty spot. -/// @param vm_start where we save the starting address for the new area. -/// @return 0 on success, 1 on failure. -static inline int __find_vm_free_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start) -{ - // Get the stack. - vm_area_struct_t *area, *prev_area; - list_for_each_prev_decl(it, &mm->mmap_list) - { - area = list_entry(it, vm_area_struct_t, vm_list); - assert(area && "There is a NULL area in the list."); - // Check the previous segment. - if (area->vm_list.prev != &mm->mmap_list) { - prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list); - assert(prev_area && "There is a NULL area in the list."); - // Compute the available space. - unsigned available_space = area->vm_start - prev_area->vm_end; - // If the space is enough, return the address. - if (available_space >= length) { - *vm_start = area->vm_start - length; - return 0; - } - } - } - return 1; -} - vm_area_struct_t *create_vm_area(mm_struct_t *mm, uint32_t vm_start, size_t size, uint32_t pgflags, uint32_t gfpflags) { + uint32_t vm_end, phy_start, order; + vm_area_struct_t *segment; + // Compute the end of the virtual memory area. - uint32_t vm_end = vm_start + size; + vm_end = vm_start + size; // Check if the range is already occupied. - if (__valid_vm_area(mm, vm_start, vm_end) <= 0) { + if (is_valid_vm_area(mm, vm_start, vm_end) <= 0) { pr_crit("The virtual memory area range [%p, %p] is already in use.\n", vm_start, vm_end); kernel_panic("Wrong virtual memory area range."); } // Allocate on kernel space the structure for the segment. - vm_area_struct_t *new_segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL); + segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL); // Find the nearest order for the given memory size. - uint32_t order = find_nearest_order_greater(vm_start, size); - uint32_t phy_vm_start; + order = find_nearest_order_greater(vm_start, size); if (pgflags & MM_COW) { - pgflags &= ~(MM_PRESENT | MM_UPDADDR); - phy_vm_start = 0; + pgflags = pgflags & ~(MM_PRESENT | MM_UPDADDR); + phy_start = 0; } else { - pgflags |= MM_UPDADDR; + pgflags = pgflags | MM_UPDADDR; page_t *page = _alloc_pages(gfpflags, order); - phy_vm_start = get_physical_address_from_page(page); + phy_start = get_physical_address_from_page(page); } - mem_upd_vm_area(mm->pgd, vm_start, phy_vm_start, size, pgflags); + mem_upd_vm_area(mm->pgd, vm_start, phy_start, size, pgflags); // Update vm_area_struct info. - new_segment->vm_start = vm_start; - new_segment->vm_end = vm_end; - new_segment->vm_mm = mm; + segment->vm_start = vm_start; + segment->vm_end = vm_end; + segment->vm_mm = mm; // Update memory descriptor list of vm_area_struct. - list_head_insert_after(&new_segment->vm_list, &mm->mmap_list); - mm->mmap_cache = new_segment; + list_head_insert_after(&segment->vm_list, &mm->mmap_list); + mm->mmap_cache = segment; // Sort the mmap_list. list_head_sort(&mm->mmap_list, vm_area_compare); @@ -176,7 +124,7 @@ vm_area_struct_t *create_vm_area(mm_struct_t *mm, mm->total_vm += (1U << order); - return new_segment; + return segment; } uint32_t clone_vm_area(mm_struct_t *mm, vm_area_struct_t *area, int cow, uint32_t gfpflags) @@ -276,6 +224,57 @@ inline vm_area_struct_t *find_vm_area(mm_struct_t *mm, uint32_t vm_start) return NULL; } +inline int is_valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end) +{ + if (vm_end <= vm_start) { + return -1; + } + // Get the stack. + vm_area_struct_t *area; + list_for_each_prev_decl(it, &mm->mmap_list) + { + area = list_entry(it, vm_area_struct_t, vm_list); + assert(area && "There is a NULL area in the list."); + if ((vm_start > area->vm_start) && (vm_start < area->vm_end)) { + pr_crit("INSIDE(START): %p <= %p <= %p", area->vm_start, vm_start, area->vm_end); + return 0; + } + if ((vm_end > area->vm_start) && (vm_end < area->vm_end)) { + pr_crit("INSIDE(END): %p <= %p <= %p", area->vm_start, vm_end, area->vm_end); + return 0; + } + if ((vm_start < area->vm_start) && (vm_end > area->vm_end)) { + pr_crit("WRAPS: %p <= (%p, %p) <= %p", vm_start, area->vm_start, area->vm_end, vm_end); + return 0; + } + } + return 1; +} + +inline int find_free_vm_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start) +{ + // Get the stack. + vm_area_struct_t *area, *prev_area; + list_for_each_prev_decl(it, &mm->mmap_list) + { + area = list_entry(it, vm_area_struct_t, vm_list); + assert(area && "There is a NULL area in the list."); + // Check the previous segment. + if (area->vm_list.prev != &mm->mmap_list) { + prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list); + assert(prev_area && "There is a NULL area in the list."); + // Compute the available space. + unsigned available_space = area->vm_start - prev_area->vm_end; + // If the space is enough, return the address. + if (available_space >= length) { + *vm_start = area->vm_start - length; + return 0; + } + } + } + return 1; +} + static void __init_pagedir(page_directory_t *pdir) { *pdir = (page_directory_t){ { 0 } }; @@ -724,11 +723,11 @@ void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t off // Get the current task. task_struct *task = scheduler_get_current_process(); // Check if we were asked for a specific spot. - if (addr && __valid_vm_area(task->mm, (uintptr_t)addr, (uintptr_t)addr + length)) { + if (addr && is_valid_vm_area(task->mm, (uintptr_t)addr, (uintptr_t)addr + length)) { vm_start = (uintptr_t)addr; } else { // Find an empty spot. - if (__find_vm_free_area(task->mm, length, &vm_start)) { + if (find_free_vm_area(task->mm, length, &vm_start)) { pr_err("We failed to find a suitable spot for a new virtual memory area.\n"); return NULL; } diff --git a/programs/tests/t_shmget.c b/programs/tests/t_shmget.c index 27fb9e9..ba72196 100644 --- a/programs/tests/t_shmget.c +++ b/programs/tests/t_shmget.c @@ -11,7 +11,7 @@ int main(void) { - int semid, shmid; + int shmid; pid_t cpid; int *array; @@ -21,40 +21,48 @@ int main(void) perror("shmget"); return EXIT_FAILURE; } - // Create a semaphore set containing one semaphore. - semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600); - if (semid == -1) { - perror("semget"); - return EXIT_FAILURE; - } + printf("shmid = %d;\n", shmid); + // Create a child. cpid = fork(); - if (cpid == 0) { + // Child attaches the shared memory. array = (int *)shmat(shmid, NULL, 0); + if (array == NULL) { + perror("shmat"); + return EXIT_FAILURE; + } printf("C: %p\n", array); array[0] = 1; return 0; - } else { - array = (int *)shmat(shmid, NULL, 0); - printf("F: %p\n", array); - array[1] = 2; } + // Father attaches the shared memory. + array = (int *)shmat(shmid, NULL, 0); + if (array == NULL) { + perror("shmat"); + return EXIT_FAILURE; + } + + // Wait for the child to finish. while (wait(NULL) != -1) continue; + + printf("F: %p\n", array); + array[1] = 2; printf("array[%d] : %d\n", 0, array[0]); printf("array[%d] : %d\n", 1, array[1]); + // Detatch the shared memory. + if (shmdt(array) < 0) { + perror("shmdt"); + return EXIT_FAILURE; + } + // Remove the shared memory. if (shmctl(shmid, IPC_RMID, NULL) == -1) { perror("shmctl"); return EXIT_FAILURE; } - // Remove the semaphore set. - if (semctl(semid, 0, IPC_RMID, NULL) == -1) { - perror("semctl"); - return EXIT_FAILURE; - } return EXIT_SUCCESS; }