From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from duke.felloff.net ([216.126.196.34]) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fjRvL-0004Pu-EL for kexec@lists.infradead.org; Sat, 28 Jul 2018 16:18:33 +0000 Message-ID: <6D6FF68EA186BC822F2508F6706F352F@felloff.net> Subject: multiboot non-elf and framebuffer support for kexec-tools Date: Sat, 28 Jul 2018 18:18:11 +0200 From: cinap_lenrek@felloff.net MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="upas-iabbbdgcrkdbexftfsjmzyosau" List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "kexec" Errors-To: kexec-bounces+dwmw2=infradead.org@lists.infradead.org To: kexec@lists.infradead.org This is a multi-part message in MIME format. --upas-iabbbdgcrkdbexftfsjmzyosau Content-Disposition: inline Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit theres a patch that provides support for non-elf multiboot kernels and passes framebuffer information to the kernel if requested in the multiboot header. only rgb truecolor framebuffers are supported right now. if --reset-vga or --console-vga was specified, it assumes the framebuffer will be standard ega text mode. also pass acpi nvs and acpi reclaim ranges in the multiboot memory map as such; instead of just reserved. this allowing to kernel to locate acpi tables on uefi systems. i'm new on the list, so is there a prefered address to send patches like this to? -- cinap --upas-iabbbdgcrkdbexftfsjmzyosau Content-Disposition: inline Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit diff --git a/include/x86/mb_info.h b/include/x86/mb_info.h index 317bdfa..fd1bb1d 100644 --- a/include/x86/mb_info.h +++ b/include/x86/mb_info.h @@ -172,6 +172,28 @@ struct multiboot_info uint16_t vbe_interface_seg; uint16_t vbe_interface_off; uint16_t vbe_interface_len; + + uint64_t framebuffer_addr; + uint32_t framebuffer_pitch; + uint32_t framebuffer_width; + uint32_t framebuffer_height; + uint8_t framebuffer_bpp; + uint8_t framebuffer_type; + + union { + struct { + uint32_t framebuffer_palette_addr; + uint16_t framebuffer_palette_num_color; + }; + struct { + uint8_t framebuffer_red_field_position; + uint8_t framebuffer_red_mask_size; + uint8_t framebuffer_green_field_position; + uint8_t framebuffer_green_mask_size; + uint8_t framebuffer_blue_field_position; + uint8_t framebuffer_blue_mask_size; + }; + }; }; /* @@ -211,6 +233,11 @@ struct multiboot_info /* Is there video information? */ #define MB_INFO_VIDEO_INFO 0x00000800 +#define MB_INFO_FRAMEBUFFER_INFO 0x00001000 + +#define MB_FRAMEBUFFER_TYPE_INDEXED 0 +#define MB_FRAMEBUFFER_TYPE_RGB 1 +#define MB_FRAMEBUFFER_TYPE_EGA_TEXT 2 /* * The following value must be present in the EAX register. diff --git a/kexec/arch/i386/kexec-multiboot-x86.c b/kexec/arch/i386/kexec-multiboot-x86.c index 69027e2..9bba4c1 100644 --- a/kexec/arch/i386/kexec-multiboot-x86.c +++ b/kexec/arch/i386/kexec-multiboot-x86.c @@ -7,9 +7,6 @@ * * TODO: * - smarter allocation of new segments - * - proper support for the MULTIBOOT_VIDEO_MODE bit - * - support for the MULTIBOOT_AOUT_KLUDGE bit - * * * Copyright (C) 2003 Tim Deegan (tjd21 at cl.cam.ac.uk) * @@ -56,21 +53,23 @@ #include #include +/* Framebuffer */ +#include +#include + /* Static storage */ static char headerbuf[MULTIBOOT_SEARCH]; static struct multiboot_header *mbh = NULL; +static off_t mbh_offset = 0; -#define MIN(_x,_y) (((_x)<=(_y))?(_x):(_y)) +extern struct arch_options_t arch_options; +#define MIN(_x,_y) (((_x)<=(_y))?(_x):(_y)) int multiboot_x86_probe(const char *buf, off_t buf_len) /* Is it a good idea to try booting this file? */ { int i, len; - /* First of all, check that this is an ELF file */ - if ((i=elf_x86_probe(buf, buf_len)) < 0) { - return i; - } /* Now look for a multiboot header in the first 8KB */ len = MULTIBOOT_SEARCH; if (len > buf_len) { @@ -81,10 +80,10 @@ int multiboot_x86_probe(const char *buf, off_t buf_len) /* Short file */ return -1; } - for (i = 0; i <= (len - 12); i += 4) + for (mbh_offset = 0; mbh_offset <= (len - 12); mbh_offset += 4) { /* Search for a multiboot header */ - mbh = (struct multiboot_header *)(headerbuf + i); + mbh = (struct multiboot_header *)(headerbuf + mbh_offset); if (mbh->magic != MULTIBOOT_MAGIC || ((mbh->magic+mbh->flags+mbh->checksum) & 0xffffffff)) { @@ -92,13 +91,34 @@ int multiboot_x86_probe(const char *buf, off_t buf_len) continue; } if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) { - /* Requires options we don't support */ - fprintf(stderr, - "Found a multiboot header, but it uses " - "a non-ELF header layout,\n" - "and I can't do that (yet). Sorry.\n"); - return -1; - } + if (mbh->load_addr & 0xfff) { + fprintf(stderr, "multiboot load address not 4k aligned\n"); + return -1; + } + if (mbh->load_addr > mbh->header_addr) { + fprintf(stderr, "multiboot header address > load address\n"); + return -1; + } + if (mbh->load_end_addr < mbh->load_addr) { + fprintf(stderr, "multiboot load end address < load address\n"); + return -1; + } + if (mbh->bss_end_addr < mbh->load_end_addr) { + fprintf(stderr, "multiboot bss end address < load end address\n"); + return -1; + } + if (mbh->load_end_addr - mbh->header_addr > buf_len - mbh_offset) { + fprintf(stderr, "multiboot file truncated\n"); + return -1; + } + if (mbh->entry_addr < mbh->load_addr || mbh->entry_addr >= mbh->load_end_addr) { + fprintf(stderr, "multiboot entry out of range\n"); + return -1; + } + } else { + if ((i=elf_x86_probe(buf, buf_len)) < 0) + return i; + } if (mbh->flags & MULTIBOOT_UNSUPPORTED) { /* Requires options we don't support */ fprintf(stderr, @@ -107,16 +127,6 @@ int multiboot_x86_probe(const char *buf, off_t buf_len) "don't understand. Sorry.\n"); return -1; } - if (mbh->flags & MULTIBOOT_VIDEO_MODE) { - /* Asked for screen mode information */ - /* XXX carry on regardless */ - fprintf(stderr, - "BEWARE! Found a multiboot header which asks " - "for screen mode information.\n" - "BEWARE! I am NOT supplying screen mode " - "information, but loading it regardless.\n"); - - } /* Bootable */ return 0; } @@ -134,6 +144,75 @@ void multiboot_x86_usage(void) printf(" (can be used multiple times).\n"); } +static int framebuffer_info(struct multiboot_info *mbi) +{ + struct fb_fix_screeninfo info; + struct fb_var_screeninfo mode; + int fd; + + /* check if purgatory will reset to standard ega text mode */ + if (arch_options.reset_vga || arch_options.console_vga) { + mbi->framebuffer_type = MB_FRAMEBUFFER_TYPE_EGA_TEXT; + mbi->framebuffer_addr = 0xb8000; + mbi->framebuffer_pitch = 80*2; + mbi->framebuffer_width = 80; + mbi->framebuffer_height = 25; + mbi->framebuffer_bpp = 16; + + mbi->flags |= MB_INFO_FRAMEBUFFER_INFO; + return 0; + } + + /* use current graphics framebuffer settings */ + fd = open("/dev/fb0", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "can't open /dev/fb0: %s\n", strerror(errno)); + return -1; + } + if (ioctl(fd, FBIOGET_FSCREENINFO, &info) < 0){ + fprintf(stderr, "can't get screeninfo: %s\n", strerror(errno)); + close(fd); + return -1; + } + if (ioctl(fd, FBIOGET_VSCREENINFO, &mode) < 0){ + fprintf(stderr, "can't get modeinfo: %s\n", strerror(errno)); + close(fd); + return -1; + } + close(fd); + + if (info.smem_start == 0 || info.smem_len == 0) { + fprintf(stderr, "can't get linerar framebuffer address\n"); + return -1; + } + + if (info.type != FB_TYPE_PACKED_PIXELS) { + fprintf(stderr, "unsupported framebuffer type\n"); + return -1; + } + + if (info.visual != FB_VISUAL_TRUECOLOR) { + fprintf(stderr, "unsupported framebuffer visual\n"); + return -1; + } + + mbi->framebuffer_type = MB_FRAMEBUFFER_TYPE_RGB; + mbi->framebuffer_addr = info.smem_start; + mbi->framebuffer_pitch = info.line_length; + mbi->framebuffer_width = mode.xres; + mbi->framebuffer_height = mode.yres; + mbi->framebuffer_bpp = mode.bits_per_pixel; + mbi->framebuffer_red_field_position = mode.red.offset; + mbi->framebuffer_red_mask_size = mode.red.length; + mbi->framebuffer_green_field_position = mode.green.offset; + mbi->framebuffer_green_mask_size = mode.green.length; + mbi->framebuffer_blue_field_position = mode.blue.offset; + mbi->framebuffer_blue_mask_size = mode.blue.length; + + mbi->flags |= MB_INFO_FRAMEBUFFER_INFO; + return 0; +} + int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) /* Marshal up a multiboot-style kernel */ @@ -154,7 +233,7 @@ int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, struct AddrRangeDesc *mmap; int command_line_len; int i, result; - uint32_t u; + uint32_t u, entry; int opt; int modules, mod_command_line_space; /* See options.h -- add any more there, too. */ @@ -211,8 +290,18 @@ int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, } command_line_len = strlen(command_line) + 1; - /* Load the ELF executable */ - elf_exec_build_load(info, &ehdr, buf, len, 0); + if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) { + add_segment(info, + buf + (mbh_offset - (mbh->header_addr - mbh->load_addr)), + mbh->load_end_addr - mbh->load_addr, + mbh->load_addr, + mbh->bss_end_addr - mbh->load_addr); + entry = mbh->entry_addr; + } else { + /* Load the ELF executable */ + elf_exec_build_load(info, &ehdr, buf, len, 0); + entry = ehdr.e_entry; + } /* Load the setup code */ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0, @@ -259,7 +348,8 @@ int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, mmap[i].base_addr_high = range[i].start >> 32; mmap[i].length_low = length & 0xffffffff; mmap[i].length_high = length >> 32; - if (range[i].type == RANGE_RAM) { + switch (range[i].type) { + case RANGE_RAM: mmap[i].Type = 1; /* RAM */ /* * Is this the "low" memory? Can't just test @@ -277,7 +367,15 @@ int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, if ((range[i].start <= 0x100000) && (range[i].end > mem_upper + 0x100000)) mem_upper = range[i].end - 0x100000; - } else { + break; + case RANGE_ACPI: + mmap[i].Type = 3; + break; + case RANGE_ACPI_NVS: + mmap[i].Type = 4; + break; + case RANGE_RESERVED: + default: mmap[i].Type = 2; /* Not RAM (reserved) */ } } @@ -302,6 +400,12 @@ int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, /* done */ } + /* Video */ + if (mbh->flags & MULTIBOOT_VIDEO_MODE) { + if (framebuffer_info(mbi) < 0) + fprintf(stderr, "not providing framebuffer information.\n"); + } + /* Load modules */ if (modules) { char *mod_filename, *mod_command_line, *mod_clp, *buf; @@ -384,7 +488,7 @@ int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); regs.eax = 0x2BADB002; regs.ebx = mbi_offset; - regs.eip = ehdr.e_entry; + regs.eip = entry; elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); out: --upas-iabbbdgcrkdbexftfsjmzyosau Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec --upas-iabbbdgcrkdbexftfsjmzyosau--