text: /*
   * Copyright (c) 2019 Intel Corporation
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <kernel.h>
  #include <string.h>
  #include <arch/x86/multiboot.h>
  #include <arch/x86/memmap.h>
  
  #ifdef CONFIG_MULTIBOOT_INFO
  
  struct multiboot_info multiboot_info;
  
  /*
   * called very early in the boot process to fetch data out of the multiboot
   * info struct. we need to grab the relevant data before any dynamic memory
   * allocation takes place, lest the struct itself or any data it points to
   * be overwritten before we read it.
   */
  
  static inline void clear_memmap(int index)
  {
  	while (index < CONFIG_X86_MEMMAP_ENTRIES) {
  		x86_memmap[index].type = X86_MEMMAP_ENTRY_UNUSED;
  		++index;
  	}
  }
  
  void z_multiboot_init(struct multiboot_info *info_pa)
  {
  	struct multiboot_info *info;
  
  #if defined(CONFIG_ARCH_MAPS_ALL_RAM) || !defined(CONFIG_X86_MMU)
  	/*
  	 * Since the struct from bootloader resides in memory
  	 * and all memory is mapped, there is no need to
  	 * manually map it before accessing.
  	 *
  	 * Without MMU, all memory are identity-mapped already
  	 * so there is no need to map them again.
  	 */
  	info = info_pa;
  #else
  	z_phys_map((uint8_t **)&info, POINTER_TO_UINT(info_pa),
  		   sizeof(*info_pa), K_MEM_CACHE_NONE);
  #endif /* CONFIG_ARCH_MAPS_ALL_RAM */
  
  	if (info != NULL) {
  		memcpy(&multiboot_info, info, sizeof(*info));
  	}
  
  #ifdef CONFIG_MULTIBOOT_MEMMAP
  	/*
  	 * If the extended map (basically, the equivalent of
  	 * the BIOS E820 map) is available, then use that.
  	 */
  
  	if ((info->flags & MULTIBOOT_INFO_FLAGS_MMAP) &&
  	    (x86_memmap_source < X86_MEMMAP_SOURCE_MULTIBOOT_MMAP)) {
  		uintptr_t address;
  		uintptr_t address_end;
  		struct multiboot_mmap *mmap;
  		int index = 0;
  		uint32_t type;
  
  #if defined(CONFIG_ARCH_MAPS_ALL_RAM) || !defined(CONFIG_X86_MMU)
  		address = info->mmap_addr;
  #else
  		uint8_t *address_va;
  
  		z_phys_map(&address_va, info->mmap_addr, info->mmap_length,
  			   K_MEM_CACHE_NONE);
  
  		address = POINTER_TO_UINT(address_va);
  #endif /* CONFIG_ARCH_MAPS_ALL_RAM */
  
  		address_end = address + info->mmap_length;
  
  		while ((address < address_end) &&
  		       (index < CONFIG_X86_MEMMAP_ENTRIES)) {
  			mmap = UINT_TO_POINTER(address);
  
  			x86_memmap[index].base = mmap->base;
  			x86_memmap[index].length = mmap->length;
  
  			switch (mmap->type) {
  			case MULTIBOOT_MMAP_RAM:
  				type = X86_MEMMAP_ENTRY_RAM;
  				break;
  			case MULTIBOOT_MMAP_ACPI:
  				type = X86_MEMMAP_ENTRY_ACPI;
  				break;
  			case MULTIBOOT_MMAP_NVS:
  				type = X86_MEMMAP_ENTRY_NVS;
  				break;
  			case MULTIBOOT_MMAP_DEFECTIVE:
  				type = X86_MEMMAP_ENTRY_DEFECTIVE;
  				break;
  			default:
  				type = X86_MEMMAP_ENTRY_UNKNOWN;
  			}
  
  			x86_memmap[index].type = type;
  			++index;
  			address += mmap->size + sizeof(mmap->size);
  		}
  
  		x86_memmap_source = X86_MEMMAP_SOURCE_MULTIBOOT_MMAP;
  		clear_memmap(index);
  	}
  
  	/* If no extended map is available, fall back to the basic map. */
  
  	if ((info->flags & MULTIBOOT_INFO_FLAGS_MEM) &&
  	    (x86_memmap_source < X86_MEMMAP_SOURCE_MULTIBOOT_MEM)) {
  		x86_memmap[0].base = 0;
  		x86_memmap[0].length = info->mem_lower * 1024ULL;
  		x86_memmap[0].type = X86_MEMMAP_ENTRY_RAM;
  
  		if (CONFIG_X86_MEMMAP_ENTRIES > 1) {
  			x86_memmap[1].base = 1048576U; /* 1MB */
  			x86_memmap[1].length = info->mem_upper * 1024ULL;
  			x86_memmap[1].type = X86_MEMMAP_ENTRY_RAM;
  			clear_memmap(2);
  		}
  
  		x86_memmap_source = X86_MEMMAP_SOURCE_MULTIBOOT_MEM;
  	}
  #endif /* CONFIG_MULTIBOOT_MEMMAP */
  }
  
  #ifdef CONFIG_MULTIBOOT_FRAMEBUF
  
  #include <display/framebuf.h>
  
  static struct framebuf_dev_data multiboot_framebuf_data = {
  	.width = CONFIG_MULTIBOOT_FRAMEBUF_X,
  	.height = CONFIG_MULTIBOOT_FRAMEBUF_Y
  };
  
  static int multiboot_framebuf_init(const struct device *dev)
  {
  	struct framebuf_dev_data *data = FRAMEBUF_DATA(dev);
  	struct multiboot_info *info = &multiboot_info;
  
  	if ((info->flags & MULTIBOOT_INFO_FLAGS_FB) &&
  	    (info->fb_width >= CONFIG_MULTIBOOT_FRAMEBUF_X) &&
  	    (info->fb_height >= CONFIG_MULTIBOOT_FRAMEBUF_Y) &&
  	    (info->fb_bpp == 32) && (info->fb_addr_hi == 0)) {
  		/*
  		 * We have a usable multiboot framebuffer - it is 32 bpp
  		 * and at least as large as the requested dimensions. Compute
  		 * the pitch and adjust the start address center our canvas.
  		 */
  
  		uint16_t adj_x;
  		uint16_t adj_y;
  		uint32_t *buffer;
  
  		adj_x = info->fb_width - CONFIG_MULTIBOOT_FRAMEBUF_X;
  		adj_y = info->fb_height - CONFIG_MULTIBOOT_FRAMEBUF_Y;
  		data->pitch = (info->fb_pitch / 4) + adj_x;
  		adj_x /= 2;
  		adj_y /= 2;
  		buffer = (uint32_t *) (uintptr_t) info->fb_addr_lo;
  		buffer += adj_x;
  		buffer += adj_y * data->pitch;
  		data->buffer = buffer;
  		return 0;
  	} else {
  		return -ENOTSUP;
  	}
  }
  
  DEVICE_DEFINE(multiboot_framebuf,
  		    "FRAMEBUF",
  		    multiboot_framebuf_init,
  		    device_pm_control_nop,
  		    &multiboot_framebuf_data,
  		    NULL,
  		    PRE_KERNEL_1,
  		    CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
  		    &framebuf_display_api);
  
  #endif /* CONFIG_MULTIBOOT_FRAMEBUF */
  
  #endif /* CONFIG_MULTIBOOT_INFO */
  