[haiku-commits] BRANCH jessicah-github.uefi-devices [6a569b28b184] in src/system/boot: platform/efi loader

  • From: jessicah-github.uefi-devices <community@xxxxxxxxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Sat, 7 Jan 2017 04:15:32 +0100 (CET)

added 3 changesets to branch 'refs/remotes/jessicah-github/uefi-devices'
old head: 0000000000000000000000000000000000000000
new head: 6a569b28b184438a41da4d3407c2e8472a1a8e74
overview: https://github.com/jessicah/haiku/compare/6a569b28b184

----------------------------------------------------------------------------

445b080ea3e7: UEFI: support selecting video resolution & vesa settings file.

be13524235f3: loader: get_boot_file_system() iterate over all devices found.
  
  * This is useful for UEFI, as we can then add the device that
    contains the UEFI loader, as well as all CD devices. As a
    result, if the device with the UEFI loader doesn't contain
    a bootable BFS partition, it will then attempt the same for
    CD devices.

6a569b28b184: UEFI: improve boot support in devices.cpp

                         [ Jessica Hamilton <jessica.l.hamilton@xxxxxxxxx> ]

----------------------------------------------------------------------------

6 files changed, 623 insertions(+), 85 deletions(-)
src/system/boot/Jamfile                  |   1 +
src/system/boot/loader/vfs.cpp           |  55 ++--
src/system/boot/platform/efi/devices.cpp | 410 +++++++++++++++++++++++----
src/system/boot/platform/efi/menu.cpp    |  17 +-
src/system/boot/platform/efi/video.cpp   | 208 +++++++++++++-
src/system/boot/platform/efi/video.h     |  17 ++

############################################################################

Commit:      445b080ea3e749d732ffb2d00cdfd16d36a8a1d2
Author:      Jessica Hamilton <jessica.l.hamilton@xxxxxxxxx>
Date:        Sun Dec 25 03:13:57 2016 UTC

UEFI: support selecting video resolution & vesa settings file.

----------------------------------------------------------------------------

diff --git a/src/system/boot/Jamfile b/src/system/boot/Jamfile
index d005d35..30fc891 100644
--- a/src/system/boot/Jamfile
+++ b/src/system/boot/Jamfile
@@ -48,6 +48,7 @@ BootMergeObject boot_libroot.o :
        strchr.c
        strrchr.c
        strtol.c
+       strtoul.c
        $(extraSources)
 ;
 
diff --git a/src/system/boot/platform/efi/menu.cpp 
b/src/system/boot/platform/efi/menu.cpp
index 0b13b2d..b4f6e6a 100644
--- a/src/system/boot/platform/efi/menu.cpp
+++ b/src/system/boot/platform/efi/menu.cpp
@@ -8,12 +8,27 @@
 #include <boot/platform/generic/text_menu.h>
 
 #include "efi_platform.h"
+#include "video.h"
 
 
 void
 platform_add_menus(Menu *menu)
 {
-       // No platform specific menus
+       MenuItem *item;
+
+       switch (menu->Type()) {
+               case MAIN_MENU:
+                       item = new(std::nothrow)MenuItem("Select video mode", 
video_mode_menu());
+                       if (item != NULL) {
+                               menu->AddItem(item);
+                               item->SetTarget(video_mode_hook);
+                               item->SetShortcut('v');
+                       }
+
+                       break;
+               default:
+                       break;
+       }
 }
 
 
diff --git a/src/system/boot/platform/efi/video.cpp 
b/src/system/boot/platform/efi/video.cpp
index e292b4d..aa96c96 100644
--- a/src/system/boot/platform/efi/video.cpp
+++ b/src/system/boot/platform/efi/video.cpp
@@ -4,23 +4,154 @@
  */
 
 
+#include "video.h"
+
+#include <stdlib.h>
+
 #include <boot/kernel_args.h>
+#include <boot/menu.h>
 #include <boot/platform.h>
 #include <boot/platform/generic/video.h>
 #include <boot/stage2.h>
 #include <boot/stdio.h>
+#include <drivers/driver_settings.h>
+#include <util/list.h>
 
 #include "efi_platform.h"
 
 
+//#define TRACE_VIDEO
+#ifdef TRACE_VIDEO
+#      define TRACE(x) dprintf x
+#else
+#      define TRACE(x) ;
+#endif
+
+
+struct video_mode {
+       list_link       link;
+       UINTN           mode;
+       UINTN           width, height, bits_per_pixel, bytes_per_row;
+};
+
+
 static EFI_GUID sGraphicsOutputGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
 static EFI_GRAPHICS_OUTPUT_PROTOCOL *sGraphicsOutput;
 static UINTN sGraphicsMode;
