⛏️ index : haiku.git

/*
 * Copyright 2003-2009, Axel DΓΆrfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include "loader.h"
#include "elf.h"
#include "RootFileSystem.h"

#include <directories.h>
#include <OS.h>
#include <util/list.h>
#include <boot/stage2.h>
#include <boot/vfs.h>
#include <boot/platform.h>
#include <boot/stdio.h>
#include <boot/partitions.h>

#include <unistd.h>
#include <string.h>


#ifndef BOOT_ARCH
#	error BOOT_ARCH has to be defined to differentiate the kernel per platform
#endif

#define SYSTEM_DIRECTORY_PREFIX	"system/"
#define KERNEL_IMAGE	"kernel_" BOOT_ARCH
#define KERNEL_PATH		SYSTEM_DIRECTORY_PREFIX KERNEL_IMAGE

#ifdef ALTERNATE_BOOT_ARCH
# define ALTERNATE_KERNEL_IMAGE	"kernel_" ALTERNATE_BOOT_ARCH
# define ALTERNATE_KERNEL_PATH	"system/" ALTERNATE_KERNEL_IMAGE
#endif


static const char* const kSystemDirectoryPrefix = SYSTEM_DIRECTORY_PREFIX;

static const char *sKernelPaths[][2] = {
	{ KERNEL_PATH, KERNEL_IMAGE },
#ifdef ALTERNATE_BOOT_ARCH
	{ ALTERNATE_KERNEL_PATH, ALTERNATE_KERNEL_IMAGE },
#endif
	{ NULL, NULL },
};

static const char *sAddonPaths[] = {
	kVolumeLocalSystemKernelAddonsDirectory,
	kVolumeLocalCommonNonpackagedKernelAddonsDirectory,
	kVolumeLocalCommonKernelAddonsDirectory,
	kVolumeLocalUserNonpackagedKernelAddonsDirectory,
	kVolumeLocalUserKernelAddonsDirectory,
	NULL
};


int
open_maybe_packaged(BootVolume& volume, const char* path, int openMode)
{
	if (strncmp(path, kSystemDirectoryPrefix, strlen(kSystemDirectoryPrefix))
			== 0) {
		path += strlen(kSystemDirectoryPrefix);
		return open_from(volume.SystemDirectory(), path, openMode);
	}

	return open_from(volume.RootDirectory(), path, openMode);
}


static int
find_kernel(BootVolume& volume, const char** name = NULL)
{
	for (int32 i = 0; sKernelPaths[i][0] != NULL; i++) {
		int fd = open_maybe_packaged(volume, sKernelPaths[i][0], O_RDONLY);
		if (fd >= 0) {
			if (name)
				*name = sKernelPaths[i][1];

			return fd;
		}
	}

	return B_ENTRY_NOT_FOUND;
}


bool
is_bootable(Directory *volume)
{
	if (volume->IsEmpty())
		return false;

	BootVolume bootVolume;
	if (bootVolume.SetTo(volume) != B_OK)
		return false;

	// check for the existance of a kernel (for our platform)
	int fd = find_kernel(bootVolume);
	if (fd < 0)
		return false;

	close(fd);

	return true;
}


status_t
load_kernel(stage2_args* args, BootVolume& volume)
{
	const char *name;
	int fd = find_kernel(volume, &name);
	if (fd < B_OK)
		return fd;

	dprintf("load kernel %s...\n", name);

	elf_init();
	preloaded_image *image;
	status_t status = elf_load_image(fd, &image);

	close(fd);

	if (status < B_OK) {
		dprintf("loading kernel failed: %" B_PRIx32 "!\n", status);
		return status;
	}

	gKernelArgs.kernel_image = image;

	status = elf_relocate_image(gKernelArgs.kernel_image);
	if (status < B_OK) {
		dprintf("relocating kernel failed: %" B_PRIx32 "!\n", status);
		return status;
	}

	gKernelArgs.kernel_image->name = kernel_args_strdup(name);

	return B_OK;
}


static status_t
load_modules_from(BootVolume& volume, const char* path)
{
	// we don't have readdir() & co. (yet?)...

	int fd = open_maybe_packaged(volume, path, O_RDONLY);
	if (fd < B_OK)
		return fd;

	Directory *modules = (Directory *)get_node_from(fd);
	if (modules == NULL)
		return B_ENTRY_NOT_FOUND;

	void *cookie;
	if (modules->Open(&cookie, O_RDONLY) == B_OK) {
		char name[B_FILE_NAME_LENGTH];
		while (modules->GetNextEntry(cookie, name, sizeof(name)) == B_OK) {
			if (!strcmp(name, ".") || !strcmp(name, ".."))
				continue;

			status_t status = elf_load_image(modules, name);
			if (status != B_OK)
				dprintf("Could not load \"%s\" error %" B_PRIx32 "\n", name, status);
		}

		modules->Close(cookie);
	}

	return B_OK;
}


status_t
load_modules(stage2_args* args, BootVolume& volume)
{
	int32 failed = 0;

	// ToDo: this should be mostly replaced by a hardware oriented detection mechanism

	int32 i = 0;
	for (; sAddonPaths[i]; i++) {
		char path[B_FILE_NAME_LENGTH];
		snprintf(path, sizeof(path), "%s/boot", sAddonPaths[i]);

		if (load_modules_from(volume, path) != B_OK)
			failed++;
	}

	if (failed == i) {
		// couldn't load any boot modules
		// fall back to load all modules (currently needed by the boot floppy)
		const char *paths[] = { "bus_managers", "busses/ide", "busses/scsi",
			"generic", "partitioning_systems", "drivers/bin", NULL};

		for (int32 i = 0; paths[i]; i++) {
			char path[B_FILE_NAME_LENGTH];
			snprintf(path, sizeof(path), "%s/%s", sAddonPaths[0], paths[i]);
			load_modules_from(volume, path);
		}
	}

	// and now load all partitioning and file system modules
	char path[B_FILE_NAME_LENGTH];
	snprintf(path, sizeof(path), "%s/%s", sAddonPaths[0], "file_systems");
	load_modules_from(volume, path);
	snprintf(path, sizeof(path), "%s/%s", sAddonPaths[0], "partitioning_systems");
	load_modules_from(volume, path);

	return B_OK;
}