/****************************************************************************
 * mm/umm_heap/umm_initialize.c
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <assert.h>

#include <nuttx/arch.h>
#include <nuttx/mm/mm.h>

#include "umm_heap/umm_heap.h"

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: umm_initialize
 *
 * Description:
 *   This is a simple wrapper for the mm_initialize() function.  This
 *   function will initialize the user heap.
 *
 *   CONFIG_BUILD_FLAT:
 *     There is only kernel mode "blob" containing both kernel and
 *     application code. Depending upon the setting of CONFIG_MM_KERNEL_HEAP
 *     there may be a single shared heap (used by both the kernel and
 *     application logic) or there may be separate (although unprotected)
 *     kernel and user heaps.
 *
 *     In this configuration, this function is called early in nx_start()
 *     to initialize the common heap.
 *
 *   CONFIG_BUILD_PROTECTED
 *     In this configuration, there are two "blobs", one containing
 *     protected kernel logic and one containing unprotected application
 *     logic.  Depending upon the setting of CONFIG_MM_KERNEL_HEAP there
 *     may be only a single shared heap, much as with CONFIG_BUILD_FLAT.
 *     Or there may be separate protected/kernel and unprotected/user
 *     heaps.
 *
 *     In either case, this function is still called early in nx_start()
 *     to initialize the user heap.
 *
 *   CONFIG_BUILD_KERNEL
 *     In this configuration there are multiple user heaps, one for each
 *     user process.  Furthermore, each heap is initially empty; memory
 *     is added to each heap dynamically via sbrk().  The heap data
 *     structure was set to zero when the address environment was created.
 *     Otherwise, the heap is uninitialized.
 *
 *     This function is not called at all.  Rather, this function is called
 *     when each user process is created before the first allocation is made.
 *
 * Input Parameters:
 *   heap_start - Address of the beginning of the (initial) memory region
 *   heap_size  - The size (in bytes) if the (initial) memory region.
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

void umm_initialize(FAR void *heap_start, size_t heap_size)
{
#if defined(CONFIG_BUILD_PROTECTED) && defined(__KERNEL__)
/* In the protected mode, there are two heaps:  A kernel heap and a single
 * user heap.  Kernel code must obtain the address of the user heap data
 * structure from the userspace interface.
 */

  FAR struct mm_heap_s **heap = &USERSPACE_HEAP;
#else
  /* Otherwise, the user heap data structures are in common .bss */

  FAR struct mm_heap_s **heap = &g_mmheap;
#endif
  struct mm_heap_config_s config;
#ifdef CONFIG_MM_POOL_PARAM
  const struct mm_pool_config_s pool_config =
    {
      CONFIG_MM_POOL_PARAM
    };
#endif

  memset(&config, 0, sizeof(config));
  config.start = heap_start;
  config.size  = heap_size;
#ifndef CONFIG_BUILD_KERNEL
  config.name = "Umem";
#endif

#ifdef CONFIG_MM_POOL_PARAM
  mm_initialize_pool(&config, &pool_config, heap);
#else
  mm_initialize_pool(&config, NULL, heap);
#endif
}

/****************************************************************************
 * Name: umm_try_initialize
 *
 * Description:
 *   Allocate and initialize the user heap if not yet.
 *
 * Input Parameters:
 * None
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_BUILD_KERNEL
void umm_try_initialize(void)
{
  uintptr_t allocbase;
  size_t npages = 1;

  /* Return if the user heap is already initialized. */

  if (USR_HEAP != NULL)
    {
      return;
    }

#ifdef CONFIG_MM_KASAN
  /* we have to commit all memory for the shadow region */

  npages = CONFIG_ARCH_HEAP_NPAGES;
#endif

  /* If we provide a zero brkaddr to pgalloc(),
   * it will create the first block in the correct virtual address
   * space and return the start address of that block.
   */

  allocbase = pgalloc(0, npages);
  DEBUGASSERT(allocbase != 0);

  /* Let umm_initialize do the real work. */

  umm_initialize((FAR void *)allocbase, npages * CONFIG_MM_PGSIZE);
}
#endif