memobj-r0drv-solaris.c revision c76316b463c608fdd551c7a3aa438a7335a5d93d
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * IPRT - Ring-0 Memory Objects, Solaris.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * Copyright (C) 2006-2012 Oracle Corporation
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * available from http://www.virtualbox.org. This file is free software;
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * you can redistribute it and/or modify it under the terms of the GNU
9ee436b6765f11cddb90819b9c4fc67899ba479bvboxsync * General Public License (GPL) as published by the Free Software
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * The contents of this file may alternatively be used under the terms
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * of the Common Development and Distribution License Version 1.0
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * VirtualBox OSE distribution, in which case the provisions of the
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * CDDL are applicable instead of those of the GPL.
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * You may elect to license modified versions of this file under the
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * terms and conditions of either the GPL or the CDDL or both.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync/*******************************************************************************
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync* Header Files *
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync*******************************************************************************/
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync/*******************************************************************************
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync* Defined Constants And Macros *
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync*******************************************************************************/
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync#define SOL_IS_KRNL_ADDR(vx) ((uintptr_t)(vx) >= kernelbase)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync/*******************************************************************************
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync* Structures and Typedefs *
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync*******************************************************************************/
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * The Solaris version of the memory object structure.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /** The core structure. */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /** Pointer to kernel memory cookie. */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /** Shadow locked pages. */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /** Access during locking. */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /** Set if large pages are involved in an RTR0MEMOBJTYPE_PHYS
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * allocation. */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync/*******************************************************************************
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync* Global Variables *
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync*******************************************************************************/
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * Returns the physical address for a virtual address.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * @param pv The virtual address.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * @returns The physical address corresponding to @a pv.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync PageFrameNum = hat_getpfnum(pHat, (caddr_t)(uVirtAddr & PAGEMASK));
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync AssertReleaseMsg(PageFrameNum != PFN_INVALID, ("rtR0MemObjSolVirtToPhys failed. pv=%p\n", pv));
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync return (((uint64_t)PageFrameNum << PAGE_SHIFT) | (uVirtAddr & PAGE_OFFSET_MASK));
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * Returns the physical address for a page.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * @param pPage Pointer to the page.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * @returns The physical address for a page.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsyncstatic inline uint64_t rtR0MemObjSolPagePhys(page_t *pPage)
b4fc07ae3f394595370fc5ed6ab1c353a87259dbvboxsync AssertReleaseMsg(PageFrameNum != PFN_INVALID, ("rtR0MemObjSolPagePhys failed pPage=%p\n"));
b4fc07ae3f394595370fc5ed6ab1c353a87259dbvboxsync * Allocates one page.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * @param virtAddr The virtual address to which this page maybe mapped in
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * the future.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * @param cbPage The size of the page.
8bed792bc65abd39393889351f22263ce6c289bfvboxsync * @returns Pointer to the allocated page, NULL on failure.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsyncstatic page_t *rtR0MemObjSolPageAlloc(caddr_t virtAddr, size_t cbPage)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync AssertCompileSize(u_offset_t, sizeof(uint64_t)); NOREF(RTASSERTVAR);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync g_offPage = RT_ALIGN_64(g_offPage, cbPage) + cbPage;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync page_t *pPage = page_create_va(&g_PageVnode, offPage, cbPage, PG_WAIT | PG_NORELOC, &KernelSeg, virtAddr);
9ee436b6765f11cddb90819b9c4fc67899ba479bvboxsync * Lock this page into memory "long term" to prevent paging out of this page.
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * Allocates physical, non-contiguous memory of pages.
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * @param uPhysHi The upper physical address limit (inclusive).
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * @param puPhys Where to store the physical address of first page. Optional,
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * can be NULL.
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync * @param cb The size of the allocation.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * @return Array of allocated pages, NULL on failure.
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsyncstatic page_t **rtR0MemObjSolPagesAlloc(uint64_t uPhysHi, uint64_t *puPhys, size_t cb)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * The page freelist and cachelist both hold pages that are not mapped into any address space.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * The cachelist is not really free pages but when memory is exhausted they'll be moved to the
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * free lists, it's the total of the free+cache list that we see on the 'free' column in vmstat.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * @todo Document what happens behind the scenes in VM2 regarding the free and cachelist.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * Non-pageable memory reservation request for _4K pages, don't sleep.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync pgcnt_t cPages = (cb + PAGE_SIZE - 1) >> PAGE_SHIFT;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * Get pages from kseg, the 'virtAddr' here is only for colouring but unfortunately
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * we don't yet have the 'virtAddr' to which this memory may be mapped.
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync for (size_t i = 0; i < cPages; i++, virtAddr += PAGE_SIZE)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * Get a page from the free list locked exclusively. The page will be named (hashed in)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * and we rely on it during free. Downgrade the page to a shared lock to prevent the page
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * from being relocated.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync pPage = rtR0MemObjSolPageAlloc(virtAddr, PAGE_SIZE);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * Check if the physical address backing the page is within the requested range if any.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * If it isn't, discard the page and try again.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /** @todo Remove this constraint here, force all high-limit applicable cases
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * through rtR0SolMemAlloc() */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync page_destroy(pPage, 0 /* move it to the free list */);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * No pages found or found pages didn't meet requirements, release what was grabbed so far.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync for (size_t k = 0; k <= i; k++)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync page_destroy(ppPages[k], 0 /* move it to the free list */);
return NULL;
if (!rc)
if (rc)
if (pRootPage)
* Lock the page into memory "long term". This prevents callers of page_try_demote_pages() (such as the
if (puPhys)
return ppPages;
* The offset must be unique (for the same vnode) or we'll encounter panics on page_create_va_large().
return NULL;
bool fDemoted = false;
if (!rc)
* Check for page demotion (regardless of relocation). Some places in Solaris (e.g. VM1 page_retire())
* could possibly demote the large page to _4K pages between our call to page_unlock() and page_lookup().
if (page_get_pagecnt(pFoundPage->p_szc) == 1) /* Base size of only _4K associated with this page. */
fDemoted = true;
if (fDemoted)
* Unmaps kernel/user-space mapped memory.
faultcode_t rc = as_fault(pProc->p_as->a_hat, pProc->p_as, (caddr_t)pv, cb, F_SOFTLOCK, fPageAccess);
if (rc)
LogRel(("rtR0MemObjSolLock failed for pv=%pv cb=%lx fPageAccess=%d rc=%d\n", pv, cb, fPageAccess, rc));
return VERR_LOCK_FAILED;
return VINF_SUCCESS;
static int rtR0MemObjSolUserMap(caddr_t *pVirtAddr, unsigned fPageAccess, uint64_t *paPhysAddrs, size_t cb, size_t cbPageSize)
case RTR0MEMOBJTYPE_LOW:
case RTR0MEMOBJTYPE_PHYS:
case RTR0MEMOBJTYPE_PHYS_NC:
case RTR0MEMOBJTYPE_PAGE:
case RTR0MEMOBJTYPE_LOCK:
case RTR0MEMOBJTYPE_MAPPING:
case RTR0MEMOBJTYPE_RES_VIRT:
AssertFailed();
return VERR_INTERNAL_ERROR;
return VINF_SUCCESS;
PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_PAGE, NULL, cb);
return VERR_NO_MEMORY;
return VERR_NO_PAGE_MEMORY;
return VINF_SUCCESS;
PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_LOW, NULL, cb);
if (!pMemSolaris)
return VERR_NO_MEMORY;
return VERR_NO_LOW_MEMORY;
return VINF_SUCCESS;
DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest)
PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_PHYS_NC, NULL, cb);
return VERR_NO_MEMORY;
if (!pvPages)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment)
PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_PHYS, NULL, cb);
return VERR_NO_MEMORY;
ASMAtomicWriteU32(&s_cbLargePage, page_get_pagesize(1)); /* Page-size code 1 maps to _2M on Solaris x86/amd64. */
return VINF_SUCCESS;
return VINF_SUCCESS;
return VERR_NO_CONT_MEMORY;
DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy)
PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_PHYS, NULL, cb);
if (!pMemSolaris)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess,
PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_LOCK, (void *)R3Ptr, cb);
if (!pMemSolaris)
return VERR_NO_MEMORY;
return rc;
return VINF_SUCCESS;
DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess)
PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_LOCK, pv, cb);
if (!pMemSolaris)
return VERR_NO_MEMORY;
return rc;
return VINF_SUCCESS;
DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment)
return VERR_NO_MEMORY;
if (!pMemSolaris)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment,
return VERR_NOT_SUPPORTED;
DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
return VERR_NOT_SUPPORTED;
if (!cbSub)
return VERR_MAP_FAILED;
PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_MAPPING, pv, cbSub);
if (pMemSolaris)
return VINF_SUCCESS;
if (off)
return rc;
DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, PRTR0MEMOBJINTERNAL pMemToMap, RTR3PTR R3PtrFixed,
AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED);
return VERR_NOT_SUPPORTED;
return VERR_NO_MEMORY;
return VINF_SUCCESS;
return rc;
DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
return VERR_NOT_SUPPORTED;
case RTR0MEMOBJTYPE_PHYS_NC:
case RTR0MEMOBJTYPE_PAGE:
case RTR0MEMOBJTYPE_LOW:
case RTR0MEMOBJTYPE_LOCK:
case RTR0MEMOBJTYPE_MAPPING:
case RTR0MEMOBJTYPE_CONT:
case RTR0MEMOBJTYPE_PHYS:
case RTR0MEMOBJTYPE_RES_VIRT:
return NIL_RTHCPHYS;