| /* |
| * Copyright (c) 2020 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * RAM-based memory buffer backing store implementation for demo purposes |
| */ |
| #include <mmu.h> |
| #include <string.h> |
| #include <kernel_arch_interface.h> |
| |
| /* |
| * TODO: |
| * |
| * This is a demonstration backing store for testing the kernel side of the |
| * demand paging feature. In production there are basically two types of |
| * backing stores: |
| * |
| * 1) A large, sparse backing store that is big enough to capture the entire |
| * address space. Implementation of these is very simple; the location |
| * token is just a function of the evicted virtual address and no space |
| * management is necessary. Clean copies of paged-in data pages may be kept |
| * indefinitely. |
| * |
| * 2) A backing store that has limited storage space, and is not sufficiently |
| * large to hold clean copies of all mapped memory. |
| * |
| * This backing store is an example of the latter case. However, locations |
| * are freed as soon as pages are paged in, in |
| * k_mem_paging_backing_store_page_finalize(). |
| * This implies that all data pages are treated as dirty as |
| * Z_PAGE_FRAME_BACKED is never set, even if the data page was paged out before |
| * and not modified since then. |
| * |
| * An optimization a real backing store will want is have |
| * k_mem_paging_backing_store_page_finalize() note the storage location of |
| * a paged-in data page in a custom field of its associated z_page_frame, and |
| * set the Z_PAGE_FRAME_BACKED bit. Invocations of |
| * k_mem_paging_backing_store_location_get() will have logic to return |
| * the previous clean page location instead of allocating |
| * a new one if Z_PAGE_FRAME_BACKED is set. |
| * |
| * This will, however, require the implementation of a clean page |
| * eviction algorithm, to free backing store locations for loaded data pages |
| * as the backing store fills up, and clear the Z_PAGE_FRAME_BACKED bit |
| * appropriately. |
| * |
| * All of this logic is local to the backing store implementation; from the |
| * core kernel's perspective the only change is that Z_PAGE_FRAME_BACKED |
| * starts getting set for certain page frames after a page-in (and possibly |
| * cleared at a later time). |
| */ |
| static char backing_store[CONFIG_MMU_PAGE_SIZE * |
| CONFIG_BACKING_STORE_RAM_PAGES]; |
| static struct k_mem_slab backing_slabs; |
| static unsigned int free_slabs; |
| |
| static void *location_to_slab(uintptr_t location) |
| { |
| __ASSERT(location % CONFIG_MMU_PAGE_SIZE == 0, |
| "unaligned location 0x%lx", location); |
| __ASSERT(location < |
| (CONFIG_BACKING_STORE_RAM_PAGES * CONFIG_MMU_PAGE_SIZE), |
| "bad location 0x%lx, past bounds of backing store", location); |
| |
| return backing_store + location; |
| } |
| |
| static uintptr_t slab_to_location(void *slab) |
| { |
| char *pos = slab; |
| uintptr_t offset; |
| |
| __ASSERT(pos >= backing_store && |
| pos < backing_store + ARRAY_SIZE(backing_store), |
| "bad slab pointer %p", slab); |
| offset = pos - backing_store; |
| __ASSERT(offset % CONFIG_MMU_PAGE_SIZE == 0, |
| "unaligned slab pointer %p", slab); |
| |
| return offset; |
| } |
| |
| int k_mem_paging_backing_store_location_get(struct z_page_frame *pf, |
| uintptr_t *location, |
| bool page_fault) |
| { |
| int ret; |
| void *slab; |
| |
| if ((!page_fault && free_slabs == 1) || free_slabs == 0) { |
| return -ENOMEM; |
| } |
| |
| ret = k_mem_slab_alloc(&backing_slabs, &slab, K_NO_WAIT); |
| __ASSERT(ret == 0, "slab count mismatch"); |
| (void)ret; |
| *location = slab_to_location(slab); |
| free_slabs--; |
| |
| return 0; |
| } |
| |
| void k_mem_paging_backing_store_location_free(uintptr_t location) |
| { |
| void *slab = location_to_slab(location); |
| |
| k_mem_slab_free(&backing_slabs, &slab); |
| free_slabs++; |
| } |
| |
| void k_mem_paging_backing_store_page_out(uintptr_t location) |
| { |
| (void)memcpy(location_to_slab(location), Z_SCRATCH_PAGE, |
| CONFIG_MMU_PAGE_SIZE); |
| } |
| |
| void k_mem_paging_backing_store_page_in(uintptr_t location) |
| { |
| (void)memcpy(Z_SCRATCH_PAGE, location_to_slab(location), |
| CONFIG_MMU_PAGE_SIZE); |
| } |
| |
| void k_mem_paging_backing_store_page_finalize(struct z_page_frame *pf, |
| uintptr_t location) |
| { |
| k_mem_paging_backing_store_location_free(location); |
| } |
| |
| void k_mem_paging_backing_store_init(void) |
| { |
| k_mem_slab_init(&backing_slabs, backing_store, CONFIG_MMU_PAGE_SIZE, |
| CONFIG_BACKING_STORE_RAM_PAGES); |
| free_slabs = CONFIG_BACKING_STORE_RAM_PAGES; |
| } |