| // 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 |
| |
| #ifndef ZIRCON_KERNEL_VM_INCLUDE_VM_PAGE_H_ |
| #define ZIRCON_KERNEL_VM_INCLUDE_VM_PAGE_H_ |
| |
| #include <stdint.h> |
| #include <sys/types.h> |
| #include <zircon/compiler.h> |
| #include <zircon/listnode.h> |
| |
| #include <ktl/atomic.h> |
| #include <ktl/type_traits.h> |
| #include <vm/page_state.h> |
| |
| // core per page structure allocated at pmm arena creation time |
| struct vm_page { |
| struct list_node queue_node; |
| paddr_t paddr_priv; // use paddr() accessor |
| |
| // offset 0x18 |
| |
| union { |
| struct { |
| // This is an optional back pointer to the vm object this page is currently contained in. It |
| // is implicitly valid when the page is in the pager_backed page queue, and not valid |
| // otherwise. Consequently, to prevent data races, this should not be modified (except under |
| // the page queue lock) whilst a page is in a page queue. |
| // Field should be modified by the setters and getters to allow for future encoding changes. |
| void* object_priv; |
| |
| // offset 0x20 |
| |
| // When object is valid, this is the offset in the vmo that contains this page. |
| // Field should be modified by the setters and getters to allow for future encoding changes. |
| uint64_t page_offset_priv; |
| |
| // offset 0x28 |
| |
| // The rotation generation where the page would be in the oldest pager-backed queue when |
| // merging occurs. It is only significant for pages in pager-backed queues to determine which |
| // page counter to update after a rotation. |
| uint32_t pager_queue_merge_rotation_priv; |
| |
| // offset 0x2c |
| |
| // Identifies which queue this page was last added to. This value might be incorrect for pages |
| // that are in the oldest pager-backed queue when queue merging occurrs. This can be detected |
| // by comparing the page's merge rotation to the current rotation generation. |
| uint8_t page_queue_priv; |
| |
| // offset 0x2d |
| |
| void* get_object() const { return object_priv; } |
| void set_object(void* object) { object_priv = object; } |
| uint64_t get_page_offset() const { return page_offset_priv; } |
| void set_page_offset(uint64_t page_offset) { page_offset_priv = page_offset; } |
| uint32_t get_pager_queue_merge_rotation() const { return pager_queue_merge_rotation_priv; } |
| void set_pager_queue_merge_rotation(uint32_t pager_queue_merge_rotation) { |
| pager_queue_merge_rotation_priv = pager_queue_merge_rotation; |
| } |
| uint8_t get_page_queue() const { return page_queue_priv; } |
| void set_page_queue(uint8_t page_queue) { page_queue_priv = page_queue; } |
| |
| #define VM_PAGE_OBJECT_PIN_COUNT_BITS 5 |
| #define VM_PAGE_OBJECT_MAX_PIN_COUNT ((1ul << VM_PAGE_OBJECT_PIN_COUNT_BITS) - 1) |
| uint8_t pin_count : VM_PAGE_OBJECT_PIN_COUNT_BITS; |
| |
| // Bits used by VmObjectPaged implementation of COW clones. |
| // |
| // Pages of VmObjectPaged have two "split" bits. These bits are used to track which |
| // pages in children of hidden VMOs have diverged from their parent. There are two |
| // bits, left and right, one for each child. In a hidden parent, a 1 split bit means |
| // that page in the child has diverged from the parent and the parent's page is |
| // no longer accessible to that child. |
| // |
| // It should never be the case that both split bits are set, as the page should |
| // be moved into the child instead of setting the second bit. |
| uint8_t cow_left_split : 1; |
| uint8_t cow_right_split : 1; |
| // This struct has no type name and exists inside an unpacked parent and so it really doesn't |
| // need to have any padding. By making it packed we allow the next outer variables, to use |
| // space we would have otherwise wasted in padding, without breaking alignment rules. |
| } __PACKED object; // attached to a vm object |
| }; |
| |
| // offset 0x2e |
| |
| // logically private; use |state()| and |set_state()| |
| ktl::atomic<vm_page_state> state_priv; |
| |
| // offset 0x2f |
| |
| // This padding is inserted here to make sizeof(vm_page) a multiple of 8 and help validate that |
| // all commented offsets were indeed correct. |
| char padding; |
| |
| // helper routines |
| bool is_free() const { return state() == vm_page_state::FREE; } |
| |
| void dump() const; |
| |
| // return the physical address |
| // future plan to store in a compressed form |
| paddr_t paddr() const { return paddr_priv; } |
| |
| vm_page_state state() const { return state_priv.load(ktl::memory_order_relaxed); } |
| |
| void set_state(vm_page_state new_state); |
| |
| // Return the approximate number of pages in state |state|. |
| // |
| // When called concurrently with |set_state|, the count may be off by a small amount. |
| static uint64_t get_count(vm_page_state state); |
| |
| // Add |n| to the count of pages in state |state|. |
| // |
| // Should be used when first constructing pages. |
| static void add_to_initial_count(vm_page_state state, uint64_t n); |
| }; |
| |
| // Provide a type alias using modern syntax to avoid clang-tidy warnings. |
| using vm_page_t = vm_page; |
| |
| // assert that the page structure isn't growing uncontrollably |
| static_assert(sizeof(vm_page) == 0x30); |
| |
| // assert that |vm_page| is a POD |
| static_assert(ktl::is_trivial_v<vm_page> && ktl::is_standard_layout_v<vm_page>); |
| |
| #endif // ZIRCON_KERNEL_VM_INCLUDE_VM_PAGE_H_ |