+static struct list sModeList;
+static uint32 sModeCount;
+static bool sModeChosen;
+static bool sSettingsLoaded;
+
+
+static int
+compare_video_modes(video_mode *a, video_mode *b)
+{
+       int compare = a->width - b->width;
+       if (compare != 0)
+               return compare;
+
+       compare = a->height - b->height;
+       if (compare != 0)
+               return compare;
+
+       return a->bits_per_pixel - b->bits_per_pixel;
+}
+
+
+static void
+add_video_mode(video_mode *videoMode)
+{
+       video_mode *mode = NULL;
+       while ((mode = (video_mode*)list_get_next_item(&sModeList, mode))
+                       != NULL) {
+               int compare = compare_video_modes(videoMode, mode);
+               if (compare == 0) {
+                       // mode already exists
+                       return;
+               }
+
+               if (compare > 0)
+                       break;
+       }
+
+       list_insert_item_before(&sModeList, mode, videoMode);
+       sModeCount++;
+}
+
+
+static video_mode*
+closest_video_mode(uint32 width, uint32 height, uint32 depth)
+{
+       video_mode *bestMode = NULL;
+       uint32 bestDiff = 0;
+
+       video_mode *mode = NULL;
+       while ((mode = (video_mode*)list_get_next_item(&sModeList, mode)) != 
NULL) {
+               if (mode->width > width) {
+                       // Only choose modes with a width less or equal than 
the searched
+                       // one; or else it might well be that the monitor 
cannot keep up.
+                       continue;
+               }
+
+               uint32 diff = 2 * abs(mode->width - width) + abs(mode->height - 
height)
+                       + abs(mode->bits_per_pixel - depth);
+
+               if (bestMode == NULL || bestDiff > diff) {
+                       bestMode = mode;
+                       bestDiff = diff;
+               }
+       }
+
+       return bestMode;
+}
+
+
+static void
+get_mode_from_settings(void)
+{
+       if (sSettingsLoaded)
+               return;
+
+       void *handle = load_driver_settings("vesa");
+       if (handle == NULL)
+               return;
+
+       bool found = false;
+
+       const driver_settings *settings = get_driver_settings(handle);
+       if (settings == NULL)
+               goto out;
+
+       sSettingsLoaded = true;
+
+       for (int32 i = 0; i < settings->parameter_count; i++) {
+               driver_parameter &parameter = settings->parameters[i];
+
+               if (strcmp(parameter.name, "mode") == 0 && 
parameter.value_count > 2) {
+                       uint32 width = strtoul(parameter.values[0], NULL, 0);
+                       uint32 height = strtoul(parameter.values[1], NULL, 0);
+                       uint32 depth = strtoul(parameter.values[2], NULL, 0);
+
+                       // search mode that fits
+                       video_mode *mode = closest_video_mode(width, height, 
depth);
+                       if (mode != NULL) {
+                               found = true;
+                               sGraphicsMode = mode->mode;
+                       }
+               }
+       }
+
+out:
+       unload_driver_settings(handle);
+}
 
 
 extern "C" status_t
 platform_init_video(void)
 {
+       list_init(&sModeList);
+
        // we don't support VESA modes or EDID
        gKernelArgs.vesa_modes = NULL;
        gKernelArgs.vesa_modes_size = 0;
@@ -39,17 +170,17 @@ platform_init_video(void)
        UINTN bestArea = 0;
        UINTN bestDepth = 0;
 
-       dprintf("looking for best graphics mode...\n");
+       TRACE(("looking for best graphics mode...\n"));
 
        for (UINTN mode = 0; mode < sGraphicsOutput->Mode->MaxMode; ++mode) {
                EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
                UINTN size, depth;
                sGraphicsOutput->QueryMode(sGraphicsOutput, mode, &size, &info);
                UINTN area = info->HorizontalResolution * 
info->VerticalResolution;
-               dprintf("  mode: %lu\n", mode);
-               dprintf("  width: %u\n", info->HorizontalResolution);
-               dprintf("  height: %u\n", info->VerticalResolution);
-               dprintf("  area: %lu\n", area);
+               TRACE(("  mode: %lu\n", mode));
+               TRACE(("  width: %u\n", info->HorizontalResolution));
+               TRACE(("  height: %u\n", info->VerticalResolution));
+               TRACE(("  area: %lu\n", area));
                if (info->PixelFormat == PixelRedGreenBlueReserved8BitPerColor) 
{
                        depth = 32;
                } else if (info->PixelFormat == 
PixelBlueGreenRedReserved8BitPerColor) {
@@ -62,16 +193,26 @@ platform_init_video(void)
                        && info->PixelInformation.ReservedMask == 0) {
                        depth = 24;
                } else {
-                       dprintf("  pixel format: %x unsupported\n",
-                               info->PixelFormat);
+                       TRACE(("  pixel format: %x unsupported\n",
+                               info->PixelFormat));
                        continue;
                }
-               dprintf("  depth: %lu\n", depth);
+               TRACE(("  depth: %lu\n", depth));
+
+               video_mode *videoMode = (video_mode*)malloc(sizeof(struct 
video_mode));
+               if (videoMode != NULL) {
+                       videoMode->mode = mode;
+                       videoMode->width = info->HorizontalResolution;
+                       videoMode->height = info->VerticalResolution;
+                       videoMode->bits_per_pixel = info->PixelFormat == 
PixelBitMask ? 24 : 32;
+                       videoMode->bytes_per_row = info->PixelsPerScanLine * 
depth / 8;
+                       add_video_mode(videoMode);
+               }
 
                area *= depth;
-               dprintf("  area (w/depth): %lu\n", area);
+               TRACE(("  area (w/depth): %lu\n", area));
                if (area >= bestArea) {
-                       dprintf("selected new best mode: %lu\n", mode);
+                       TRACE(("selected new best mode: %lu\n", mode));
                        bestArea = area;
                        bestDepth = depth;
                        sGraphicsMode = mode;
@@ -85,6 +226,8 @@ platform_init_video(void)
        }
 
        gKernelArgs.frame_buffer.enabled = true;
+       sModeChosen = false;
+       sSettingsLoaded = false;
        return B_OK;
 }
 
@@ -95,6 +238,9 @@ platform_switch_to_logo(void)
        if (sGraphicsOutput == NULL || !gKernelArgs.frame_buffer.enabled)
                return;
 
+       if (!sModeChosen)
+               get_mode_from_settings();
+
        sGraphicsOutput->SetMode(sGraphicsOutput, sGraphicsMode);
        gKernelArgs.frame_buffer.physical_buffer.start =
                sGraphicsOutput->Mode->FrameBufferBase;
@@ -114,6 +260,48 @@ platform_switch_to_logo(void)
 }
 
 
