diff --git a/include/nanvix/mm.h b/include/nanvix/mm.h index 9f734f0a4..fa256a949 100644 --- a/include/nanvix/mm.h +++ b/include/nanvix/mm.h @@ -116,4 +116,128 @@ /**@}*/ +/*============================================================================* + * Page Frame Allocator * + *============================================================================*/ + +/** + * @addtogroup kernel-mm-frame Frame Allocator + * @ingroup kernel-mm + * + * @brief Page Frame Allocator + */ +/**@{*/ + + /** + * @brief Number of page frames for user use. + */ + #define NUM_UFRAMES (UMEM_SIZE/PAGE_SIZE) + + /** + * @param Null frame. + */ + #define FRAME_NULL ((frame_t) -1) + + /** + * @brief Asserts if a frame ID is valid. + * + * The frame_is_valid_id() function asserts whether or not the + * frame @p ID is valid. + * + * @returns If @p id is valid, non zero is returned. Otherwise, + * zero is returned instead. + * + * @author Pedro Henrique Penna + */ + static inline int frame_is_valid_id(int id) + { + return ((id >= 0) && (id < NUM_UFRAMES)); + } + + /** + * @brief Converts an ID of a user page frame to a page frame number. + * + * @param id ID of target user page frame. + * + * @returns Frame number of target user page frame. + * + * @author Pedro Henrique Penna + */ + static inline frame_t frame_id_to_num(int id) + { + /* Invalid ID. */ + if (!frame_is_valid_id(id)) + return (FRAME_NULL); + + return ((UBASE_PHYS >> PAGE_SHIFT) + id); + } + + /** + * @brief Asserts if a frame number is valid. + * + * The frame_is_valid_num() function asserts whether or not the + * frame number @p frame is valid. + * + * @returns If @p frame is valid, non zero is returned. Otherwise, + * zero is returned instead. + * + * @author Pedro Henrique Penna + */ + static inline int frame_is_valid_num(frame_t frame) + { + return ( + (frame >= (UBASE_PHYS >> PAGE_SHIFT)) && + (frame < ((UBASE_PHYS >> PAGE_SHIFT) + NUM_UFRAMES)) + ); + } + + /** + * @brief Converts a page frame number to an ID of a user page frame. + * + * @param frame Number of the target page frame. + * + * @returns ID of target user page frame. + * + * @author Pedro Henrique Penna + */ + static inline int frame_num_to_id(frame_t frame) + { + /* Invalid frame. */ + if (!frame_is_valid_num(frame)) + return (-1); + + return (frame - (UBASE_PHYS >> PAGE_SHIFT)); + } + + /** + * @brief Allocates a page frame. + * + * @returns Upon successful completion, the number of the + * allocated page frame is returned. Upon failure, @p FRAME_NULL + * is returned instead. + */ + EXTERN frame_t frame_alloc(void); + + /** + * @brief Frees a page frame. + * + * @param frame Number of the target page frame. + * + * @returns Upon successful completion, zero is returned. Upon + * failure, a negative error code is returned instead. + */ + EXTERN int frame_free(frame_t frame); + + /** + * @brief Runs unit tests on the frame allocator. + */ + EXTERN void frame_test_driver(void); + + /** + * @brief Initializes the frame allocator. + */ + EXTERN void frame_init(void); + +/**@}*/ + #endif /** NANVIX_MM_H_ */ diff --git a/src/kernel/mm/frame.c b/src/kernel/mm/frame.c new file mode 100644 index 000000000..c83f326c4 --- /dev/null +++ b/src/kernel/mm/frame.c @@ -0,0 +1,113 @@ +/* + * MIT License + * + * Copyright(c) 2011-2018 Pedro Henrique Penna + * 2015-2016 Davidson Francis + * + * 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 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. + */ + +#include +#include +#include +#include +#include + +/** + * @brief Reference count for page frames. + */ +PRIVATE int frames[NUM_UFRAMES] = {0, }; + +/*============================================================================* + * frame_alloc() * + *============================================================================*/ + +/** + * The frame_alloc() function searches sequentially, for a free frame + * to be allocated. The first free frame is allocated (if any) and its + * number is returned. + */ +PUBLIC frame_t frame_alloc(void) +{ + /* Search for a free frame. */ + for (int i = 0; i < NUM_UFRAMES; i++) + { + /* Found it. */ + if (frames[i] == 0) + { + frames[i] = 1; + hal_dcache_invalidate(); + + return (frame_id_to_num(i)); + } + } + + kprintf("mm: page frame overflow"); + + return (FRAME_NULL); +} + +/*============================================================================* + * frame_free() * + *============================================================================*/ + +/** + * The frame_free() function frees a previously allocated page frame + * whose number equals to @p frame. + */ +PUBLIC int frame_free(frame_t frame) +{ + if (!frame_is_valid_num(frame)) + return (-EINVAL); + + if (frames[frame_num_to_id(frame)] == 0) + { + kprintf("mm: double free on page frame"); + return (-EFAULT); + } + + frames[frame_num_to_id(frame)]--; + hal_dcache_invalidate(); + + return (0); +} + +/*============================================================================* + * frame_init() * + *============================================================================*/ + +/** + * The frame_init() function initializes internal structures of the + * page frame allocator. Additionally, if the kernel is compiled + * without the @p NDEBUG build option, unit tests on the page frame + * allocator are launched once its initialization is completed. + */ +PUBLIC void frame_init(void) +{ + kprintf("initializing the page frame allocator"); + +#ifndef __NANVIX_FAST_BOOT + for (int i = 0; i < NUM_UFRAMES; i++) + frames[i] = 0; +#endif + +#ifndef NDEBUG + frame_test_driver(); +#endif +}