/*
* Copyright (c) 2016-2017 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*
* The purpose of this test module is to show whether allocating and
* modifying all available physical pages leads to unexpected problems.
* In particular, if the early allocation code misbehaves, important
* data structures such as kernel page tables may be considered free,
* in which case this test will catch the error.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct list test_pages;
static struct cpumap test_cpumap;
static unsigned char test_pattern = 1;
static void
test_write_pages(void)
{
struct vm_map *kernel_map;
struct pmap *kernel_pmap;
struct vm_page *page;
int error, flags;
uintptr_t va;
kernel_map = vm_map_get_kernel_map();
kernel_pmap = pmap_get_kernel_pmap();
for (;;) {
page = vm_page_alloc(0, VM_PAGE_SEL_HIGHMEM, VM_PAGE_KERNEL);
if (page == NULL) {
break;
}
va = 0;
flags = VM_MAP_FLAGS(VM_PROT_ALL, VM_PROT_ALL, VM_INHERIT_NONE,
VM_ADV_DEFAULT, 0);
error = vm_map_enter(kernel_map, &va, PAGE_SIZE, 0, flags, NULL, 0);
error_check(error, __func__);
error = pmap_enter(kernel_pmap, va, vm_page_to_pa(page),
VM_PROT_READ | VM_PROT_WRITE, 0);
error_check(error, __func__);
error = pmap_update(kernel_pmap);
error_check(error, __func__);
memset((void *)va, test_pattern, PAGE_SIZE);
error = pmap_remove(kernel_pmap, va, &test_cpumap);
error_check(error, __func__);
error = pmap_update(kernel_pmap);
error_check(error, __func__);
vm_map_remove(kernel_map, va, va + PAGE_SIZE);
list_insert_tail(&test_pages, &page->node);
}
}
static void
test_reset_pages(void)
{
struct vm_map *kernel_map;
struct pmap *kernel_pmap;
struct vm_page *page;
int error, flags;
uintptr_t va;
kernel_map = vm_map_get_kernel_map();
kernel_pmap = pmap_get_kernel_pmap();
while (!list_empty(&test_pages)) {
page = list_first_entry(&test_pages, struct vm_page, node);
list_remove(&page->node);
va = 0;
flags = VM_MAP_FLAGS(VM_PROT_ALL, VM_PROT_ALL, VM_INHERIT_NONE,
VM_ADV_DEFAULT, 0);
error = vm_map_enter(kernel_map, &va, PAGE_SIZE, 0, flags, NULL, 0);
error_check(error, __func__);
error = pmap_enter(kernel_pmap, va, vm_page_to_pa(page),
VM_PROT_READ | VM_PROT_WRITE, 0);
error_check(error, __func__);
error = pmap_update(kernel_pmap);
error_check(error, __func__);
memset((void *)va, 0, PAGE_SIZE);
error = pmap_remove(kernel_pmap, va, &test_cpumap);
error_check(error, __func__);
error = pmap_update(kernel_pmap);
error_check(error, __func__);
vm_map_remove(kernel_map, va, va + PAGE_SIZE);
vm_page_free(page, 0);
}
}
static void
test_run(void *arg)
{
unsigned int i;
(void)arg;
for (i = 0; /* no condition */; i++) {
printf("test: pass:%u pattern:%hhx\n", i, test_pattern);
test_write_pages();
test_reset_pages();
test_pattern++;
}
}
void __init
test_setup(void)
{
struct thread_attr attr;
struct thread *thread;
int error;
list_init(&test_pages);
cpumap_zero(&test_cpumap);
cpumap_set(&test_cpumap, cpu_id());
thread_attr_init(&attr, THREAD_KERNEL_PREFIX "test_run");
thread_attr_set_detached(&attr);
thread_attr_set_cpumap(&attr, &test_cpumap);
error = thread_create(&thread, &attr, test_run, NULL);
error_check(error, "thread_create");
}