Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * 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)
{
	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)) {
		u32_t address = info->mmap_addr;
		struct multiboot_mmap *mmap;
		int index = 0;
		u32_t type;

		while ((address < (info->mmap_addr + info->mmap_length)) &&
		       (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(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.
		 */

		u16_t adj_x;
		u16_t adj_y;
		u32_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_AND_API_INIT(multiboot_framebuf,
		    "FRAMEBUF",
		    multiboot_framebuf_init,
		    &multiboot_framebuf_data,
		    NULL,
		    PRE_KERNEL_1,
		    CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
		    &framebuf_display_api);

#endif /* CONFIG_MULTIBOOT_FRAMEBUF */

#endif /* CONFIG_MULTIBOOT_INFO */