Index: head/sys/arm64/include/metadata.h =================================================================== --- head/sys/arm64/include/metadata.h (revision 324994) +++ head/sys/arm64/include/metadata.h (revision 324995) @@ -1,41 +1,54 @@ /*- * Copyright (c) 2014 Andrew Turner * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_METADATA_H_ #define _MACHINE_METADATA_H_ #define MODINFOMD_EFI_MAP 0x1001 #define MODINFOMD_DTBP 0x1002 +#define MODINFOMD_EFI_FB 0x1003 struct efi_map_header { size_t memory_size; size_t descriptor_size; uint32_t descriptor_version; +}; + +struct efi_fb { + uint64_t fb_addr; + uint64_t fb_size; + uint32_t fb_height; + uint32_t fb_width; + uint32_t fb_stride; + uint32_t fb_mask_red; + uint32_t fb_mask_green; + uint32_t fb_mask_blue; + uint32_t fb_mask_reserved; }; #endif /* !_MACHINE_METADATA_H_ */ Index: head/sys/boot/efi/loader/Makefile =================================================================== --- head/sys/boot/efi/loader/Makefile (revision 324994) +++ head/sys/boot/efi/loader/Makefile (revision 324995) @@ -1,163 +1,164 @@ # $FreeBSD$ MAN= .include MK_SSP= no PROG= loader.sym INTERNALPROG= WARNS?= 3 LOADER_NET_SUPPORT?= yes LOADER_MSDOS_SUPPORT?= yes LOADER_UFS_SUPPORT?= yes LOADER_CD9660_SUPPORT?= no LOADER_EXT2FS_SUPPORT?= no # architecture-specific loader code SRCS= autoload.c \ bootinfo.c \ conf.c \ copy.c \ efi_main.c \ + framebuffer.c \ main.c \ self_reloc.c \ smbios.c \ vers.c .if ${MK_ZFS} != "no" SRCS+= zfs.c .PATH: ${.CURDIR}/../../zfs SRCS+= skein.c skein_block.c # Do not unroll skein loops, reduce code size CFLAGS+= -DSKEIN_LOOP=111 .PATH: ${.CURDIR}/../../../crypto/skein # Disable warnings that are currently incompatible with the zfs boot code CWARNFLAGS.zfs.c+= -Wno-sign-compare CWARNFLAGS.zfs.c+= -Wno-array-bounds CWARNFLAGS.zfs.c+= -Wno-missing-prototypes .endif .if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201 CWARNFLAGS.self_reloc.c+= -Wno-error=maybe-uninitialized .endif # We implement a slightly non-standard %S in that it always takes a # CHAR16 that's common in UEFI-land instead of a wchar_t. This only # seems to matter on arm64 where wchar_t defaults to an int instead # of a short. There's no good cast to use here so just ignore the # warnings for now. CWARNFLAGS.main.c+= -Wno-format .PATH: ${.CURDIR}/arch/${MACHINE} # For smbios.c .PATH: ${.CURDIR}/../../i386/libi386 .include "${.CURDIR}/arch/${MACHINE}/Makefile.inc" CFLAGS+= -I${.CURDIR} CFLAGS+= -I${.CURDIR}/arch/${MACHINE} CFLAGS+= -I${.CURDIR}/../include CFLAGS+= -I${.CURDIR}/../include/${MACHINE} CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/include CFLAGS+= -I${.CURDIR}/../../.. CFLAGS+= -I${.CURDIR}/../../i386/libi386 .if ${MK_ZFS} != "no" CFLAGS+= -I${.CURDIR}/../../zfs CFLAGS+= -I${.CURDIR}/../../../cddl/boot/zfs CFLAGS+= -I${.CURDIR}/../../../crypto/skein CFLAGS+= -DEFI_ZFS_BOOT .endif CFLAGS+= -DNO_PCI -DEFI .if !defined(BOOT_HIDE_SERIAL_NUMBERS) # Export serial numbers, UUID, and asset tag from loader. CFLAGS+= -DSMBIOS_SERIAL_NUMBERS .if defined(BOOT_LITTLE_ENDIAN_UUID) # Use little-endian UUID format as defined in SMBIOS 2.6. CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID .elif defined(BOOT_NETWORK_ENDIAN_UUID) # Use network-endian UUID format for backward compatibility. CFLAGS+= -DSMBIOS_NETWORK_ENDIAN_UUID .endif .endif LOADER_FDT_SUPPORT?= no .if ${MK_FDT} != "no" && ${LOADER_FDT_SUPPORT} != "no" CFLAGS+= -I${.CURDIR}/../../fdt CFLAGS+= -I${.OBJDIR}/../../fdt CFLAGS+= -DLOADER_FDT_SUPPORT LIBEFI_FDT= ${.OBJDIR}/../../efi/fdt/libefi_fdt.a LIBFDT= ${.OBJDIR}/../../fdt/libfdt.a .endif # Include bcache code. HAVE_BCACHE= yes .if defined(EFI_STAGING_SIZE) CFLAGS+= -DEFI_STAGING_SIZE=${EFI_STAGING_SIZE} .endif # Always add MI sources .include "../../loader.mk" FILES+= loader.efi FILESMODE_loader.efi= ${BINMODE} LDSCRIPT= ${.CURDIR}/arch/${MACHINE}/ldscript.${MACHINE} LDFLAGS+= -Wl,-T${LDSCRIPT},-Bsymbolic,-znotext -shared CLEANFILES+= loader.efi NEWVERSWHAT= "EFI loader" ${MACHINE} NM?= nm OBJCOPY?= objcopy .if ${MACHINE_CPUARCH} == "amd64" EFI_TARGET= efi-app-x86_64 .elif ${MACHINE_CPUARCH} == "i386" EFI_TARGET= efi-app-ia32 .else EFI_TARGET= binary .endif # Arbitrarily set the PE/COFF header timestamps to 1 Jan 2016 00:00:00 # for build reproducibility. SOURCE_DATE_EPOCH?=1451606400 loader.efi: ${PROG} if ${NM} ${.ALLSRC} | grep ' U '; then \ echo "Undefined symbols in ${.ALLSRC}"; \ exit 1; \ fi SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \ ${OBJCOPY} -j .peheader -j .text -j .sdata -j .data \ -j .dynamic -j .dynsym -j .rel.dyn \ -j .rela.dyn -j .reloc -j .eh_frame -j set_Xcommand_set \ -j set_Xficl_compile_set \ --output-target=${EFI_TARGET} ${.ALLSRC} ${.TARGET} LIBEFI= ${.OBJDIR}/../libefi/libefi.a DPADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSA} \ ${LDSCRIPT} LDADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSA} .include beforedepend ${OBJS}: machine CLEANFILES+= machine machine: .NOMETA ln -sf ${.CURDIR}/../../../${MACHINE}/include machine .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" beforedepend ${OBJS}: x86 CLEANFILES+= x86 x86: .NOMETA ln -sf ${.CURDIR}/../../../x86/include x86 .endif Index: head/sys/boot/efi/loader/arch/amd64/framebuffer.c =================================================================== --- head/sys/boot/efi/loader/arch/amd64/framebuffer.c (revision 324994) +++ head/sys/boot/efi/loader/arch/amd64/framebuffer.c (nonexistent) @@ -1,568 +0,0 @@ -/*- - * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. - * - * This software was developed by Benno Rice under sponsorship from - * the FreeBSD Foundation. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "framebuffer.h" - -static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; -static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID; -static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID; - -static u_int -efifb_color_depth(struct efi_fb *efifb) -{ - uint32_t mask; - u_int depth; - - mask = efifb->fb_mask_red | efifb->fb_mask_green | - efifb->fb_mask_blue | efifb->fb_mask_reserved; - if (mask == 0) - return (0); - for (depth = 1; mask != 1; depth++) - mask >>= 1; - return (depth); -} - -static int -efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt, - EFI_PIXEL_BITMASK *pixinfo) -{ - int result; - - result = 0; - switch (pixfmt) { - case PixelRedGreenBlueReserved8BitPerColor: - efifb->fb_mask_red = 0x000000ff; - efifb->fb_mask_green = 0x0000ff00; - efifb->fb_mask_blue = 0x00ff0000; - efifb->fb_mask_reserved = 0xff000000; - break; - case PixelBlueGreenRedReserved8BitPerColor: - efifb->fb_mask_red = 0x00ff0000; - efifb->fb_mask_green = 0x0000ff00; - efifb->fb_mask_blue = 0x000000ff; - efifb->fb_mask_reserved = 0xff000000; - break; - case PixelBitMask: - efifb->fb_mask_red = pixinfo->RedMask; - efifb->fb_mask_green = pixinfo->GreenMask; - efifb->fb_mask_blue = pixinfo->BlueMask; - efifb->fb_mask_reserved = pixinfo->ReservedMask; - break; - default: - result = 1; - break; - } - return (result); -} - -static int -efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode, - EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info) -{ - int result; - - efifb->fb_addr = mode->FrameBufferBase; - efifb->fb_size = mode->FrameBufferSize; - efifb->fb_height = info->VerticalResolution; - efifb->fb_width = info->HorizontalResolution; - efifb->fb_stride = info->PixelsPerScanLine; - result = efifb_mask_from_pixfmt(efifb, info->PixelFormat, - &info->PixelInformation); - return (result); -} - -static ssize_t -efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line, - EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size) -{ - EFI_UGA_PIXEL pix0, pix1; - uint8_t *data1, *data2; - size_t count, maxcount = 1024; - ssize_t ofs; - EFI_STATUS status; - u_int idx; - - status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer, - 0, line, 0, 0, 1, 1, 0); - if (EFI_ERROR(status)) { - printf("UGA BLT operation failed (video->buffer)"); - return (-1); - } - pix1.Red = ~pix0.Red; - pix1.Green = ~pix0.Green; - pix1.Blue = ~pix0.Blue; - pix1.Reserved = 0; - - data1 = calloc(maxcount, 2); - if (data1 == NULL) { - printf("Unable to allocate memory"); - return (-1); - } - data2 = data1 + maxcount; - - ofs = 0; - while (size > 0) { - count = min(size, maxcount); - - status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, - EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, - data1); - if (EFI_ERROR(status)) { - printf("Error reading frame buffer (before)"); - goto fail; - } - status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo, - 0, 0, 0, line, 1, 1, 0); - if (EFI_ERROR(status)) { - printf("UGA BLT operation failed (modify)"); - goto fail; - } - status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, - EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, - data2); - if (EFI_ERROR(status)) { - printf("Error reading frame buffer (after)"); - goto fail; - } - status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo, - 0, 0, 0, line, 1, 1, 0); - if (EFI_ERROR(status)) { - printf("UGA BLT operation failed (restore)"); - goto fail; - } - for (idx = 0; idx < count; idx++) { - if (data1[idx] != data2[idx]) { - free(data1); - return (ofs + (idx & ~3)); - } - } - ofs += count; - size -= count; - } - printf("No change detected in frame buffer"); - - fail: - printf(" -- error %lu\n", EFI_ERROR_CODE(status)); - free(data1); - return (-1); -} - -static EFI_PCI_IO_PROTOCOL * -efifb_uga_get_pciio(void) -{ - EFI_PCI_IO_PROTOCOL *pciio; - EFI_HANDLE *buf, *hp; - EFI_STATUS status; - UINTN bufsz; - - /* Get all handles that support the UGA protocol. */ - bufsz = 0; - status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL); - if (status != EFI_BUFFER_TOO_SMALL) - return (NULL); - buf = malloc(bufsz); - status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf); - if (status != EFI_SUCCESS) { - free(buf); - return (NULL); - } - bufsz /= sizeof(EFI_HANDLE); - - /* Get the PCI I/O interface of the first handle that supports it. */ - pciio = NULL; - for (hp = buf; hp < buf + bufsz; hp++) { - status = BS->HandleProtocol(*hp, &pciio_guid, (void **)&pciio); - if (status == EFI_SUCCESS) { - free(buf); - return (pciio); - } - } - free(buf); - return (NULL); -} - -static EFI_STATUS -efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp, - uint64_t *sizep) -{ - uint8_t *resattr; - uint64_t addr, size; - EFI_STATUS status; - u_int bar; - - if (pciio == NULL) - return (EFI_DEVICE_ERROR); - - /* Attempt to get the frame buffer address (imprecise). */ - *addrp = 0; - *sizep = 0; - for (bar = 0; bar < 6; bar++) { - status = pciio->GetBarAttributes(pciio, bar, NULL, - (void **)&resattr); - if (status != EFI_SUCCESS) - continue; - /* XXX magic offsets and constants. */ - if (resattr[0] == 0x87 && resattr[3] == 0) { - /* 32-bit address space descriptor (MEMIO) */ - addr = le32dec(resattr + 10); - size = le32dec(resattr + 22); - } else if (resattr[0] == 0x8a && resattr[3] == 0) { - /* 64-bit address space descriptor (MEMIO) */ - addr = le64dec(resattr + 14); - size = le64dec(resattr + 38); - } else { - addr = 0; - size = 0; - } - BS->FreePool(resattr); - if (addr == 0 || size == 0) - continue; - - /* We assume the largest BAR is the frame buffer. */ - if (size > *sizep) { - *addrp = addr; - *sizep = size; - } - } - return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0); -} - -static int -efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga) -{ - EFI_PCI_IO_PROTOCOL *pciio; - char *ev, *p; - EFI_STATUS status; - ssize_t offset; - uint64_t fbaddr; - uint32_t horiz, vert, stride; - uint32_t np, depth, refresh; - - status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh); - if (EFI_ERROR(status)) - return (1); - efifb->fb_height = vert; - efifb->fb_width = horiz; - /* Paranoia... */ - if (efifb->fb_height == 0 || efifb->fb_width == 0) - return (1); - - /* The color masks are fixed AFAICT. */ - efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor, - NULL); - - /* pciio can be NULL on return! */ - pciio = efifb_uga_get_pciio(); - - /* Try to find the frame buffer. */ - status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr, - &efifb->fb_size); - if (EFI_ERROR(status)) { - efifb->fb_addr = 0; - efifb->fb_size = 0; - } - - /* - * There's no reliable way to detect the frame buffer or the - * offset within the frame buffer of the visible region, nor - * the stride. Our only option is to look at the system and - * fill in the blanks based on that. Luckily, UGA was mostly - * only used on Apple hardware. - */ - offset = -1; - ev = getenv("smbios.system.maker"); - if (ev != NULL && !strcmp(ev, "Apple Inc.")) { - ev = getenv("smbios.system.product"); - if (ev != NULL && !strcmp(ev, "iMac7,1")) { - /* These are the expected values we should have. */ - horiz = 1680; - vert = 1050; - fbaddr = 0xc0000000; - /* These are the missing bits. */ - offset = 0x10000; - stride = 1728; - } else if (ev != NULL && !strcmp(ev, "MacBook3,1")) { - /* These are the expected values we should have. */ - horiz = 1280; - vert = 800; - fbaddr = 0xc0000000; - /* These are the missing bits. */ - offset = 0x0; - stride = 2048; - } - } - - /* - * If this is hardware we know, make sure that it looks familiar - * before we accept our hardcoded values. - */ - if (offset >= 0 && efifb->fb_width == horiz && - efifb->fb_height == vert && efifb->fb_addr == fbaddr) { - efifb->fb_addr += offset; - efifb->fb_size -= offset; - efifb->fb_stride = stride; - return (0); - } else if (offset >= 0) { - printf("Hardware make/model known, but graphics not " - "as expected.\n"); - printf("Console may not work!\n"); - } - - /* - * The stride is equal or larger to the width. Often it's the - * next larger power of two. We'll start with that... - */ - efifb->fb_stride = efifb->fb_width; - do { - np = efifb->fb_stride & (efifb->fb_stride - 1); - if (np) { - efifb->fb_stride |= (np - 1); - efifb->fb_stride++; - } - } while (np); - - ev = getenv("hw.efifb.address"); - if (ev == NULL) { - if (efifb->fb_addr == 0) { - printf("Please set hw.efifb.address and " - "hw.efifb.stride.\n"); - return (1); - } - - /* - * The visible part of the frame buffer may not start at - * offset 0, so try to detect it. Note that we may not - * always be able to read from the frame buffer, which - * means that we may not be able to detect anything. In - * that case, we would take a long time scanning for a - * pixel change in the frame buffer, which would have it - * appear that we're hanging, so we limit the scan to - * 1/256th of the frame buffer. This number is mostly - * based on PR 202730 and the fact that on a MacBoook, - * where we can't read from the frame buffer the offset - * of the visible region is 0. In short: we want to scan - * enough to handle all adapters that have an offset - * larger than 0 and we want to scan as little as we can - * to not appear to hang when we can't read from the - * frame buffer. - */ - offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr, - efifb->fb_size >> 8); - if (offset == -1) { - printf("Unable to reliably detect frame buffer.\n"); - } else if (offset > 0) { - efifb->fb_addr += offset; - efifb->fb_size -= offset; - } - } else { - offset = 0; - efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4; - efifb->fb_addr = strtoul(ev, &p, 0); - if (*p != '\0') - return (1); - } - - ev = getenv("hw.efifb.stride"); - if (ev == NULL) { - if (pciio != NULL && offset != -1) { - /* Determine the stride. */ - offset = efifb_uga_find_pixel(uga, 1, pciio, - efifb->fb_addr, horiz * 8); - if (offset != -1) - efifb->fb_stride = offset >> 2; - } else { - printf("Unable to reliably detect the stride.\n"); - } - } else { - efifb->fb_stride = strtoul(ev, &p, 0); - if (*p != '\0') - return (1); - } - - /* - * We finalized on the stride, so recalculate the size of the - * frame buffer. - */ - efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4; - return (0); -} - -int -efi_find_framebuffer(struct efi_fb *efifb) -{ - EFI_GRAPHICS_OUTPUT *gop; - EFI_UGA_DRAW_PROTOCOL *uga; - EFI_STATUS status; - - status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop); - if (status == EFI_SUCCESS) - return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info)); - - status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga); - if (status == EFI_SUCCESS) - return (efifb_from_uga(efifb, uga)); - - return (1); -} - -static void -print_efifb(int mode, struct efi_fb *efifb, int verbose) -{ - u_int depth; - - if (mode >= 0) - printf("mode %d: ", mode); - depth = efifb_color_depth(efifb); - printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height, - depth, efifb->fb_stride); - if (verbose) { - printf("\n frame buffer: address=%jx, size=%jx", - (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size); - printf("\n color mask: R=%08x, G=%08x, B=%08x\n", - efifb->fb_mask_red, efifb->fb_mask_green, - efifb->fb_mask_blue); - } -} - -COMMAND_SET(gop, "gop", "graphics output protocol", command_gop); - -static int -command_gop(int argc, char *argv[]) -{ - struct efi_fb efifb; - EFI_GRAPHICS_OUTPUT *gop; - EFI_STATUS status; - u_int mode; - - status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop); - if (EFI_ERROR(status)) { - snprintf(command_errbuf, sizeof(command_errbuf), - "%s: Graphics Output Protocol not present (error=%lu)", - argv[0], EFI_ERROR_CODE(status)); - return (CMD_ERROR); - } - - if (argc < 2) - goto usage; - - if (!strcmp(argv[1], "set")) { - char *cp; - - if (argc != 3) - goto usage; - mode = strtol(argv[2], &cp, 0); - if (cp[0] != '\0') { - sprintf(command_errbuf, "mode is an integer"); - return (CMD_ERROR); - } - status = gop->SetMode(gop, mode); - if (EFI_ERROR(status)) { - snprintf(command_errbuf, sizeof(command_errbuf), - "%s: Unable to set mode to %u (error=%lu)", - argv[0], mode, EFI_ERROR_CODE(status)); - return (CMD_ERROR); - } - } else if (!strcmp(argv[1], "get")) { - if (argc != 2) - goto usage; - efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info); - print_efifb(gop->Mode->Mode, &efifb, 1); - printf("\n"); - } else if (!strcmp(argv[1], "list")) { - EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; - UINTN infosz; - - if (argc != 2) - goto usage; - pager_open(); - for (mode = 0; mode < gop->Mode->MaxMode; mode++) { - status = gop->QueryMode(gop, mode, &infosz, &info); - if (EFI_ERROR(status)) - continue; - efifb_from_gop(&efifb, gop->Mode, info); - print_efifb(mode, &efifb, 0); - if (pager_output("\n")) - break; - } - pager_close(); - } - return (CMD_OK); - - usage: - snprintf(command_errbuf, sizeof(command_errbuf), - "usage: %s [list | get | set ]", argv[0]); - return (CMD_ERROR); -} - -COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga); - -static int -command_uga(int argc, char *argv[]) -{ - struct efi_fb efifb; - EFI_UGA_DRAW_PROTOCOL *uga; - EFI_STATUS status; - - status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga); - if (EFI_ERROR(status)) { - snprintf(command_errbuf, sizeof(command_errbuf), - "%s: UGA Protocol not present (error=%lu)", - argv[0], EFI_ERROR_CODE(status)); - return (CMD_ERROR); - } - - if (argc != 1) - goto usage; - - if (efifb_from_uga(&efifb, uga) != CMD_OK) { - snprintf(command_errbuf, sizeof(command_errbuf), - "%s: Unable to get UGA information", argv[0]); - return (CMD_ERROR); - } - - print_efifb(-1, &efifb, 1); - printf("\n"); - return (CMD_OK); - - usage: - snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]); - return (CMD_ERROR); -} Property changes on: head/sys/boot/efi/loader/arch/amd64/framebuffer.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/boot/efi/loader/arch/amd64/framebuffer.h =================================================================== --- head/sys/boot/efi/loader/arch/amd64/framebuffer.h (revision 324994) +++ head/sys/boot/efi/loader/arch/amd64/framebuffer.h (nonexistent) @@ -1,36 +0,0 @@ -/*- - * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. - * - * This software was developed by Benno Rice under sponsorship from - * the FreeBSD Foundation. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _EFIFB_H_ -#define _EFIFB_H_ - -int efi_find_framebuffer(struct efi_fb *efifb); - -#endif /* _EFIFB_H_ */ Property changes on: head/sys/boot/efi/loader/arch/amd64/framebuffer.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/boot/efi/loader/arch/amd64/Makefile.inc =================================================================== --- head/sys/boot/efi/loader/arch/amd64/Makefile.inc (revision 324994) +++ head/sys/boot/efi/loader/arch/amd64/Makefile.inc (revision 324995) @@ -1,16 +1,15 @@ # $FreeBSD$ SRCS+= amd64_tramp.S \ start.S \ - framebuffer.c \ elf64_freebsd.c \ trap.c \ exc.S .PATH: ${.CURDIR}/../../i386/libi386 SRCS+= nullconsole.c \ comconsole.c \ spinconsole.c CFLAGS+= -fPIC -DTERM_EMU LDFLAGS+= -Wl,-znocombreloc Index: head/sys/boot/efi/loader/bootinfo.c =================================================================== --- head/sys/boot/efi/loader/bootinfo.c (revision 324994) +++ head/sys/boot/efi/loader/bootinfo.c (revision 324995) @@ -1,470 +1,471 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 2004, 2006 Marcel Moolenaar * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "bootstrap.h" #include "loader_efi.h" #if defined(__amd64__) #include -#include "framebuffer.h" #endif +#include "framebuffer.h" + #if defined(LOADER_FDT_SUPPORT) #include #endif int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp); extern EFI_SYSTEM_TABLE *ST; static const char howto_switches[] = "aCdrgDmphsv"; static int howto_masks[] = { RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE, RB_MUTE, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE }; static int bi_getboothowto(char *kargs) { const char *sw; char *opts; char *console; int howto, i; howto = 0; /* Get the boot options from the environment first. */ for (i = 0; howto_names[i].ev != NULL; i++) { if (getenv(howto_names[i].ev) != NULL) howto |= howto_names[i].mask; } console = getenv("console"); if (console != NULL) { if (strcmp(console, "comconsole") == 0) howto |= RB_SERIAL; if (strcmp(console, "nullconsole") == 0) howto |= RB_MUTE; } /* Parse kargs */ if (kargs == NULL) return (howto); opts = strchr(kargs, '-'); while (opts != NULL) { while (*(++opts) != '\0') { sw = strchr(howto_switches, *opts); if (sw == NULL) break; howto |= howto_masks[sw - howto_switches]; } opts = strchr(opts, '-'); } return (howto); } /* * Copy the environment into the load area starting at (addr). * Each variable is formatted as =, with a single nul * separating each variable, and a double nul terminating the environment. */ static vm_offset_t bi_copyenv(vm_offset_t start) { struct env_var *ep; vm_offset_t addr, last; size_t len; addr = last = start; /* Traverse the environment. */ for (ep = environ; ep != NULL; ep = ep->ev_next) { len = strlen(ep->ev_name); if ((size_t)archsw.arch_copyin(ep->ev_name, addr, len) != len) break; addr += len; if (archsw.arch_copyin("=", addr, 1) != 1) break; addr++; if (ep->ev_value != NULL) { len = strlen(ep->ev_value); if ((size_t)archsw.arch_copyin(ep->ev_value, addr, len) != len) break; addr += len; } if (archsw.arch_copyin("", addr, 1) != 1) break; last = ++addr; } if (archsw.arch_copyin("", last++, 1) != 1) last = start; return(last); } /* * Copy module-related data into the load area, where it can be * used as a directory for loaded modules. * * Module data is presented in a self-describing format. Each datum * is preceded by a 32-bit identifier and a 32-bit size field. * * Currently, the following data are saved: * * MOD_NAME (variable) module name (string) * MOD_TYPE (variable) module type (string) * MOD_ARGS (variable) module parameters (string) * MOD_ADDR sizeof(vm_offset_t) module load address * MOD_SIZE sizeof(size_t) module size * MOD_METADATA (variable) type-specific metadata */ #define COPY32(v, a, c) { \ uint32_t x = (v); \ if (c) \ archsw.arch_copyin(&x, a, sizeof(x)); \ a += sizeof(x); \ } #define MOD_STR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(strlen(s) + 1, a, c); \ if (c) \ archsw.arch_copyin(s, a, strlen(s) + 1); \ a += roundup(strlen(s) + 1, sizeof(u_long)); \ } #define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) #define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) #define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) #define MOD_VAR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(sizeof(s), a, c); \ if (c) \ archsw.arch_copyin(&s, a, sizeof(s)); \ a += roundup(sizeof(s), sizeof(u_long)); \ } #define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) #define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) #define MOD_METADATA(a, mm, c) { \ COPY32(MODINFO_METADATA | mm->md_type, a, c); \ COPY32(mm->md_size, a, c); \ if (c) \ archsw.arch_copyin(mm->md_data, a, mm->md_size); \ a += roundup(mm->md_size, sizeof(u_long)); \ } #define MOD_END(a, c) { \ COPY32(MODINFO_END, a, c); \ COPY32(0, a, c); \ } static vm_offset_t bi_copymodules(vm_offset_t addr) { struct preloaded_file *fp; struct file_metadata *md; int c; uint64_t v; c = addr != 0; /* Start with the first module on the list, should be the kernel. */ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { MOD_NAME(addr, fp->f_name, c); /* This must come first. */ MOD_TYPE(addr, fp->f_type, c); if (fp->f_args) MOD_ARGS(addr, fp->f_args, c); v = fp->f_addr; #if defined(__arm__) v -= __elfN(relocation_offset); #endif MOD_ADDR(addr, v, c); v = fp->f_size; MOD_SIZE(addr, v, c); for (md = fp->f_metadata; md != NULL; md = md->md_next) if (!(md->md_type & MODINFOMD_NOCOPY)) MOD_METADATA(addr, md, c); } MOD_END(addr, c); return(addr); } static int bi_load_efi_data(struct preloaded_file *kfp) { EFI_MEMORY_DESCRIPTOR *mm; EFI_PHYSICAL_ADDRESS addr; EFI_STATUS status; size_t efisz; UINTN efi_mapkey; UINTN mmsz, pages, retry, sz; UINT32 mmver; struct efi_map_header *efihdr; -#if defined(__amd64__) +#if defined(__amd64__) || defined(__aarch64__) struct efi_fb efifb; if (efi_find_framebuffer(&efifb) == 0) { printf("EFI framebuffer information:\n"); - printf("addr, size 0x%lx, 0x%lx\n", efifb.fb_addr, + printf("addr, size 0x%jx, 0x%jx\n", efifb.fb_addr, efifb.fb_size); printf("dimensions %d x %d\n", efifb.fb_width, efifb.fb_height); printf("stride %d\n", efifb.fb_stride); printf("masks 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", efifb.fb_mask_red, efifb.fb_mask_green, efifb.fb_mask_blue, efifb.fb_mask_reserved); file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb); } #endif efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; /* * Assgin size of EFI_MEMORY_DESCRIPTOR to keep compatible with * u-boot which doesn't fill this value when buffer for memory * descriptors is too small (eg. 0 to obtain memory map size) */ mmsz = sizeof(EFI_MEMORY_DESCRIPTOR); /* * It is possible that the first call to ExitBootServices may change * the map key. Fetch a new map key and retry ExitBootServices in that * case. */ for (retry = 2; retry > 0; retry--) { /* * Allocate enough pages to hold the bootinfo block and the * memory map EFI will return to us. The memory map has an * unknown size, so we have to determine that first. Note that * the AllocatePages call can itself modify the memory map, so * we have to take that into account as well. The changes to * the memory map are caused by splitting a range of free * memory into two (AFAICT), so that one is marked as being * loader data. */ sz = 0; BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver); sz += mmsz; sz = (sz + 0xf) & ~0xf; pages = EFI_SIZE_TO_PAGES(sz + efisz); status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages, &addr); if (EFI_ERROR(status)) { printf("%s: AllocatePages error %lu\n", __func__, EFI_ERROR_CODE(status)); return (ENOMEM); } /* * Read the memory map and stash it after bootinfo. Align the * memory map on a 16-byte boundary (the bootinfo block is page * aligned). */ efihdr = (struct efi_map_header *)addr; mm = (void *)((uint8_t *)efihdr + efisz); sz = (EFI_PAGE_SIZE * pages) - efisz; status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver); if (EFI_ERROR(status)) { printf("%s: GetMemoryMap error %lu\n", __func__, EFI_ERROR_CODE(status)); return (EINVAL); } status = BS->ExitBootServices(IH, efi_mapkey); if (EFI_ERROR(status) == 0) { efihdr->memory_size = sz; efihdr->descriptor_size = mmsz; efihdr->descriptor_version = mmver; file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, efihdr); return (0); } BS->FreePages(addr, pages); } printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status)); return (EINVAL); } /* * Load the information expected by an amd64 kernel. * * - The 'boothowto' argument is constructed. * - The 'bootdev' argument is constructed. * - The 'bootinfo' struct is constructed, and copied into the kernel space. * - The kernel environment is copied into kernel space. * - Module metadata are formatted and placed in kernel space. */ int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp) { struct preloaded_file *xp, *kfp; struct devdesc *rootdev; struct file_metadata *md; vm_offset_t addr; uint64_t kernend; uint64_t envp; vm_offset_t size; char *rootdevname; int howto; #if defined(LOADER_FDT_SUPPORT) vm_offset_t dtbp; int dtb_size; #endif #if defined(__arm__) vm_offset_t vaddr; size_t i; /* * These metadata addreses must be converted for kernel after * relocation. */ uint32_t mdt[] = { MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND, MODINFOMD_ENVP, #if defined(LOADER_FDT_SUPPORT) MODINFOMD_DTBP #endif }; #endif howto = bi_getboothowto(args); /* * Allow the environment variable 'rootdev' to override the supplied * device. This should perhaps go to MI code and/or have $rootdev * tested/set by MI code before launching the kernel. */ rootdevname = getenv("rootdev"); archsw.arch_getdev((void**)(&rootdev), rootdevname, NULL); if (rootdev == NULL) { printf("Can't determine root device.\n"); return(EINVAL); } /* Try reading the /etc/fstab file to select the root device */ getrootmount(efi_fmtdev((void *)rootdev)); addr = 0; for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { if (addr < (xp->f_addr + xp->f_size)) addr = xp->f_addr + xp->f_size; } /* Pad to a page boundary. */ addr = roundup(addr, PAGE_SIZE); /* Copy our environment. */ envp = addr; addr = bi_copyenv(addr); /* Pad to a page boundary. */ addr = roundup(addr, PAGE_SIZE); #if defined(LOADER_FDT_SUPPORT) /* Handle device tree blob */ dtbp = addr; dtb_size = fdt_copy(addr); /* Pad to a page boundary */ if (dtb_size) addr += roundup(dtb_size, PAGE_SIZE); #endif kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) kfp = file_findfile(NULL, "elf64 kernel"); if (kfp == NULL) panic("can't find kernel file"); kernend = 0; /* fill it in later */ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); #if defined(LOADER_FDT_SUPPORT) if (dtb_size) file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp); else printf("WARNING! Trying to fire up the kernel, but no " "device tree blob found!\n"); #endif file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST); bi_load_efi_data(kfp); /* Figure out the size and location of the metadata. */ *modulep = addr; size = bi_copymodules(0); kernend = roundup(addr + size, PAGE_SIZE); *kernendp = kernend; /* patch MODINFOMD_KERNEND */ md = file_findmetadata(kfp, MODINFOMD_KERNEND); bcopy(&kernend, md->md_data, sizeof kernend); #if defined(__arm__) *modulep -= __elfN(relocation_offset); /* Do relocation fixup on metadata of each module. */ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { for (i = 0; i < nitems(mdt); i++) { md = file_findmetadata(xp, mdt[i]); if (md) { bcopy(md->md_data, &vaddr, sizeof vaddr); vaddr -= __elfN(relocation_offset); bcopy(&vaddr, md->md_data, sizeof vaddr); } } } #endif /* Copy module list and metadata. */ (void)bi_copymodules(addr); return (0); } Index: head/sys/boot/efi/loader/framebuffer.c =================================================================== --- head/sys/boot/efi/loader/framebuffer.c (nonexistent) +++ head/sys/boot/efi/loader/framebuffer.c (revision 324995) @@ -0,0 +1,568 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Benno Rice under sponsorship from + * the FreeBSD Foundation. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "framebuffer.h" + +static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; +static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID; +static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID; + +static u_int +efifb_color_depth(struct efi_fb *efifb) +{ + uint32_t mask; + u_int depth; + + mask = efifb->fb_mask_red | efifb->fb_mask_green | + efifb->fb_mask_blue | efifb->fb_mask_reserved; + if (mask == 0) + return (0); + for (depth = 1; mask != 1; depth++) + mask >>= 1; + return (depth); +} + +static int +efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt, + EFI_PIXEL_BITMASK *pixinfo) +{ + int result; + + result = 0; + switch (pixfmt) { + case PixelRedGreenBlueReserved8BitPerColor: + efifb->fb_mask_red = 0x000000ff; + efifb->fb_mask_green = 0x0000ff00; + efifb->fb_mask_blue = 0x00ff0000; + efifb->fb_mask_reserved = 0xff000000; + break; + case PixelBlueGreenRedReserved8BitPerColor: + efifb->fb_mask_red = 0x00ff0000; + efifb->fb_mask_green = 0x0000ff00; + efifb->fb_mask_blue = 0x000000ff; + efifb->fb_mask_reserved = 0xff000000; + break; + case PixelBitMask: + efifb->fb_mask_red = pixinfo->RedMask; + efifb->fb_mask_green = pixinfo->GreenMask; + efifb->fb_mask_blue = pixinfo->BlueMask; + efifb->fb_mask_reserved = pixinfo->ReservedMask; + break; + default: + result = 1; + break; + } + return (result); +} + +static int +efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode, + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info) +{ + int result; + + efifb->fb_addr = mode->FrameBufferBase; + efifb->fb_size = mode->FrameBufferSize; + efifb->fb_height = info->VerticalResolution; + efifb->fb_width = info->HorizontalResolution; + efifb->fb_stride = info->PixelsPerScanLine; + result = efifb_mask_from_pixfmt(efifb, info->PixelFormat, + &info->PixelInformation); + return (result); +} + +static ssize_t +efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line, + EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size) +{ + EFI_UGA_PIXEL pix0, pix1; + uint8_t *data1, *data2; + size_t count, maxcount = 1024; + ssize_t ofs; + EFI_STATUS status; + u_int idx; + + status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer, + 0, line, 0, 0, 1, 1, 0); + if (EFI_ERROR(status)) { + printf("UGA BLT operation failed (video->buffer)"); + return (-1); + } + pix1.Red = ~pix0.Red; + pix1.Green = ~pix0.Green; + pix1.Blue = ~pix0.Blue; + pix1.Reserved = 0; + + data1 = calloc(maxcount, 2); + if (data1 == NULL) { + printf("Unable to allocate memory"); + return (-1); + } + data2 = data1 + maxcount; + + ofs = 0; + while (size > 0) { + count = min(size, maxcount); + + status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, + EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, + data1); + if (EFI_ERROR(status)) { + printf("Error reading frame buffer (before)"); + goto fail; + } + status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo, + 0, 0, 0, line, 1, 1, 0); + if (EFI_ERROR(status)) { + printf("UGA BLT operation failed (modify)"); + goto fail; + } + status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, + EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, + data2); + if (EFI_ERROR(status)) { + printf("Error reading frame buffer (after)"); + goto fail; + } + status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo, + 0, 0, 0, line, 1, 1, 0); + if (EFI_ERROR(status)) { + printf("UGA BLT operation failed (restore)"); + goto fail; + } + for (idx = 0; idx < count; idx++) { + if (data1[idx] != data2[idx]) { + free(data1); + return (ofs + (idx & ~3)); + } + } + ofs += count; + size -= count; + } + printf("No change detected in frame buffer"); + + fail: + printf(" -- error %lu\n", EFI_ERROR_CODE(status)); + free(data1); + return (-1); +} + +static EFI_PCI_IO_PROTOCOL * +efifb_uga_get_pciio(void) +{ + EFI_PCI_IO_PROTOCOL *pciio; + EFI_HANDLE *buf, *hp; + EFI_STATUS status; + UINTN bufsz; + + /* Get all handles that support the UGA protocol. */ + bufsz = 0; + status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL); + if (status != EFI_BUFFER_TOO_SMALL) + return (NULL); + buf = malloc(bufsz); + status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf); + if (status != EFI_SUCCESS) { + free(buf); + return (NULL); + } + bufsz /= sizeof(EFI_HANDLE); + + /* Get the PCI I/O interface of the first handle that supports it. */ + pciio = NULL; + for (hp = buf; hp < buf + bufsz; hp++) { + status = BS->HandleProtocol(*hp, &pciio_guid, (void **)&pciio); + if (status == EFI_SUCCESS) { + free(buf); + return (pciio); + } + } + free(buf); + return (NULL); +} + +static EFI_STATUS +efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp, + uint64_t *sizep) +{ + uint8_t *resattr; + uint64_t addr, size; + EFI_STATUS status; + u_int bar; + + if (pciio == NULL) + return (EFI_DEVICE_ERROR); + + /* Attempt to get the frame buffer address (imprecise). */ + *addrp = 0; + *sizep = 0; + for (bar = 0; bar < 6; bar++) { + status = pciio->GetBarAttributes(pciio, bar, NULL, + (void **)&resattr); + if (status != EFI_SUCCESS) + continue; + /* XXX magic offsets and constants. */ + if (resattr[0] == 0x87 && resattr[3] == 0) { + /* 32-bit address space descriptor (MEMIO) */ + addr = le32dec(resattr + 10); + size = le32dec(resattr + 22); + } else if (resattr[0] == 0x8a && resattr[3] == 0) { + /* 64-bit address space descriptor (MEMIO) */ + addr = le64dec(resattr + 14); + size = le64dec(resattr + 38); + } else { + addr = 0; + size = 0; + } + BS->FreePool(resattr); + if (addr == 0 || size == 0) + continue; + + /* We assume the largest BAR is the frame buffer. */ + if (size > *sizep) { + *addrp = addr; + *sizep = size; + } + } + return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0); +} + +static int +efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga) +{ + EFI_PCI_IO_PROTOCOL *pciio; + char *ev, *p; + EFI_STATUS status; + ssize_t offset; + uint64_t fbaddr; + uint32_t horiz, vert, stride; + uint32_t np, depth, refresh; + + status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh); + if (EFI_ERROR(status)) + return (1); + efifb->fb_height = vert; + efifb->fb_width = horiz; + /* Paranoia... */ + if (efifb->fb_height == 0 || efifb->fb_width == 0) + return (1); + + /* The color masks are fixed AFAICT. */ + efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor, + NULL); + + /* pciio can be NULL on return! */ + pciio = efifb_uga_get_pciio(); + + /* Try to find the frame buffer. */ + status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr, + &efifb->fb_size); + if (EFI_ERROR(status)) { + efifb->fb_addr = 0; + efifb->fb_size = 0; + } + + /* + * There's no reliable way to detect the frame buffer or the + * offset within the frame buffer of the visible region, nor + * the stride. Our only option is to look at the system and + * fill in the blanks based on that. Luckily, UGA was mostly + * only used on Apple hardware. + */ + offset = -1; + ev = getenv("smbios.system.maker"); + if (ev != NULL && !strcmp(ev, "Apple Inc.")) { + ev = getenv("smbios.system.product"); + if (ev != NULL && !strcmp(ev, "iMac7,1")) { + /* These are the expected values we should have. */ + horiz = 1680; + vert = 1050; + fbaddr = 0xc0000000; + /* These are the missing bits. */ + offset = 0x10000; + stride = 1728; + } else if (ev != NULL && !strcmp(ev, "MacBook3,1")) { + /* These are the expected values we should have. */ + horiz = 1280; + vert = 800; + fbaddr = 0xc0000000; + /* These are the missing bits. */ + offset = 0x0; + stride = 2048; + } + } + + /* + * If this is hardware we know, make sure that it looks familiar + * before we accept our hardcoded values. + */ + if (offset >= 0 && efifb->fb_width == horiz && + efifb->fb_height == vert && efifb->fb_addr == fbaddr) { + efifb->fb_addr += offset; + efifb->fb_size -= offset; + efifb->fb_stride = stride; + return (0); + } else if (offset >= 0) { + printf("Hardware make/model known, but graphics not " + "as expected.\n"); + printf("Console may not work!\n"); + } + + /* + * The stride is equal or larger to the width. Often it's the + * next larger power of two. We'll start with that... + */ + efifb->fb_stride = efifb->fb_width; + do { + np = efifb->fb_stride & (efifb->fb_stride - 1); + if (np) { + efifb->fb_stride |= (np - 1); + efifb->fb_stride++; + } + } while (np); + + ev = getenv("hw.efifb.address"); + if (ev == NULL) { + if (efifb->fb_addr == 0) { + printf("Please set hw.efifb.address and " + "hw.efifb.stride.\n"); + return (1); + } + + /* + * The visible part of the frame buffer may not start at + * offset 0, so try to detect it. Note that we may not + * always be able to read from the frame buffer, which + * means that we may not be able to detect anything. In + * that case, we would take a long time scanning for a + * pixel change in the frame buffer, which would have it + * appear that we're hanging, so we limit the scan to + * 1/256th of the frame buffer. This number is mostly + * based on PR 202730 and the fact that on a MacBoook, + * where we can't read from the frame buffer the offset + * of the visible region is 0. In short: we want to scan + * enough to handle all adapters that have an offset + * larger than 0 and we want to scan as little as we can + * to not appear to hang when we can't read from the + * frame buffer. + */ + offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr, + efifb->fb_size >> 8); + if (offset == -1) { + printf("Unable to reliably detect frame buffer.\n"); + } else if (offset > 0) { + efifb->fb_addr += offset; + efifb->fb_size -= offset; + } + } else { + offset = 0; + efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4; + efifb->fb_addr = strtoul(ev, &p, 0); + if (*p != '\0') + return (1); + } + + ev = getenv("hw.efifb.stride"); + if (ev == NULL) { + if (pciio != NULL && offset != -1) { + /* Determine the stride. */ + offset = efifb_uga_find_pixel(uga, 1, pciio, + efifb->fb_addr, horiz * 8); + if (offset != -1) + efifb->fb_stride = offset >> 2; + } else { + printf("Unable to reliably detect the stride.\n"); + } + } else { + efifb->fb_stride = strtoul(ev, &p, 0); + if (*p != '\0') + return (1); + } + + /* + * We finalized on the stride, so recalculate the size of the + * frame buffer. + */ + efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4; + return (0); +} + +int +efi_find_framebuffer(struct efi_fb *efifb) +{ + EFI_GRAPHICS_OUTPUT *gop; + EFI_UGA_DRAW_PROTOCOL *uga; + EFI_STATUS status; + + status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop); + if (status == EFI_SUCCESS) + return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info)); + + status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga); + if (status == EFI_SUCCESS) + return (efifb_from_uga(efifb, uga)); + + return (1); +} + +static void +print_efifb(int mode, struct efi_fb *efifb, int verbose) +{ + u_int depth; + + if (mode >= 0) + printf("mode %d: ", mode); + depth = efifb_color_depth(efifb); + printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height, + depth, efifb->fb_stride); + if (verbose) { + printf("\n frame buffer: address=%jx, size=%jx", + (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size); + printf("\n color mask: R=%08x, G=%08x, B=%08x\n", + efifb->fb_mask_red, efifb->fb_mask_green, + efifb->fb_mask_blue); + } +} + +COMMAND_SET(gop, "gop", "graphics output protocol", command_gop); + +static int +command_gop(int argc, char *argv[]) +{ + struct efi_fb efifb; + EFI_GRAPHICS_OUTPUT *gop; + EFI_STATUS status; + u_int mode; + + status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop); + if (EFI_ERROR(status)) { + snprintf(command_errbuf, sizeof(command_errbuf), + "%s: Graphics Output Protocol not present (error=%lu)", + argv[0], EFI_ERROR_CODE(status)); + return (CMD_ERROR); + } + + if (argc < 2) + goto usage; + + if (!strcmp(argv[1], "set")) { + char *cp; + + if (argc != 3) + goto usage; + mode = strtol(argv[2], &cp, 0); + if (cp[0] != '\0') { + sprintf(command_errbuf, "mode is an integer"); + return (CMD_ERROR); + } + status = gop->SetMode(gop, mode); + if (EFI_ERROR(status)) { + snprintf(command_errbuf, sizeof(command_errbuf), + "%s: Unable to set mode to %u (error=%lu)", + argv[0], mode, EFI_ERROR_CODE(status)); + return (CMD_ERROR); + } + } else if (!strcmp(argv[1], "get")) { + if (argc != 2) + goto usage; + efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info); + print_efifb(gop->Mode->Mode, &efifb, 1); + printf("\n"); + } else if (!strcmp(argv[1], "list")) { + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; + UINTN infosz; + + if (argc != 2) + goto usage; + pager_open(); + for (mode = 0; mode < gop->Mode->MaxMode; mode++) { + status = gop->QueryMode(gop, mode, &infosz, &info); + if (EFI_ERROR(status)) + continue; + efifb_from_gop(&efifb, gop->Mode, info); + print_efifb(mode, &efifb, 0); + if (pager_output("\n")) + break; + } + pager_close(); + } + return (CMD_OK); + + usage: + snprintf(command_errbuf, sizeof(command_errbuf), + "usage: %s [list | get | set ]", argv[0]); + return (CMD_ERROR); +} + +COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga); + +static int +command_uga(int argc, char *argv[]) +{ + struct efi_fb efifb; + EFI_UGA_DRAW_PROTOCOL *uga; + EFI_STATUS status; + + status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga); + if (EFI_ERROR(status)) { + snprintf(command_errbuf, sizeof(command_errbuf), + "%s: UGA Protocol not present (error=%lu)", + argv[0], EFI_ERROR_CODE(status)); + return (CMD_ERROR); + } + + if (argc != 1) + goto usage; + + if (efifb_from_uga(&efifb, uga) != CMD_OK) { + snprintf(command_errbuf, sizeof(command_errbuf), + "%s: Unable to get UGA information", argv[0]); + return (CMD_ERROR); + } + + print_efifb(-1, &efifb, 1); + printf("\n"); + return (CMD_OK); + + usage: + snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]); + return (CMD_ERROR); +} Property changes on: head/sys/boot/efi/loader/framebuffer.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/boot/efi/loader/framebuffer.h =================================================================== --- head/sys/boot/efi/loader/framebuffer.h (nonexistent) +++ head/sys/boot/efi/loader/framebuffer.h (revision 324995) @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Benno Rice under sponsorship from + * the FreeBSD Foundation. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _EFIFB_H_ +#define _EFIFB_H_ + +int efi_find_framebuffer(struct efi_fb *efifb); + +#endif /* _EFIFB_H_ */ Property changes on: head/sys/boot/efi/loader/framebuffer.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property