This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 0ed4123326a2121897f3ae5d8b13d227030df1e4 Author: Brennan Ashton <bash...@brennanashton.com> AuthorDate: Tue Aug 4 03:29:05 2020 -0700 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 <bash...@brennanashton.com> x86_64: Add conditionals around the multiboot framebuffer --- .../x86_64/qemu/boards/qemu-intel64/index.rst | 19 ++ arch/x86_64/Kconfig | 19 +- arch/x86_64/src/common/x86_64_internal.h | 8 +- arch/x86_64/src/intel64/Make.defs | 4 + arch/x86_64/src/intel64/intel64_head.S | 14 +- arch/x86_64/src/intel64/intel64_lowsetup.c | 45 +++ arch/x86_64/src/intel64/intel64_mbfb.c | 309 +++++++++++++++++++++ arch/x86_64/src/intel64/intel64_serial.c | 4 + .../intel64/qemu-intel64/configs/earlyfb/defconfig | 72 +++++ 9 files changed, 488 insertions(+), 6 deletions(-) diff --git a/Documentation/platforms/x86_64/qemu/boards/qemu-intel64/index.rst b/Documentation/platforms/x86_64/qemu/boards/qemu-intel64/index.rst index d1fe76186d..c927ad922a 100644 --- a/Documentation/platforms/x86_64/qemu/boards/qemu-intel64/index.rst +++ b/Documentation/platforms/x86_64/qemu/boards/qemu-intel64/index.rst @@ -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 ===== diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index fac4bb7d61..5e1115ad0e 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -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 diff --git a/arch/x86_64/src/common/x86_64_internal.h b/arch/x86_64/src/common/x86_64_internal.h index da5a5a451e..f98d9605bf 100644 --- a/arch/x86_64/src/common/x86_64_internal.h +++ b/arch/x86_64/src/common/x86_64_internal.h @@ -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 diff --git a/arch/x86_64/src/intel64/Make.defs b/arch/x86_64/src/intel64/Make.defs index 010b56acd6..c2d6db49aa 100644 --- a/arch/x86_64/src/intel64/Make.defs +++ b/arch/x86_64/src/intel64/Make.defs @@ -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 diff --git a/arch/x86_64/src/intel64/intel64_head.S b/arch/x86_64/src/intel64/intel64_head.S index 34c7bdf8da..f22b8eb322 100644 --- a/arch/x86_64/src/intel64/intel64_head.S +++ b/arch/x86_64/src/intel64/intel64_head.S @@ -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 diff --git a/arch/x86_64/src/intel64/intel64_lowsetup.c b/arch/x86_64/src/intel64/intel64_lowsetup.c index 51f04e2009..988a9523ff 100644 --- a/arch/x86_64/src/intel64/intel64_lowsetup.c +++ b/arch/x86_64/src/intel64/intel64_lowsetup.c @@ -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(); diff --git a/arch/x86_64/src/intel64/intel64_mbfb.c b/arch/x86_64/src/intel64/intel64_mbfb.c new file mode 100644 index 0000000000..f536cffb35 --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_mbfb.c @@ -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 */ diff --git a/arch/x86_64/src/intel64/intel64_serial.c b/arch/x86_64/src/intel64/intel64_serial.c index 00896fc1f8..dffd6304c9 100644 --- a/arch/x86_64/src/intel64/intel64_serial.c +++ b/arch/x86_64/src/intel64/intel64_serial.c @@ -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) diff --git a/boards/x86_64/intel64/qemu-intel64/configs/earlyfb/defconfig b/boards/x86_64/intel64/qemu-intel64/configs/earlyfb/defconfig new file mode 100644 index 0000000000..3342336c68 --- /dev/null +++ b/boards/x86_64/intel64/qemu-intel64/configs/earlyfb/defconfig @@ -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