+bool
+video_mode_hook(Menu *menu, MenuItem *item)
+{
+       menu = item->Submenu();
+       item = menu->FindMarked();
+       if (item != NULL) {
+               sGraphicsMode = (UINTN)item->Data();
+               sModeChosen = true;
+       }
+
+       return true;
+}
+
+
+Menu*
+video_mode_menu()
+{
+       Menu *menu = new(std::nothrow)Menu(CHOICE_MENU, "Select Video Mode");
+       MenuItem *item;
+
+       video_mode *mode = NULL;
+       while ((mode = (video_mode*)list_get_next_item(&sModeList, mode)) != 
NULL) {
+               char label[64];
+               snprintf(label, sizeof(label), "%lux%lu %lu bit", mode->width,
+                       mode->height, mode->bits_per_pixel);
+
+               menu->AddItem(item = new (std::nothrow)MenuItem(label));
+               item->SetData((const void*)mode->mode);
+               if (mode->mode == sGraphicsMode) {
+                       item->SetMarked(true);
+                       item->Select(true);
+               }
+       }
+
+       menu->AddSeparatorItem();
+       menu->AddItem(item = new(std::nothrow)MenuItem("Return to main menu"));
+       item->SetType(MENU_ITEM_NO_CHOICE);
+
+       return menu;
+}
+
+
 extern "C" void
 platform_blit4(addr_t frameBuffer, const uint8 *data,
        uint16 width, uint16 height, uint16 imageWidth,
diff --git a/src/system/boot/platform/efi/video.h 
b/src/system/boot/platform/efi/video.h
new file mode 100644
index 0000000..bc7ce85
--- /dev/null
+++ b/src/system/boot/platform/efi/video.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2004, Axel Dörfler, axeld@xxxxxxxxxxxxxxxx. All rights reserved.
+ * Copyright 2011, Rene Gollent, rene@xxxxxxxxxxx. All rights reserved.
+ *
+ * Distributed under the terms of the Haiku License.
+ */
+#ifndef VIDEO_H
+#define VIDEO_H
+
+
+class Menu;
+class MenuItem;
+
+bool video_mode_hook(Menu *menu, MenuItem *item);
+Menu *video_mode_menu();
+
+#endif /* VIDEO_H */

############################################################################

Commit:      be13524235f351054ff8cb238e76670c43692054
Author:      Jessica Hamilton <jessica.l.hamilton@xxxxxxxxx>
Date:        Sat Jan  7 02:11:54 2017 UTC

loader: get_boot_file_system() iterate over all devices found.

* This is useful for UEFI, as we can then add the device that
  contains the UEFI loader, as well as all CD devices. As a
  result, if the device with the UEFI loader doesn't contain
  a bootable BFS partition, it will then attempt the same for
  CD devices.

----------------------------------------------------------------------------

diff --git a/src/system/boot/loader/vfs.cpp b/src/system/boot/loader/vfs.cpp
index 73344cc..3dc9b0d 100644
--- a/src/system/boot/loader/vfs.cpp
+++ b/src/system/boot/loader/vfs.cpp
@@ -527,7 +527,7 @@ BootVolume::_SetTo(Directory* rootDirectory,
        fSystemDirectory = static_cast<Directory*>(systemNode);
 
        if (packageVolumeInfo == NULL) {
-               // get a package volume info 
+               // get a package volume info
                BReference<PackageVolumeInfo> packageVolumeInfoReference(
                        new(std::nothrow) PackageVolumeInfo);
                status_t error = 
packageVolumeInfoReference->SetTo(fSystemDirectory,
@@ -646,40 +646,43 @@ register_boot_file_system(BootVolume& bootVolume)
 status_t
 get_boot_file_system(stage2_args* args, BootVolume& _bootVolume)
 {
-       Node *device;
        status_t error = platform_add_boot_device(args, &gBootDevices);
        if (error != B_OK)
                return error;
 
-       // the boot device must be the first device in the list
-       device = gBootDevices.First();
+       NodeIterator iterator = gBootDevices.GetIterator();
+       while (iterator.HasNext()) {
+               Node *device = iterator.Next();
 
-       error = add_partitions_for(device, false, true);
-       if (error != B_OK)
-               return error;
+               error = add_partitions_for(device, false, true);
+               if (error != B_OK)
+                       continue;
 
-       Partition *partition;
-       error = platform_get_boot_partition(args, device, &gPartitions, 
&partition);
-       if (error != B_OK)
-               return error;
+               Partition *partition;
+               error = platform_get_boot_partition(args, device, &gPartitions, 
&partition);
+               if (error != B_OK)
+                       continue;
 
-       Directory *fileSystem;
-       error = partition->Mount(&fileSystem, true);
-       if (error != B_OK) {
-               // this partition doesn't contain any known file system; we
-               // don't need it anymore
-               gPartitions.Remove(partition);
-               delete partition;
-               return error;
-       }
+               Directory *fileSystem;
+               error = partition->Mount(&fileSystem, true);
+               if (error != B_OK) {
+                       // this partition doesn't contain any known file 
system; we
+                       // don't need it anymore
+                       gPartitions.Remove(partition);
+                       delete partition;
+                       continue;
+               }
 
-       // init the BootVolume
-       error = _bootVolume.SetTo(fileSystem);
-       if (error != B_OK)
-               return error;
+               // init the BootVolume
+               error = _bootVolume.SetTo(fileSystem);
+               if (error != B_OK)
+                       continue;
 
-       sBootDevice = device;
-       return B_OK;
+               sBootDevice = device;
+               return B_OK;
+       }
+
+       return B_ERROR;
 }
 
 

############################################################################

Commit:      6a569b28b184438a41da4d3407c2e8472a1a8e74
Author:      Jessica Hamilton <jessica.l.hamilton@xxxxxxxxx>
Date:        Sat Jan  7 02:15:20 2017 UTC

UEFI: improve boot support in devices.cpp

----------------------------------------------------------------------------

diff --git a/src/system/boot/platform/efi/devices.cpp 
b/src/system/boot/platform/efi/devices.cpp
index b0488e9..0bb873a 100644
--- a/src/system/boot/platform/efi/devices.cpp
+++ b/src/system/boot/platform/efi/devices.cpp
@@ -4,18 +4,107 @@
  */
 
 
+#include <string.h>
+
 #include <boot/partitions.h>
 #include <boot/platform.h>
 #include <boot/stage2.h>
+#include <boot/stdio.h>
+#include <util/list.h>
 
 #include "efi_platform.h"
 
 
+struct device_handle {
+       list_link                       link;
+       EFI_DEVICE_PATH*        device_path;
+       EFI_HANDLE                      handle;
+};
+
+
+static struct list sMessagingDevices;
+static struct list sMediaDevices;
+
 static EFI_GUID BlockIoGUID = BLOCK_IO_PROTOCOL;
 static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
 
 
+static void
+dump_device_path(EFI_DEVICE_PATH* path)
+{
+       while (!IsDevicePathEnd(path)) {
+               dprintf("%x:%x => ", path->Type, path->SubType);
+               path = NextDevicePathNode(path);
+       }
+       dprintf("end\n");
+}
+
+
+static UINTN
+device_path_length(EFI_DEVICE_PATH* path)
+{
+       EFI_DEVICE_PATH *node = path;
+       UINTN length = 0;
+       while (!IsDevicePathEnd(node)) {
+               length += DevicePathNodeLength(node);
+               node = NextDevicePathNode(node);
+       }
+
+       // node now points to the device path end node; add its length as well
+       return length + DevicePathNodeLength(node);
+}
+
+
+// If matchSubPath is true, then the second device path can be a sub-path
+// of the first device path
+static bool
+compare_device_paths(EFI_DEVICE_PATH* first, EFI_DEVICE_PATH* second, bool 
matchSubPath = false)
+{
+       EFI_DEVICE_PATH *firstNode = first;
+       EFI_DEVICE_PATH *secondNode = second;
+       while (!IsDevicePathEnd(firstNode) && !IsDevicePathEnd(secondNode)) {
+               UINTN firstLength = DevicePathNodeLength(firstNode);
+               UINTN secondLength = DevicePathNodeLength(secondNode);
+               if (firstLength != secondLength || memcmp(firstNode, 
secondNode, firstLength) != 0) {
+                       return false;
+               }
+               firstNode = NextDevicePathNode(firstNode);
+               secondNode = NextDevicePathNode(secondNode);
+       }
+
+       if (matchSubPath)
+               return IsDevicePathEnd(secondNode);
+
+       return IsDevicePathEnd(firstNode) && IsDevicePathEnd(secondNode);
+}
+
+
+static bool
+add_device_path(struct list *list, EFI_DEVICE_PATH* path, EFI_HANDLE handle)
+{
+       device_handle *node = NULL;
+       while ((node = (device_handle*)list_get_next_item(list, node)) != NULL) 
{
+               if (compare_device_paths(node->device_path, path)) {
+                       dprintf("    device path already exists\n");
+                       return false;
+               }
+       }
+
+       dprintf("    adding a new device path!\n");
+       dump_device_path(path);
+       UINTN length = device_path_length(path);
+       node = (device_handle*)malloc(sizeof(struct device_handle));
+       node->device_path = (EFI_DEVICE_PATH*)malloc(length);
+       node->handle = handle;
+       memcpy(node->device_path, path, length);
+
+       list_add_item(list, node);
+
+       return true;
+}
+
+
 class EfiDevice : public Node
 {
        public:
@@ -30,8 +119,20 @@ class EfiDevice : public Node
                        return (fBlockIo->Media->LastBlock + 1) * BlockSize(); }
 
                uint32 BlockSize() const { return fBlockIo->Media->BlockSize; }
-               //TODO: Check for ATAPI messaging in device path?
-               bool IsCD() { return fBlockIo->Media->ReadOnly;}
+               bool ReadOnly() const { return fBlockIo->Media->ReadOnly; }
+               int32 BootMethod() const {
+                       if (fDevicePath->Type == MEDIA_DEVICE_PATH) {
+                               if (fDevicePath->SubType == MEDIA_CDROM_DP)
+                                       return BOOT_METHOD_CD;
+                               if (fDevicePath->SubType == MEDIA_HARDDRIVE_DP)
+                                       return BOOT_METHOD_HARD_DISK;
+                       }
+
+                       return BOOT_METHOD_DEFAULT;
+               }
+
+               EFI_DEVICE_PATH* DevicePath() { return fDevicePath; }
+
        private:
                EFI_BLOCK_IO*           fBlockIo;
                EFI_DEVICE_PATH*        fDevicePath;
@@ -69,6 +170,74 @@ EfiDevice::ReadAt(void *cookie, off_t pos, void *buffer, 
size_t bufferSize)
        return bufferSize;
 }
 
+#if 0
+static EFI_DEVICE_PATH*
+find_device_path(EFI_DEVICE_PATH *devicePath, uint16 type, uint16 subType)
+{
+       EFI_DEVICE_PATH *node = devicePath;
+       while (!IsDevicePathEnd(node)) {
+               if (DevicePathType(node) == type
+                       && (subType == 0xFFFF || DevicePathSubType(node) == 
subType))
+                       return node;
+
+               node = NextDevicePathNode(node);
+       }
+
+       return NULL;
+}
+#endif
+
+static status_t
+build_device_handles()
+{
+       EFI_GUID blockIoGuid = BLOCK_IO_PROTOCOL;
+       EFI_GUID devicePathGuid = DEVICE_PATH_PROTOCOL;
+
+       //EFI_BLOCK_IO *blockIo;
+       EFI_DEVICE_PATH *devicePath, *node;
+       EFI_HANDLE *handles = NULL;
+       EFI_STATUS status;
+       UINTN size = 0;
+
+       status = kBootServices->LocateHandle(ByProtocol, &blockIoGuid, 0, 
&size, 0);
+       if (status != EFI_BUFFER_TOO_SMALL)
+               return B_ENTRY_NOT_FOUND;
+
+       handles = (EFI_HANDLE*)malloc(size);
+       status = kBootServices->LocateHandle(ByProtocol, &blockIoGuid, 0, &size,
+               handles);
+       if (status != EFI_SUCCESS) {
+               free(handles);
+               return B_ENTRY_NOT_FOUND;
+       }
+
+       for (UINTN n = 0; n < (size / sizeof(EFI_HANDLE)); n++) {
+               dprintf("  processing handle %lu\n", n);
+               status = kBootServices->HandleProtocol(handles[n], 
&devicePathGuid,
+                       (void**)&devicePath);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               node = devicePath;
+               while (!IsDevicePathEnd(NextDevicePathNode(node)))
+                       node = NextDevicePathNode(node);
+
+               if (DevicePathType(node) == MEDIA_DEVICE_PATH) {
+                       // Add to our media devices list
+                       dprintf("    adding a media device path instance\n");
+                       add_device_path(&sMediaDevices, devicePath, handles[n]);
+               } else if (DevicePathType(node) == MESSAGING_DEVICE_PATH) {
+                       // Add to our messaging devices list
+                       dprintf("    adding a messaging device path 
instance\n");
+                       add_device_path(&sMessagingDevices, devicePath, 
handles[n]);
+               }
+               dprintf("  finished pass...\n");
+       }
+       dprintf("finished processing handles\n");
+
+       return B_OK;
+}
+
 
 static off_t
 get_next_check_sum_offset(int32 index, off_t maxSize)
@@ -80,7 +249,6 @@ get_next_check_sum_offset(int32 index, off_t maxSize)
                return (maxSize >> 10) + index * 2048;
 
        return ((system_time() + index) % (maxSize >> 9)) * 512;
-       //return 42 * 512;
 }
 
 
@@ -105,91 +273,234 @@ compute_check_sum(Node *device, off_t offset)
 }
 
 
-status_t
-platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
+static status_t
+add_boot_device(NodeList *devicesList)
+{
+       return B_ENTRY_NOT_FOUND;
+}
+
+
+static device_handle*
+get_messaging_device_for_media_device(device_handle *media_device)
+{
+       device_handle *messaging_device = NULL;
+       while ((messaging_device = 
(device_handle*)list_get_next_item(&sMessagingDevices, messaging_device)) != 
NULL) {
+               if (compare_device_paths(media_device->device_path,
+                               messaging_device->device_path, true)) {
+                       dprintf("found messaging device for media device\n");
+                       return messaging_device;
+               }
+       }
+
+       return NULL;
+}
+
+
+static status_t
+add_boot_device_for_image(NodeList *devicesList)
 {
        EFI_LOADED_IMAGE *loadedImage;
-       if  (kBootServices->HandleProtocol(kImage, &LoadedImageGUID,
+       if (kBootServices->HandleProtocol(kImage, &LoadedImageGUID,
                        (void**)&loadedImage) != EFI_SUCCESS)
                return B_ERROR;
 
        EFI_DEVICE_PATH *devicePath, *node;
        if (kBootServices->HandleProtocol(loadedImage->DeviceHandle,
-                       &DevicePathGUID, (void **)&devicePath) != EFI_SUCCESS)
-               panic("Failed to lookup boot device path!");
+                       &DevicePathGUID, (void**)&devicePath) != EFI_SUCCESS)
+               return B_ERROR;
 
-       for (node = devicePath;DevicePathType(node) != MESSAGING_DEVICE_PATH;
+       dprintf("got device path for image handle\n");
+       dump_device_path(devicePath);
+       for (node = devicePath; DevicePathType(node) != MESSAGING_DEVICE_PATH;
                        node = NextDevicePathNode(node)) {
+               dprintf("finding messaging device path node\n");
+               dump_device_path(node);
                if (IsDevicePathEnd(node))
-                       panic("Could not find disk of EFI partition!");
+                       return B_ERROR;
        }
 
        SetDevicePathEndNode(NextDevicePathNode(node));
+       dprintf("updating device path end\n");
+       dump_device_path(devicePath);
+       dump_device_path(node);
+
+       UINTN length = device_path_length(devicePath);
+       EFI_DEVICE_PATH *savedDevicePath = (EFI_DEVICE_PATH*)malloc(length);
+       memcpy(savedDevicePath, devicePath, length);
+
 
        EFI_HANDLE handle;
        if (kBootServices->LocateDevicePath(&BlockIoGUID, &devicePath, &handle)
                        != EFI_SUCCESS)
-               panic("Cannot get boot device handle!");
+               return B_ERROR;
+
+       if (!IsDevicePathEnd(devicePath))
+               return B_ERROR;
+
+       dprintf("LocateDevicePath(BlockIoGUID, devicePath, handle)\n");
+       dump_device_path(savedDevicePath);
 
        EFI_BLOCK_IO *blockIo;
        if (kBootServices->HandleProtocol(handle, &BlockIoGUID, 
(void**)&blockIo)
                        != EFI_SUCCESS)
-               panic("Cannot get boot block io protocol!");
+               return B_ERROR;
 
        if (!blockIo->Media->MediaPresent)
-               panic("Boot disk has no media present!");
-
+               return B_ERROR;
 
-       EfiDevice *device = new(std::nothrow)EfiDevice(blockIo, devicePath);
+       EfiDevice *device = new(std::nothrow)EfiDevice(blockIo, 
savedDevicePath);
        if (device == NULL)
-               panic("Can't allocate memory for boot device!");
+               return B_ERROR;
+
+       dprintf("checking whether to add boot device to messaging devices 
list...\n");
+       if (add_device_path(&sMessagingDevices, savedDevicePath, handle)) {
+               dprintf("adding the boot device to the list\n");
+       } else {
+               dprintf("device already present in messaging devices\n");
+       }
 
+       dprintf(" => adding the boot device based on image handle\n");
        devicesList->Insert(device);
        return B_OK;
 }
 
 
-status_t
-platform_add_block_devices(struct stage2_args *args, NodeList *devicesList)
+static status_t
+add_cd_devices(NodeList *devicesList)
 {
-       EFI_BLOCK_IO *blockIo;
-       UINTN memSize = 0;
-
-       // Read to zero sized buffer to get memory needed for handles
-       if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 
0)
-                       != EFI_BUFFER_TOO_SMALL)
-               panic("Cannot read size of block device handles!");
+       device_handle *handle = NULL;
+       while ((handle = (device_handle*)list_get_next_item(&sMediaDevices, 
handle)) != NULL) {
+               EFI_DEVICE_PATH *node = handle->device_path;
+               while (!IsDevicePathEnd(NextDevicePathNode(node)))
+                       node = NextDevicePathNode(node);
 
-       uint32 noOfHandles = memSize / sizeof(EFI_HANDLE);
-
-       EFI_HANDLE handles[noOfHandles];
-       if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize,
-                       handles) != EFI_SUCCESS)
-               panic("Failed to locate block devices!");
+               if (DevicePathType(node) != MEDIA_DEVICE_PATH)
+                       continue;
 
-       for (uint32 n = 0; n < noOfHandles; n++) {
-               if (kBootServices->HandleProtocol(handles[n], &BlockIoGUID,
-                               (void**)&blockIo) != EFI_SUCCESS)
-                       panic("Cannot get block device handle!");
+               if (DevicePathSubType(node) != MEDIA_CDROM_DP)
+                       continue;
 
-               // Skip partition block devices, Haiku does partition scan
-               if (!blockIo->Media->MediaPresent || 
blockIo->Media->LogicalPartition)
+               device_handle *messaging_device = 
get_messaging_device_for_media_device(handle);
+               if (messaging_device == NULL) {
+                       dprintf("couldn't find messaging device for media 
device\n");
+                       continue;
+               }
+
+               EFI_BLOCK_IO *blockIo;
+               EFI_GUID blockIoGuid = BLOCK_IO_PROTOCOL;
+               EFI_STATUS status = 
kBootServices->HandleProtocol(messaging_device->handle,
+                       &blockIoGuid, (void**)&blockIo);
+               if (status != EFI_SUCCESS) {
+                       dprintf("unable to get block IO for device path\n");
                        continue;
+               }
+               if (!blockIo->Media->MediaPresent) {
+                       dprintf("media not present for device path\n");
+                       continue;
+               }
 
-               EFI_DEVICE_PATH *devicePath;
-               // Atm devicePath isn't necessary so result isn't checked
-               kBootServices->HandleProtocol(handles[n], &DevicePathGUID,
-                       (void**)&devicePath);
+               if (blockIo->Media->ReadOnly) {
+                       dprintf("note: media is readonly\n");
+               }
 
-               EfiDevice *device = new(std::nothrow)EfiDevice(blockIo, 
devicePath);
+               EfiDevice *device = new(std::nothrow)EfiDevice(blockIo, 
handle->device_path);
                if (device == NULL)
-                       panic("Can't allocate memory for block devices!");
+                       continue;
+
+               dprintf(" => adding a CD device\n");
                devicesList->Insert(device);
        }
+
        return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND;
 }
 
 
+static status_t
+add_remaining_devices(NodeList *devicesList)
+{
+       device_handle *node = NULL;
+       while ((node = (device_handle*)list_get_next_item(&sMessagingDevices, 
node)) != NULL) {
+               NodeIterator it = devicesList->GetIterator();
+               bool found = false;
+               while (it.HasNext()) {
+                       EfiDevice *device = (EfiDevice*)it.Next();
+                       // device->DevicePath() is a Media Device Path instance
+                       if (compare_device_paths(device->DevicePath(), 
node->device_path, true)) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found) {
+                       EFI_BLOCK_IO *blockIo;
+                       EFI_GUID blockIoGuid = BLOCK_IO_PROTOCOL;
+                       EFI_STATUS status = 
kBootServices->HandleProtocol(node->handle,
+                               &blockIoGuid, (void**)&blockIo);
+                       if (status != EFI_SUCCESS) {
+                               dprintf("unable to get block IO for device 
path\n");
+                               continue;
+                       }
+                       if (!blockIo->Media->MediaPresent) {
+                               dprintf("media not present for device path\n");
+                               continue;
+                       }
+
+                       EfiDevice *device = new(std::nothrow)EfiDevice(blockIo, 
node->device_path);
+                       if (device == NULL)
+                               continue;
+
+                       devicesList->Insert(device);
+                       dprintf(" => added a new messaging device to devices 
list\n");
+               } else {
+                       dprintf(" => skipping existing messaging device 
path\n");
+               }
+       }
+
+       return B_OK;
+}
+
+
+static bool
+get_boot_uuid(void)
+{
+       return false;
+}
+
+
+status_t
+platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
+{
+       // This is the first entry point, so init the lists here
+       list_init(&sMessagingDevices);
+       list_init(&sMediaDevices);
+
+       build_device_handles();
+
+       if (get_boot_uuid()) {
+               // If we have the UUID, add the boot device containing that 
partition
+               return add_boot_device(devicesList);
+       } else {
+               // If we don't have a UUID, add all CD devices with media, and 
the
+               // device that haiku_loader.efi is located on
+               add_boot_device_for_image(devicesList);
+                       // We do this first, so that booting from CD is the 
fallback
+               add_cd_devices(devicesList);
+               if (devicesList->Count() > 0)
+                       return B_OK;
+       }
+
+       // Otherwise, we don't know what the boot device is; defer to
+       // platform_add_block_devices() to add the rest
+       return B_ENTRY_NOT_FOUND;
+}
+
+
+status_t
+platform_add_block_devices(struct stage2_args *args, NodeList *devicesList)
+{
+       return add_remaining_devices(devicesList);
+}
+
+
 status_t
 platform_get_boot_partition(struct stage2_args *args, Node *bootDevice,
                NodeList *partitions, boot::Partition **_partition)
@@ -197,10 +508,14 @@ platform_get_boot_partition(struct stage2_args *args, 
Node *bootDevice,
        NodeIterator iterator = partitions->GetIterator();
        boot::Partition *partition = NULL;
        while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
-               //Currently we pick first partition. If not found menu lets 
user select.
+               // Currently we pick last partition; seems to work enough for 
now
+               // until we can actually identify the partition we want to boot
+               // from
                *_partition = partition;
-               return B_OK;
+               if (!iterator.HasNext())
+                       return B_OK;
        }
+
        return B_ENTRY_NOT_FOUND;
 }
 
@@ -222,9 +537,8 @@ platform_register_boot_device(Node *device)
                identifier.device.unknown.check_sums[i].sum = 
compute_check_sum(device, offset);
        }
 
-       gBootVolume.SetInt32(BOOT_METHOD, efiDevice->IsCD() ? BOOT_METHOD_CD:
-               BOOT_METHOD_HARD_DISK);
-       gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, true);
+       gBootVolume.SetInt32(BOOT_METHOD, efiDevice->BootMethod());
+       gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, 
efiDevice->ReadOnly());
        gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
                &identifier, sizeof(disk_identifier));
 


Other related posts:

  • » [haiku-commits] BRANCH jessicah-github.uefi-devices [6a569b28b184] in src/system/boot: platform/efi loader - jessicah-github . uefi-devices