blob: 19c5a207069f9d1fd17dfba827a6dce775a45171 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "vm/page.h"
#include <inttypes.h>
#include <lib/console.h>
#include <stdio.h>
#include <string.h>
#include <trace.h>
#include <zircon/errors.h>
#include <kernel/percpu.h>
#include <pretty/hexdump.h>
#include <vm/physmap.h>
#include <vm/pmm.h>
#include <vm/vm.h>
#define LOCAL_TRACE 0
const char* page_state_to_string(unsigned int state) {
switch (state) {
case VM_PAGE_STATE_FREE:
return "free";
case VM_PAGE_STATE_ALLOC:
return "alloc";
case VM_PAGE_STATE_WIRED:
return "wired";
case VM_PAGE_STATE_HEAP:
return "heap";
case VM_PAGE_STATE_OBJECT:
return "object";
case VM_PAGE_STATE_MMU:
return "mmu";
case VM_PAGE_STATE_IPC:
return "ipc";
default:
return "unknown";
}
}
void vm_page::dump() const {
printf("page %p: address %#" PRIxPTR " state %s flags %#x", this, paddr(),
page_state_to_string(state_priv), flags);
if (state_priv == VM_PAGE_STATE_OBJECT) {
printf(" pin_count %d split_bits %d%d\n", object.pin_count, object.cow_left_split,
object.cow_right_split);
} else {
printf("\n");
}
}
void vm_page::set_state(vm_page_state new_state) {
constexpr uint32_t kMask = (1 << VM_PAGE_STATE_BITS) - 1;
DEBUG_ASSERT_MSG(new_state == (new_state & kMask), "invalid state %u\n", new_state);
const vm_page_state old_state = vm_page_state(state_priv);
state_priv = (new_state & kMask);
// By only modifying the counters for the current CPU with preemption disabled, we can ensure
// the values are not modified concurrently. See comment at the definition of |vm_page_counts|.
percpu::WithCurrentPreemptDisable([&old_state, &new_state](percpu* p) {
// Be sure to not block, else we lose the protection provided by disabling preemption.
p->vm_page_counts.by_state[old_state] -= 1;
p->vm_page_counts.by_state[new_state] += 1;
});
}
uint64_t vm_page::get_count(vm_page_state state) {
int64_t result = 0;
percpu::ForEachPreemptDisable([&state, &result](percpu* p) {
// Because |get_count| could be called concurrently with |set_state| we're not guaranteed to
// get a consistent snapshot of the page counts. It's OK if the values are a little off. See
// comment at the definition of |vm_page_state|.
result += p->vm_page_counts.by_state[state];
});
return result >= 0 ? result : 0;
}
void vm_page::add_to_initial_count(vm_page_state state, uint64_t n) {
percpu::WithCurrentPreemptDisable(
[&state, &n](percpu* p) { p->vm_page_counts.by_state[state] += n; });
}
static int cmd_vm_page(int argc, const cmd_args* argv, uint32_t flags) {
if (argc < 2) {
notenoughargs:
printf("not enough arguments\n");
usage:
printf("usage:\n");
printf("%s dump <address>\n", argv[0].str);
printf("%s hexdump <address>\n", argv[0].str);
return ZX_ERR_INTERNAL;
}
if (!strcmp(argv[1].str, "dump")) {
if (argc < 3) {
goto notenoughargs;
}
vm_page* page = reinterpret_cast<vm_page*>(argv[2].u);
page->dump();
} else if (!strcmp(argv[1].str, "hexdump")) {
if (argc < 3) {
goto notenoughargs;
}
vm_page* page = reinterpret_cast<vm_page*>(argv[2].u);
paddr_t pa = page->paddr();
void* ptr = paddr_to_physmap(pa);
if (!ptr) {
printf("bad page or page not mapped in kernel space\n");
return ZX_ERR_INTERNAL;
}
hexdump(ptr, PAGE_SIZE);
} else {
printf("unknown command\n");
goto usage;
}
return ZX_OK;
}
STATIC_COMMAND_START
STATIC_COMMAND("vm_page", "vm_page debug commands", &cmd_vm_page)
STATIC_COMMAND_END(vm_page)