kernel_liteos_a1/kernel/base/vm/los_vm_phys.c

647 lines
16 KiB
C
Executable File

/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_vm_phys.h"
#include "los_vm_boot.h"
#include "los_vm_common.h"
#include "los_vm_map.h"
#include "los_vm_dump.h"
#include "los_process_pri.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#ifdef LOSCFG_KERNEL_VM
#define ONE_PAGE 1
/* Physical memory area array */
STATIC struct VmPhysArea g_physArea[] = {
{
.start = SYS_MEM_BASE,
.size = SYS_MEM_SIZE_DEFAULT,
},
};
struct VmPhysSeg g_vmPhysSeg[VM_PHYS_SEG_MAX];
INT32 g_vmPhysSegNum = 0;
LosVmPhysSeg *OsGVmPhysSegGet()
{
return g_vmPhysSeg;
}
STATIC VOID OsVmPhysLruInit(struct VmPhysSeg *seg)
{
INT32 i;
UINT32 intSave;
LOS_SpinInit(&seg->lruLock);
LOS_SpinLockSave(&seg->lruLock, &intSave);
for (i = 0; i < VM_NR_LRU_LISTS; i++) {
seg->lruSize[i] = 0;
LOS_ListInit(&seg->lruList[i]);
}
LOS_SpinUnlockRestore(&seg->lruLock, intSave);
}
STATIC INT32 OsVmPhysSegCreate(paddr_t start, size_t size)
{
struct VmPhysSeg *seg = NULL;
if (g_vmPhysSegNum >= VM_PHYS_SEG_MAX) {
return -1;
}
seg = &g_vmPhysSeg[g_vmPhysSegNum++];
for (; (seg > g_vmPhysSeg) && ((seg - 1)->start > (start + size)); seg--) {
*seg = *(seg - 1);
}
seg->start = start;
seg->size = size;
return 0;
}
VOID OsVmPhysSegAdd(VOID)
{
INT32 i, ret;
LOS_ASSERT(g_vmPhysSegNum < VM_PHYS_SEG_MAX);
for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {
ret = OsVmPhysSegCreate(g_physArea[i].start, g_physArea[i].size);
if (ret != 0) {
VM_ERR("create phys seg failed");
}
}
}
VOID OsVmPhysAreaSizeAdjust(size_t size)
{
/*
* The first physics memory segment is used for kernel image and kernel heap,
* so just need to adjust the first one here.
*/
g_physArea[0].start += size;
g_physArea[0].size -= size;
}
UINT32 OsVmPhysPageNumGet(VOID)
{
UINT32 nPages = 0;
INT32 i;
for (i = 0; i < (sizeof(g_physArea) / sizeof(g_physArea[0])); i++) {
nPages += g_physArea[i].size >> PAGE_SHIFT;
}
return nPages;
}
STATIC INLINE VOID OsVmPhysFreeListInit(struct VmPhysSeg *seg)
{
int i;
UINT32 intSave;
struct VmFreeList *list = NULL;
LOS_SpinInit(&seg->freeListLock);
LOS_SpinLockSave(&seg->freeListLock, &intSave);
for (i = 0; i < VM_LIST_ORDER_MAX; i++) {
list = &seg->freeList[i];
LOS_ListInit(&list->node);
list->listCnt = 0;
}
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
VOID OsVmPhysInit(VOID)
{
struct VmPhysSeg *seg = NULL;
UINT32 nPages = 0;
int i;
for (i = 0; i < g_vmPhysSegNum; i++) {
seg = &g_vmPhysSeg[i];
seg->pageBase = &g_vmPageArray[nPages];
nPages += seg->size >> PAGE_SHIFT;
OsVmPhysFreeListInit(seg);
OsVmPhysLruInit(seg);
}
}
STATIC VOID OsVmPhysFreeListAdd(LosVmPage *page, UINT8 order)
{
struct VmPhysSeg *seg = NULL;
struct VmFreeList *list = NULL;
if (page->segID >= VM_PHYS_SEG_MAX) {
LOS_Panic("The page segment id(%d) is invalid\n", page->segID);
}
page->order = order;
seg = &g_vmPhysSeg[page->segID];
list = &seg->freeList[order];
LOS_ListTailInsert(&list->node, &page->node);
list->listCnt++;
}
STATIC VOID OsVmPhysFreeListAddUnsafe(LosVmPage *page, UINT8 order)
{
struct VmPhysSeg *seg = NULL;
struct VmFreeList *list = NULL;
if (page->segID >= VM_PHYS_SEG_MAX) {
LOS_Panic("The page segment id(%d) is invalid\n", page->segID);
}
page->order = order;
seg = &g_vmPhysSeg[page->segID];
list = &seg->freeList[order];
LOS_ListTailInsert(&list->node, &page->node);
list->listCnt++;
}
STATIC VOID OsVmPhysFreeListDelUnsafe(LosVmPage *page)
{
struct VmPhysSeg *seg = NULL;
struct VmFreeList *list = NULL;
if ((page->segID >= VM_PHYS_SEG_MAX) || (page->order >= VM_LIST_ORDER_MAX)) {
LOS_Panic("The page segment id(%u) or order(%u) is invalid\n", page->segID, page->order);
}
seg = &g_vmPhysSeg[page->segID];
list = &seg->freeList[page->order];
list->listCnt--;
LOS_ListDelete(&page->node);
page->order = VM_LIST_ORDER_MAX;
}
STATIC VOID OsVmPhysFreeListDel(LosVmPage *page)
{
struct VmPhysSeg *seg = NULL;
struct VmFreeList *list = NULL;
if ((page->segID >= VM_PHYS_SEG_MAX) || (page->order >= VM_LIST_ORDER_MAX)) {
LOS_Panic("The page segment id(%u) or order(%u) is invalid\n", page->segID, page->order);
}
seg = &g_vmPhysSeg[page->segID];
list = &seg->freeList[page->order];
list->listCnt--;
LOS_ListDelete(&page->node);
page->order = VM_LIST_ORDER_MAX;
}
STATIC VOID OsVmPhysPagesSpiltUnsafe(LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
{
UINT32 order;
LosVmPage *buddyPage = NULL;
for (order = newOrder; order > oldOrder;) {
order--;
buddyPage = &page[VM_ORDER_TO_PAGES(order)];
LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);
OsVmPhysFreeListAddUnsafe(buddyPage, order);
}
}
LosVmPage *OsVmPhysToPage(paddr_t pa, UINT8 segID)
{
struct VmPhysSeg *seg = NULL;
paddr_t offset;
if (segID >= VM_PHYS_SEG_MAX) {
LOS_Panic("The page segment id(%d) is invalid\n", segID);
}
seg = &g_vmPhysSeg[segID];
if ((pa < seg->start) || (pa >= (seg->start + seg->size))) {
return NULL;
}
offset = pa - seg->start;
return (seg->pageBase + (offset >> PAGE_SHIFT));
}
VOID *OsVmPageToVaddr(LosVmPage *page)
{
VADDR_T vaddr;
vaddr = KERNEL_ASPACE_BASE + page->physAddr - SYS_MEM_BASE;
return (VOID *)(UINTPTR)vaddr;
}
LosVmPage *OsVmVaddrToPage(VOID *ptr)
{
struct VmPhysSeg *seg = NULL;
PADDR_T pa = LOS_PaddrQuery(ptr);
UINT32 segID;
for (segID = 0; segID < g_vmPhysSegNum; segID++) {
seg = &g_vmPhysSeg[segID];
if ((pa >= seg->start) && (pa < (seg->start + seg->size))) {
return seg->pageBase + ((pa - seg->start) >> PAGE_SHIFT);
}
}
return NULL;
}
STATIC INLINE VOID OsVmRecycleExtraPages(LosVmPage *page, size_t startPage, size_t endPage)
{
if (startPage >= endPage) {
return;
}
OsVmPhysPagesFreeContiguous(page, endPage - startPage);
}
STATIC LosVmPage *OsVmPhysLargeAlloc(struct VmPhysSeg *seg, size_t nPages)
{
struct VmFreeList *list = NULL;
LosVmPage *page = NULL;
LosVmPage *tmp = NULL;
PADDR_T paStart;
PADDR_T paEnd;
size_t size = nPages << PAGE_SHIFT;
list = &seg->freeList[VM_LIST_ORDER_MAX - 1];
LOS_DL_LIST_FOR_EACH_ENTRY(page, &list->node, LosVmPage, node) {
paStart = page->physAddr;
paEnd = paStart + size;
if (paEnd > (seg->start + seg->size)) {
continue;
}
for (;;) {
paStart += PAGE_SIZE << (VM_LIST_ORDER_MAX - 1);
if ((paStart >= paEnd) || (paStart < seg->start) ||
(paStart >= (seg->start + seg->size))) {
break;
}
tmp = &seg->pageBase[(paStart - seg->start) >> PAGE_SHIFT];
if (tmp->order != (VM_LIST_ORDER_MAX - 1)) {
break;
}
}
if (paStart >= paEnd) {
return page;
}
}
return NULL;
}
STATIC LosVmPage *OsVmPhysPagesAlloc(struct VmPhysSeg *seg, size_t nPages)
{
struct VmFreeList *list = NULL;
LosVmPage *page = NULL;
LosVmPage *tmp = NULL;
UINT32 order;
UINT32 newOrder;
order = OsVmPagesToOrder(nPages);
if (order < VM_LIST_ORDER_MAX) {
for (newOrder = order; newOrder < VM_LIST_ORDER_MAX; newOrder++) {
list = &seg->freeList[newOrder];
if (LOS_ListEmpty(&list->node)) {
continue;
}
page = LOS_DL_LIST_ENTRY(LOS_DL_LIST_FIRST(&list->node), LosVmPage, node);
goto DONE;
}
} else {
newOrder = VM_LIST_ORDER_MAX - 1;
page = OsVmPhysLargeAlloc(seg, nPages);
if (page != NULL) {
goto DONE;
}
}
return NULL;
DONE:
for (tmp = page; tmp < &page[nPages]; tmp = &tmp[1 << newOrder]) {
OsVmPhysFreeListDelUnsafe(tmp);
}
OsVmPhysPagesSpiltUnsafe(page, order, newOrder);
OsVmRecycleExtraPages(&page[nPages], nPages, ROUNDUP(nPages, (1 << min(order, newOrder))));
return page;
}
VOID OsVmPhysPagesFree(LosVmPage *page, UINT8 order)
{
paddr_t pa;
LosVmPage *buddyPage = NULL;
if ((page == NULL) || (order >= VM_LIST_ORDER_MAX)) {
return;
}
if (order < VM_LIST_ORDER_MAX - 1) {
pa = VM_PAGE_TO_PHYS(page);
do {
pa ^= VM_ORDER_TO_PHYS(order);
buddyPage = OsVmPhysToPage(pa, page->segID);
if ((buddyPage == NULL) || (buddyPage->order != order)) {
break;
}
OsVmPhysFreeListDel(buddyPage);
order++;
pa &= ~(VM_ORDER_TO_PHYS(order) - 1);
page = OsVmPhysToPage(pa, page->segID);
} while (order < VM_LIST_ORDER_MAX - 1);
}
OsVmPhysFreeListAdd(page, order);
}
VOID OsVmPhysPagesFreeContiguous(LosVmPage *page, size_t nPages)
{
paddr_t pa;
UINT32 order;
size_t n;
while (TRUE) {
pa = VM_PAGE_TO_PHYS(page);
order = VM_PHYS_TO_ORDER(pa);
n = VM_ORDER_TO_PAGES(order);
if (n > nPages) {
break;
}
OsVmPhysPagesFree(page, order);
nPages -= n;
page += n;
}
while (nPages > 0) {
order = LOS_HighBitGet(nPages);
n = VM_ORDER_TO_PAGES(order);
OsVmPhysPagesFree(page, order);
nPages -= n;
page += n;
}
}
STATIC LosVmPage *OsVmPhysPagesGet(size_t nPages)
{
UINT32 intSave;
struct VmPhysSeg *seg = NULL;
LosVmPage *page = NULL;
UINT32 segID;
for (segID = 0; segID < g_vmPhysSegNum; segID++) {
seg = &g_vmPhysSeg[segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
page = OsVmPhysPagesAlloc(seg, nPages);
if (page != NULL) {
/* the first page of continuous physical addresses holds refCounts */
LOS_AtomicSet(&page->refCounts, 0);
page->nPages = nPages;
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
return page;
}
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
return NULL;
}
VOID *LOS_PhysPagesAllocContiguous(size_t nPages)
{
LosVmPage *page = NULL;
if (nPages == 0) {
return NULL;
}
page = OsVmPhysPagesGet(nPages);
if (page == NULL) {
return NULL;
}
return OsVmPageToVaddr(page);
}
VOID LOS_PhysPagesFreeContiguous(VOID *ptr, size_t nPages)
{
UINT32 intSave;
struct VmPhysSeg *seg = NULL;
LosVmPage *page = NULL;
if (ptr == NULL) {
return;
}
page = OsVmVaddrToPage(ptr);
if (page == NULL) {
VM_ERR("vm page of ptr(%#x) is null", ptr);
return;
}
page->nPages = 0;
seg = &g_vmPhysSeg[page->segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
OsVmPhysPagesFreeContiguous(page, nPages);
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
VADDR_T *LOS_PaddrToKVaddr(PADDR_T paddr)
{
struct VmPhysSeg *seg = NULL;
UINT32 segID;
for (segID = 0; segID < g_vmPhysSegNum; segID++) {
seg = &g_vmPhysSeg[segID];
if ((paddr >= seg->start) && (paddr < (seg->start + seg->size))) {
return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);
}
}
return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);
}
VOID LOS_PhysPageFree(LosVmPage *page)
{
UINT32 intSave;
struct VmPhysSeg *seg = NULL;
if (page == NULL) {
return;
}
if (LOS_AtomicDecRet(&page->refCounts) <= 0) {
seg = &g_vmPhysSeg[page->segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
OsVmPhysPagesFreeContiguous(page, ONE_PAGE);
LOS_AtomicSet(&page->refCounts, 0);
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
}
LosVmPage *LOS_PhysPageAlloc(VOID)
{
return OsVmPhysPagesGet(ONE_PAGE);
}
size_t LOS_PhysPagesAlloc(size_t nPages, LOS_DL_LIST *list)
{
LosVmPage *page = NULL;
size_t count = 0;
if ((list == NULL) || (nPages == 0)) {
return 0;
}
while (nPages--) {
page = OsVmPhysPagesGet(ONE_PAGE);
if (page == NULL) {
break;
}
LOS_ListTailInsert(list, &page->node);
count++;
}
return count;
}
VOID OsPhysSharePageCopy(PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage)
{
UINT32 intSave;
LosVmPage *oldPage = NULL;
VOID *newMem = NULL;
VOID *oldMem = NULL;
LosVmPhysSeg *seg = NULL;
if ((newPage == NULL) || (newPaddr == NULL)) {
VM_ERR("new Page invalid");
return;
}
oldPage = LOS_VmPageGet(oldPaddr);
if (oldPage == NULL) {
VM_ERR("invalid paddr %p", oldPaddr);
return;
}
seg = &g_vmPhysSeg[oldPage->segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
if (LOS_AtomicRead(&oldPage->refCounts) == 1) {
*newPaddr = oldPaddr;
} else {
newMem = LOS_PaddrToKVaddr(*newPaddr);
oldMem = LOS_PaddrToKVaddr(oldPaddr);
if ((newMem == NULL) || (oldMem == NULL)) {
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
return;
}
if (memcpy_s(newMem, PAGE_SIZE, oldMem, PAGE_SIZE) != EOK) {
VM_ERR("memcpy_s failed");
}
LOS_AtomicInc(&newPage->refCounts);
LOS_AtomicDec(&oldPage->refCounts);
}
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
return;
}
struct VmPhysSeg *OsVmPhysSegGet(LosVmPage *page)
{
if ((page == NULL) || (page->segID >= VM_PHYS_SEG_MAX)) {
return NULL;
}
return (OsGVmPhysSegGet() + page->segID);
}
UINT32 OsVmPagesToOrder(size_t nPages)
{
UINT32 order;
for (order = 0; VM_ORDER_TO_PAGES(order) < nPages; order++);
return order;
}
size_t LOS_PhysPagesFree(LOS_DL_LIST *list)
{
UINT32 intSave;
LosVmPage *page = NULL;
LosVmPage *nPage = NULL;
LosVmPhysSeg *seg = NULL;
size_t count = 0;
if (list == NULL) {
return 0;
}
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(page, nPage, list, LosVmPage, node) {
LOS_ListDelete(&page->node);
if (LOS_AtomicDecRet(&page->refCounts) <= 0) {
seg = &g_vmPhysSeg[page->segID];
LOS_SpinLockSave(&seg->freeListLock, &intSave);
OsVmPhysPagesFreeContiguous(page, ONE_PAGE);
LOS_AtomicSet(&page->refCounts, 0);
LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
}
count++;
}
return count;
}
#else
VADDR_T *LOS_PaddrToKVaddr(PADDR_T paddr)
{
if ((paddr < DDR_MEM_ADDR) || (paddr >= (DDR_MEM_ADDR + DDR_MEM_SIZE))) {
return NULL;
}
return (VADDR_T *)DMA_TO_VMM_ADDR(paddr);
}
#endif
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */