x86_64: Early framebuffer console

This adds support for creating an early frame buffer and primatives for
writing to this frame buffer as a console. This does require the font
infrastructure as well as multiboot2.

Additionally this can now be used with a UEFI bootloader long as it
boots NuttX via Multiboot2.  There does seem to be a PCI interrupt
issue when running in UEFI mode.

I was able to boot my laptop using this and see PCI devices enumerate.

Signed-off-by: Brennan Ashton <bashton@brennanashton.com>

x86_64: Add conditionals around the multiboot framebuffer
This commit is contained in:
Brennan Ashton 2020-08-04 03:29:05 -07:00 committed by Xiang Xiao
parent 69ed5bb67d
commit 0ed4123326
9 changed files with 488 additions and 6 deletions

View File

@ -36,6 +36,22 @@ P.S. In some distros, ``grub-mkrescue`` is called ``grub2-mkrescue``::
grub-mkrescue -o boot.iso iso
Grub with UEFI
--------------
This flow is very similar except you need to have the BOOTX64.EFI file.
You can find this in most Linux distributions::
iso/
└── boot
├── efi
│ └── EFI
│ └── BOOT
│ └── BOOTX64.EFI
├── grub
│ └── grub.cfg
└── nuttx.elf
QEMU/KVM
========
@ -79,6 +95,9 @@ with the pcitest NuttX configuration::
This will enable the QEMU pci-test and edu PCI test devices which test PIO, MMIO, IRQ, and DMA
functions. Additionally it will show detailed information about the enumeration of the PCI bus.
If you want to boot using UEFI and TianoCore you will need to add a flag like this to
point at OVMF ``--bios /usr/share/edk2/ovmf/OVMF_CODE.fd``
Bochs
=====

View File

@ -63,10 +63,21 @@ config ARCH_BOARD
endif # ARCH_CHIP_QEMU
config ARCH_EXCLUDE_MULTIBOOT
bool "Don't append multiboot2 header"
default n
config ARCH_MULTIBOOT2
bool "Append multiboot2 header"
default y
---help---
Some platforms, e.g. jailhouse, do not like to have a multiboot header
Include a multiboot2 header. This also provides information to the
system to enable certain features like the low level framebuffer.
if ARCH_MULTIBOOT2
config MULTBOOT2_FB_TERM
bool "Multiboot2 framebuffer terminal"
default n
depends on NXFONTS
---help---
Enable a framebuffer terminal for early debug printing
endif # ARCH_MULTIBOOT2
endif # ARCH_X86_64

View File

@ -32,6 +32,7 @@
# include <nuttx/sched.h>
# include <stdint.h>
# include <arch/io.h>
# include <arch/multiboot2.h>
#endif
/****************************************************************************
@ -63,7 +64,7 @@
# undef USE_SERIALDRIVER
# undef USE_EARLYSERIALINIT
# undef CONFIG_DEV_LOWCONSOLE
# else
# elif defined(CONFIG_16550_UART)
# define USE_SERIALDRIVER 1
# define USE_EARLYSERIALINIT 1
# endif
@ -195,6 +196,11 @@ void x86_64_checktasks(void);
void x86_64_syscall(uint64_t *regs);
#ifdef CONFIG_ARCH_MULTIBOOT2
void x86_64_mb2_fbinitialize(struct multiboot_tag_framebuffer *tag);
void fb_putc(char ch);
#endif
/* Defined in up_allocateheap.c */
#if CONFIG_MM_REGIONS > 1

View File

@ -38,6 +38,10 @@ CHIP_CSRCS += intel64_serial.c intel64_rng.c intel64_check_capability.c
# Configuration-dependent intel64 files
ifeq ($(CONFIG_ARCH_MULTIBOOT2),y)
CHIP_CSRCS += intel64_mbfb.c
endif
ifneq ($(CONFIG_SCHED_TICKLESS),y)
CHIP_CSRCS += intel64_timerisr.c
endif

View File

@ -65,6 +65,8 @@
.global nx_start /* nx_start is defined elsewhere */
.global up_lowsetup /* up_lowsetup is defined elsewhere */
.global g_idle_topstack /* The end of the idle stack, the start of the heap */
.global mb_info_struct
.global mb_magic
/* These are the page tables */
.global pdpt_low
@ -89,7 +91,7 @@
.align 8
header_start:
#ifndef CONFIG_ARCH_EXCLUDE_MULTIBOOT
#ifdef CONFIG_ARCH_MULTIBOOT2
.long MULTIBOOT2_HEADER_MAGIC
.long MULTIBOOT_ARCHITECTURE_I386
.long HEADER_LENGTH
@ -97,6 +99,12 @@ header_start:
// multiboot tags go here
.short MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST
.short 0 // flags, none set
.long 16 // size, including itself (short + short + long)
.long MULTIBOOT_TAG_TYPE_EFI64
.long MULTIBOOT_TAG_TYPE_FRAMEBUFFER
.short MULTIBOOT_HEADER_TAG_END
.short 0 // flags, none set
.long 8 // size, including itself (short + short + long)
@ -159,6 +167,10 @@ start32_0:
.type __pmode_entry, @function
__pmode_entry:
start32:
#ifdef CONFIG_ARCH_MULTIBOOT2
movl %ebx, mb_info_struct
movl %eax, mb_magic
#endif
// initialize rest of the page directory
lea pd_low, %edi

View File

@ -26,6 +26,7 @@
#include <nuttx/arch.h>
#include <arch/board/board.h>
#include <arch/multiboot2.h>
#include "x86_64_internal.h"
@ -52,10 +53,48 @@ volatile uint64_t *pt;
volatile struct ist_s *ist64;
volatile struct gdt_entry_s *gdt64;
/* This holds information passed by the multiboot2 bootloader */
uint32_t mb_magic __attribute__((section(".loader.bss")));
uint32_t mb_info_struct __attribute__((section(".loader.bss")));
/****************************************************************************
* Private Functions
****************************************************************************/
static void x86_64_mb2_config(void)
{
struct multiboot_tag *tag;
/* Check that we were actually booted by a mulitboot2 bootloader */
if (mb_magic != MULTIBOOT2_BOOTLOADER_MAGIC)
return;
for (tag = (struct multiboot_tag *)(uintptr_t)(mb_info_struct + 8);
tag->type != MULTIBOOT_TAG_TYPE_END;
tag = (struct multiboot_tag *)((uint8_t *)tag + ((tag->size + 7) & ~7)))
{
switch (tag->type)
{
case MULTIBOOT_TAG_TYPE_EFI64:
{
break;
}
case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
{
x86_64_mb2_fbinitialize(
(struct multiboot_tag_framebuffer *)tag);
break;
}
default:
break;
}
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -94,6 +133,10 @@ void up_lowsetup(void)
x86_64_check_and_enable_capability();
/* Handle multiboot2 info */
x86_64_mb2_config();
/* Revoke the lower memory */
__revoke_low_memory();
@ -102,9 +145,11 @@ void up_lowsetup(void)
x86_64_boardinitialize();
#ifdef USE_EARLYSERIALINIT
/* Early serial driver initialization */
x86_64_earlyserialinit();
#endif
x86_64_timer_calibrate_freq();

View File

@ -0,0 +1,309 @@
/****************************************************************************
* arch/x86_64/src/intel64/intel64_lowsetup.c
*
* 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 <string.h>
#include <nuttx/arch.h>
#include <arch/board/board.h>
#include <arch/multiboot2.h>
#ifdef CONFIG_MULTBOOT2_FB_TERM
#include <nuttx/nx/nxfonts.h>
#endif
#include "up_internal.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
struct multiboot_fb_s
{
void *baseaddr;
uint32_t height;
uint32_t width;
uint32_t pitch;
uint8_t bpp;
uint8_t type;
};
#ifdef CONFIG_MULTBOOT2_FB_TERM
struct fb_term_s
{
const struct nx_fontpackage_s *font;
uint32_t cursor_x;
uint32_t cursor_y;
};
void fb_term_initialize(void);
#endif /* CONFIG_MULTBOOT2_FB_TERM */
void fb_clear(void);
/****************************************************************************
* Private Data
****************************************************************************/
struct multiboot_fb_s fb =
{
.baseaddr = NULL
};
#ifdef CONFIG_MULTBOOT2_FB_TERM
struct fb_term_s fb_term;
#endif /* CONFIG_MULTBOOT2_FB_TERM */
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Function: fb_draw_pixel
*
* Description:
* Draw a pixel on the framebuffer. Note that the color paramter must
* be in the format specified by the bpp of the framebuffer.
*
****************************************************************************/
static void fb_draw_pixel(uint32_t color, uint32_t x, uint32_t y)
{
/* Check if we support this type of framebuffer */
if (fb.type != MULTIBOOT_FRAMEBUFFER_TYPE_RGB)
return;
/* Make sure we are within the bounds */
if (x >= fb.width || y >= fb.height)
return;
switch (fb.bpp)
{
case 8:
{
uint8_t *pixel = (uint8_t *)(
(uintptr_t)fb.baseaddr + (fb.pitch * y) + x);
*pixel = (uint8_t)color;
break;
}
case 15:
case 16:
{
uint16_t *pixel = (uint16_t *)(
(uintptr_t)fb.baseaddr + (fb.pitch * y) + x * 2);
*pixel = (uint16_t)color;
break;
}
case 24:
{
/* We have to be careful here to not overwrite the lower 8bits
* of the next pixel in the buffer.
*/
uint32_t *pixel = (uint32_t *)(
(uintptr_t)fb.baseaddr + (fb.pitch * y) + x * 3);
*pixel = (color & 0xffffff) | (*pixel & 0xff000000);
break;
}
case 32:
{
uint32_t *pixel = (uint32_t *)(
(uintptr_t)fb.baseaddr + (fb.pitch * y) + x * 4);
*pixel = color;
break;
}
}
}
#if 0
/****************************************************************************
* Function: fb_test_line
*
* Description:
* This is a simple test function that can be used to draw a 45deg
* line across the screen.
*
****************************************************************************/
static void fb_test_line(void)
{
size_t idx;
uint32_t color;
switch (fb.bpp)
{
case 8:
color = 0xff;
break;
case 15:
case 16:
color = 0x7fff;
break;
case 24:
color = 0xffffff;
break;
case 32:
color = 0xffffffff;
break;
default:
return;
}
for (idx = 0; (idx < fb.height) && (idx < fb.width); idx++)
{
fb_draw_pixel(color, idx, idx);
}
}
#endif
#ifdef CONFIG_MULTBOOT2_FB_TERM
static void fb_scroll(void)
{
void *destp = fb.baseaddr;
uint32_t save_rows = ((fb.height / fb_term.font->metrics.mxheight) - 1);
size_t row_size = fb.pitch * fb_term.font->metrics.mxheight;
uint32_t pxl_row = 0;
for (; pxl_row < save_rows * fb_term.font->metrics.mxheight; pxl_row++)
{
memcpy(destp, destp + row_size, fb.pitch);
destp += fb.pitch;
}
memset(destp, 0, fb.pitch * (fb.height - pxl_row));
fb_term.cursor_y -= fb_term.font->metrics.mxheight;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
void x86_64_mb2_fbinitialize(struct multiboot_tag_framebuffer *fbt)
{
fb.baseaddr = (void *)(uintptr_t)fbt->common.framebuffer_addr;
fb.width = fbt->common.framebuffer_width;
fb.height = fbt->common.framebuffer_height;
fb.pitch = fbt->common.framebuffer_pitch;
fb.bpp = fbt->common.framebuffer_bpp;
fb.type = fbt->common.framebuffer_type;
up_map_region(fb.baseaddr, fb.pitch * fb.height,
X86_PAGE_WR | X86_PAGE_PRESENT |
X86_PAGE_NOCACHE | X86_PAGE_GLOBAL);
fb_clear();
#ifdef CONFIG_MULTBOOT2_FB_TERM
fb_term_initialize();
#endif
}
void fb_clear(void)
{
if (fb.baseaddr == NULL)
return;
memset(fb.baseaddr, 0, fb.pitch * fb.height);
}
#ifdef CONFIG_MULTBOOT2_FB_TERM
void fb_term_initialize(void)
{
fb_term.font = nxf_getfonthandle(FONTID_DEFAULT);
fb_term.cursor_x = 0;
fb_term.cursor_y = 0;
}
void fb_putc(char ch)
{
uint8_t gly_x;
uint8_t gly_y;
const struct nx_fontbitmap_s *fbm;
if (fb.baseaddr == NULL)
return;
if (ch == '\n')
{
fb_term.cursor_y += fb_term.font->metrics.mxheight;
return;
}
if (ch == '\r')
{
fb_term.cursor_x = 0;
return;
}
fbm = nxf_getbitmap((NXHANDLE)fb_term.font, ch);
if (fbm == NULL)
{
fb_putc('.');
return;
}
for (gly_y = 0; gly_y < fbm->metric.height; gly_y++)
{
if (fb_term.cursor_y + gly_y >= fb.height)
{
fb_scroll();
fb_putc(ch);
return;
}
for (gly_x = 0; gly_x < fbm->metric.width; gly_x++)
{
if (fb_term.cursor_x + gly_x >= fb.width)
{
break;
}
uint8_t stride = (fbm->metric.width + 7) >> 3;
uint8_t gly_byte = stride * gly_y + (gly_x >> 3);
uint8_t gly_bit = gly_x & 0x7;
uint32_t color = 0; /* Black no matter the color depth */
if ((fbm->bitmap[gly_byte] >> (7 - gly_bit)) & 0x01)
color = 0xffffffff; /* Black no matter the color depth */
fb_draw_pixel(
color, fb_term.cursor_x + gly_x, fb_term.cursor_y + gly_y);
}
}
fb_term.cursor_x += fbm->metric.width;
}
#endif /* CONFIG_MULTBOOT2_FB_TERM */

View File

@ -97,6 +97,10 @@ int up_putc(int ch)
return ch;
}
void up_lowputc(char ch)
{
fb_putc(ch);
}
#endif /* USE_SERIALDRIVER */
void x86_64_earlyserialinit(void)

View File

@ -0,0 +1,72 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_SERIAL is not set
CONFIG_ARCH="x86_64"
CONFIG_ARCH_BOARD="qemu-intel64"
CONFIG_ARCH_BOARD_INTEL64_QEMU=y
CONFIG_ARCH_CHIP="intel64"
CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ=2600000
CONFIG_ARCH_SIZET_LONG=y
CONFIG_ARCH_X86_64=y
CONFIG_BOARD_LOOPSPERMSEC=999
CONFIG_BOOT_RUNFROMEXTSRAM=y
CONFIG_BUILTIN=y
CONFIG_CLOCK_MONOTONIC=y
CONFIG_CONSOLE_SYSLOG=y
CONFIG_DEBUG_ERROR=y
CONFIG_DEBUG_FEATURES=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_PCI=y
CONFIG_DEBUG_PCI_ERROR=y
CONFIG_DEBUG_PCI_INFO=y
CONFIG_DEBUG_PCI_WARN=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEBUG_WARN=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_EXAMPLES_HELLO_STACKSIZE=4194304
CONFIG_FS_PROCFS=y
CONFIG_IDLETHREAD_STACKSIZE=4194304
CONFIG_LIBM=y
CONFIG_MAX_TASKS=64
CONFIG_MULTBOOT2_FB_TERM=y
CONFIG_NFILE_DESCRIPTORS=32
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_DISABLE_IFCONFIG=y
CONFIG_NSH_DISABLE_IFUPDOWN=y
CONFIG_NSH_READLINE=y
CONFIG_NXFONTS=y
CONFIG_NXFONT_MONO5X8=y
CONFIG_PREALLOC_CHILDSTATUS=16
CONFIG_PRIORITY_INHERITANCE=y
CONFIG_PTHREAD_MUTEX_TYPES=y
CONFIG_PTHREAD_STACK_DEFAULT=4194304
CONFIG_PTHREAD_STACK_MIN=4194304
CONFIG_QEMU_PCI=y
CONFIG_RAM_SIZE=268435456
CONFIG_SCHED_CHILD_STATUS=y
CONFIG_SCHED_HAVE_PARENT=y
CONFIG_SCHED_IRQMONITOR=y
CONFIG_SCHED_TICKLESS=y
CONFIG_SCHED_TICKLESS_ALARM=y
CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP=y
CONFIG_SCHED_WAITPID=y
CONFIG_SDCLONE_DISABLE=y
CONFIG_SIG_DEFAULT=y
CONFIG_START_DAY=3
CONFIG_START_MONTH=3
CONFIG_START_YEAR=2011
CONFIG_SYSTEM_CLE=y
CONFIG_SYSTEM_NSH=y
CONFIG_SYSTEM_TIME64=y
CONFIG_USEC_PER_TICK=1
CONFIG_USERMAIN_STACKSIZE=4194304
CONFIG_USER_ENTRYPOINT="nsh_main"
CONFIG_VIRT=y
CONFIG_VIRT_QEMU_EDU=y
CONFIG_VIRT_QEMU_PCI_TEST=y