2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2010 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/loader.h>
2N/A#include <grub/file.h>
2N/A#include <grub/err.h>
2N/A#include <grub/device.h>
2N/A#include <grub/disk.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/types.h>
2N/A#include <grub/partition.h>
2N/A#include <grub/msdos_partition.h>
2N/A#include <grub/scsi.h>
2N/A#include <grub/dl.h>
2N/A#include <grub/command.h>
2N/A#include <grub/i18n.h>
2N/A#include <grub/video.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/cpu/relocator.h>
2N/A#include <grub/extcmd.h>
2N/A
2N/AGRUB_MOD_LICENSE ("GPLv3+");
2N/A
2N/Astatic grub_dl_t my_mod;
2N/Astatic struct grub_relocator *rel;
2N/Astatic grub_uint32_t eip = 0xffffffff;
2N/A
2N/A#define GRUB_PLAN9_TARGET 0x100000
2N/A#define GRUB_PLAN9_ALIGN 4096
2N/A#define GRUB_PLAN9_CONFIG_ADDR 0x001200
2N/A#define GRUB_PLAN9_CONFIG_PATH_SIZE 0x000040
2N/A#define GRUB_PLAN9_CONFIG_MAGIC "ZORT 0\r\n"
2N/A
2N/Astatic const struct grub_arg_option options[] =
2N/A {
2N/A {"map", 'm', GRUB_ARG_OPTION_REPEATABLE,
2N/A N_("Override guessed mapping of Plan9 devices."),
2N/A N_("GRUBDEVICE=PLAN9DEVICE"),
2N/A ARG_TYPE_STRING},
2N/A {0, 0, 0, 0, 0, 0}
2N/A };
2N/A
2N/Astruct grub_plan9_header
2N/A{
2N/A grub_uint32_t magic;
2N/A#define GRUB_PLAN9_MAGIC 0x1eb
2N/A grub_uint32_t text_size;
2N/A grub_uint32_t data_size;
2N/A grub_uint32_t bss_size;
2N/A grub_uint32_t sectiona;
2N/A grub_uint32_t entry_addr;
2N/A grub_uint32_t zero;
2N/A grub_uint32_t sectionb;
2N/A};
2N/A
2N/Astatic grub_err_t
2N/Agrub_plan9_boot (void)
2N/A{
2N/A struct grub_relocator32_state state = {
2N/A .eax = 0,
2N/A .eip = eip,
2N/A .ebx = 0,
2N/A .ecx = 0,
2N/A .edx = 0,
2N/A .edi = 0,
2N/A .esp = 0,
2N/A .ebp = 0,
2N/A .esi = 0
2N/A };
2N/A grub_video_set_mode ("text", 0, 0);
2N/A
2N/A return grub_relocator32_boot (rel, state);
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_plan9_unload (void)
2N/A{
2N/A grub_relocator_unload (rel);
2N/A rel = NULL;
2N/A grub_dl_unref (my_mod);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_cmd_plan9 (grub_extcmd_context_t ctxt, int argc, char *argv[])
2N/A{
2N/A grub_file_t file = 0;
2N/A void *mem;
2N/A grub_size_t memsize, padsize;
2N/A struct grub_plan9_header hdr;
2N/A char *config, *configptr;
2N/A grub_size_t configsize;
2N/A char *pmap = NULL;
2N/A grub_size_t pmapalloc = 256;
2N/A grub_size_t pmapptr = 0;
2N/A int noslash = 1;
2N/A char prefixes[5][10] = {"dos", "plan9", "ntfs", "linux", "linuxswap"};
2N/A int prefixescnt[5];
2N/A char *bootdisk = NULL, *bootpart = NULL, *bootpath = NULL;
2N/A
2N/A auto int fill_partition (grub_disk_t disk,
2N/A const grub_partition_t partition);
2N/A int fill_partition (grub_disk_t disk,
2N/A const grub_partition_t partition)
2N/A {
2N/A int file_disk = 0;
2N/A int pstart, pend;
2N/A if (!noslash)
2N/A {
2N/A if (grub_extend_alloc (pmapptr + 1, &pmapalloc, (void **) &pmap))
2N/A return 1;
2N/A pmap[pmapptr++] = '/';
2N/A }
2N/A noslash = 0;
2N/A
2N/A file_disk = file->device->disk && disk->id == file->device->disk->id
2N/A && disk->dev->id == file->device->disk->dev->id;
2N/A
2N/A pstart = pmapptr;
2N/A if (grub_strcmp (partition->partmap->name, "plan") == 0)
2N/A {
2N/A unsigned ptr = partition->index + sizeof ("part ") - 1;
2N/A grub_err_t err;
2N/A disk->partition = partition->parent;
2N/A do
2N/A {
2N/A if (grub_extend_alloc (pmapptr + 1, &pmapalloc, (void **) &pmap))
2N/A return 1;
2N/A err = grub_disk_read (disk, 1, ptr, 1, pmap + pmapptr);
2N/A if (err)
2N/A {
2N/A disk->partition = 0;
2N/A return err;
2N/A }
2N/A ptr++;
2N/A pmapptr++;
2N/A }
2N/A while (grub_isalpha (pmap[pmapptr - 1])
2N/A || grub_isdigit (pmap[pmapptr - 1]));
2N/A pmapptr--;
2N/A }
2N/A else
2N/A {
2N/A char name[50];
2N/A int c = 0;
2N/A if (grub_strcmp (partition->partmap->name, "msdos") == 0)
2N/A {
2N/A switch (partition->msdostype)
2N/A {
2N/A case GRUB_PC_PARTITION_TYPE_PLAN9:
2N/A c = 1;
2N/A break;
2N/A case GRUB_PC_PARTITION_TYPE_NTFS:
2N/A c = 2;
2N/A break;
2N/A case GRUB_PC_PARTITION_TYPE_MINIX:
2N/A case GRUB_PC_PARTITION_TYPE_LINUX_MINIX:
2N/A case GRUB_PC_PARTITION_TYPE_EXT2FS:
2N/A c = 3;
2N/A break;
2N/A case GRUB_PC_PARTITION_TYPE_LINUX_SWAP:
2N/A c = 4;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (prefixescnt[c] == 0)
2N/A grub_strcpy (name, prefixes[c]);
2N/A else
2N/A grub_snprintf (name, sizeof (name), "%s.%d", prefixes[c],
2N/A prefixescnt[c]);
2N/A prefixescnt[c]++;
2N/A if (grub_extend_alloc (pmapptr + grub_strlen (name) + 1,
2N/A &pmapalloc, (void **) &pmap))
2N/A return 1;
2N/A grub_strcpy (pmap + pmapptr, name);
2N/A pmapptr += grub_strlen (name);
2N/A }
2N/A pend = pmapptr;
2N/A if (grub_extend_alloc (pmapptr + 2 + 25 + 5 + 25, &pmapalloc,
2N/A (void **) &pmap))
2N/A return 1;
2N/A pmap[pmapptr++] = ' ';
2N/A grub_snprintf (pmap + pmapptr, 25 + 5 + 25,
2N/A "%" PRIuGRUB_UINT64_T " %" PRIuGRUB_UINT64_T,
2N/A grub_partition_get_start (partition),
2N/A grub_partition_get_start (partition)
2N/A + grub_partition_get_len (partition));
2N/A if (file_disk && grub_partition_get_start (partition)
2N/A == grub_partition_get_start (file->device->disk->partition)
2N/A && grub_partition_get_len (partition)
2N/A == grub_partition_get_len (file->device->disk->partition))
2N/A {
2N/A grub_free (bootpart);
2N/A bootpart = grub_strndup (pmap + pstart, pend - pstart);
2N/A }
2N/A
2N/A pmapptr += grub_strlen (pmap + pmapptr);
2N/A return 0;
2N/A }
2N/A
2N/A auto int fill_disk (const char *name);
2N/A int fill_disk (const char *name)
2N/A {
2N/A grub_device_t dev;
2N/A char *plan9name = NULL;
2N/A unsigned i;
2N/A int file_disk = 0;
2N/A
2N/A dev = grub_device_open (name);
2N/A if (!dev)
2N/A {
2N/A grub_print_error ();
2N/A return 0;
2N/A }
2N/A if (!dev->disk)
2N/A {
2N/A grub_device_close (dev);
2N/A return 0;
2N/A }
2N/A file_disk = file->device->disk && dev->disk->id == file->device->disk->id
2N/A && dev->disk->dev->id == file->device->disk->dev->id;
2N/A for (i = 0; ctxt->state[0].args && ctxt->state[0].args[i]; i++)
2N/A if (grub_strncmp (name, ctxt->state[0].args[i], grub_strlen (name)) == 0
2N/A && ctxt->state[0].args[i][grub_strlen (name)] == '=')
2N/A break;
2N/A if (ctxt->state[0].args && ctxt->state[0].args[i])
2N/A plan9name = grub_strdup (ctxt->state[0].args[i] + grub_strlen (name) + 1);
2N/A else
2N/A switch (dev->disk->dev->id)
2N/A {
2N/A case GRUB_DISK_DEVICE_BIOSDISK_ID:
2N/A if (dev->disk->id & 0x80)
2N/A plan9name = grub_xasprintf ("sdB%u",
2N/A (unsigned) (dev->disk->id & 0x7f));
2N/A else
2N/A plan9name = grub_xasprintf ("fd%u",
2N/A (unsigned) (dev->disk->id & 0x7f));
2N/A break;
2N/A /* Shouldn't happen as Plan9 doesn't work on these platforms. */
2N/A case GRUB_DISK_DEVICE_OFDISK_ID:
2N/A case GRUB_DISK_DEVICE_EFIDISK_ID:
2N/A
2N/A /* Plan9 doesn't see those. */
2N/A default:
2N/A
2N/A /* Not sure how to handle those. */
2N/A case GRUB_DISK_DEVICE_PXE_ID:
2N/A case GRUB_DISK_DEVICE_NAND_ID:
2N/A if (!file_disk)
2N/A {
2N/A grub_device_close (dev);
2N/A return 0;
2N/A }
2N/A
2N/A /* if it's the disk the kernel is loaded from we need to name
2N/A it nevertheless. */
2N/A plan9name = grub_strdup ("sdZ0");
2N/A break;
2N/A
2N/A case GRUB_DISK_DEVICE_ATA_ID:
2N/A {
2N/A int unit;
2N/A if (grub_strlen (dev->disk->name) < sizeof ("ata0") - 1)
2N/A unit = 0;
2N/A else
2N/A unit = grub_strtoul (dev->disk->name + sizeof ("ata0") - 1, 0, 0);
2N/A plan9name = grub_xasprintf ("sd%c%d", 'C' + unit / 2, unit % 2);
2N/A }
2N/A break;
2N/A case GRUB_DISK_DEVICE_SCSI_ID:
2N/A if (((dev->disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xff)
2N/A == GRUB_SCSI_SUBSYSTEM_PATA)
2N/A {
2N/A int unit;
2N/A if (grub_strlen (dev->disk->name) < sizeof ("ata0") - 1)
2N/A unit = 0;
2N/A else
2N/A unit = grub_strtoul (dev->disk->name + sizeof ("ata0") - 1,
2N/A 0, 0);
2N/A plan9name = grub_xasprintf ("sd%c%d", 'C' + unit / 2, unit % 2);
2N/A break;
2N/A }
2N/A
2N/A /* FIXME: how does Plan9 number controllers?
2N/A We probably need save the SCSI devices and sort them */
2N/A plan9name
2N/A = grub_xasprintf ("sd0%u", (unsigned)
2N/A ((dev->disk->id >> GRUB_SCSI_ID_BUS_SHIFT)
2N/A & 0xf));
2N/A break;
2N/A }
2N/A if (!plan9name)
2N/A {
2N/A grub_print_error ();
2N/A return 0;
2N/A }
2N/A if (grub_extend_alloc (pmapptr + grub_strlen (plan9name)
2N/A + sizeof ("part="), &pmapalloc,
2N/A (void **) &pmap))
2N/A {
2N/A grub_free (plan9name);
2N/A return 1;
2N/A }
2N/A grub_strcpy (pmap + pmapptr, plan9name);
2N/A pmapptr += grub_strlen (plan9name);
2N/A if (!file_disk)
2N/A grub_free (plan9name);
2N/A else
2N/A {
2N/A grub_free (bootdisk);
2N/A bootdisk = plan9name;
2N/A }
2N/A grub_strcpy (pmap + pmapptr, "part=");
2N/A pmapptr += sizeof ("part=") - 1;
2N/A
2N/A noslash = 1;
2N/A grub_memset (prefixescnt, 0, sizeof (prefixescnt));
2N/A if (grub_partition_iterate (dev->disk, fill_partition))
2N/A return 1;
2N/A if (grub_extend_alloc (pmapptr + 1, &pmapalloc, (void **) &pmap))
2N/A return 1;
2N/A pmap[pmapptr++] = '\n';
2N/A
2N/A return 0;
2N/A }
2N/A
2N/A if (argc == 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified");
2N/A
2N/A grub_dl_ref (my_mod);
2N/A
2N/A rel = grub_relocator_new ();
2N/A if (!rel)
2N/A goto fail;
2N/A
2N/A file = grub_file_open (argv[0]);
2N/A if (! file)
2N/A goto fail;
2N/A
2N/A pmap = grub_malloc (pmapalloc);
2N/A if (!pmap)
2N/A goto fail;
2N/A
2N/A if (grub_disk_dev_iterate (fill_disk))
2N/A goto fail;
2N/A
2N/A if (grub_extend_alloc (pmapptr + 1, &pmapalloc,
2N/A (void **) &pmap))
2N/A goto fail;
2N/A pmap[pmapptr] = 0;
2N/A
2N/A {
2N/A char *file_name = grub_strchr (argv[0], ')');
2N/A if (file_name)
2N/A file_name++;
2N/A else
2N/A file_name = argv[0];
2N/A if (*file_name)
2N/A file_name++;
2N/A
2N/A if (bootpart)
2N/A bootpath = grub_xasprintf ("%s!%s!%s", bootdisk, bootpart, file_name);
2N/A else
2N/A bootpath = grub_xasprintf ("%s!%s", bootdisk, file_name);
2N/A grub_free (bootdisk);
2N/A grub_free (bootpart);
2N/A }
2N/A if (!bootpath)
2N/A goto fail;
2N/A
2N/A if (grub_file_read (file, &hdr, sizeof (hdr)) != (grub_ssize_t) sizeof (hdr))
2N/A goto fail;
2N/A
2N/A if (grub_be_to_cpu32 (hdr.magic) != GRUB_PLAN9_MAGIC
2N/A || hdr.zero)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_OS, "unsupported Plan9");
2N/A goto fail;
2N/A }
2N/A
2N/A memsize = ALIGN_UP (grub_be_to_cpu32 (hdr.text_size) + sizeof (hdr),
2N/A GRUB_PLAN9_ALIGN);
2N/A memsize += ALIGN_UP (grub_be_to_cpu32 (hdr.data_size), GRUB_PLAN9_ALIGN);
2N/A memsize += ALIGN_UP(grub_be_to_cpu32 (hdr.bss_size), GRUB_PLAN9_ALIGN);
2N/A eip = grub_be_to_cpu32 (hdr.entry_addr) & 0xfffffff;
2N/A
2N/A /* path */
2N/A configsize = GRUB_PLAN9_CONFIG_PATH_SIZE;
2N/A /* magic */
2N/A configsize += sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1;
2N/A {
2N/A int i;
2N/A for (i = 1; i < argc; i++)
2N/A configsize += grub_strlen (argv[i]) + 1;
2N/A }
2N/A configsize += (sizeof ("bootfile=") - 1) + grub_strlen (bootpath) + 1;
2N/A configsize += pmapptr;
2N/A /* Terminating \0. */
2N/A configsize++;
2N/A
2N/A {
2N/A grub_relocator_chunk_t ch;
2N/A grub_err_t err;
2N/A err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_PLAN9_CONFIG_ADDR,
2N/A configsize);
2N/A if (err)
2N/A goto fail;
2N/A config = get_virtual_current_address (ch);
2N/A }
2N/A
2N/A grub_memset (config, 0, GRUB_PLAN9_CONFIG_PATH_SIZE);
2N/A grub_strncpy (config, bootpath, GRUB_PLAN9_CONFIG_PATH_SIZE - 1);
2N/A
2N/A configptr = config + GRUB_PLAN9_CONFIG_PATH_SIZE;
2N/A grub_memcpy (configptr, GRUB_PLAN9_CONFIG_MAGIC,
2N/A sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1);
2N/A configptr += sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1;
2N/A configptr = grub_stpcpy (configptr, "bootfile=");
2N/A configptr = grub_stpcpy (configptr, bootpath);
2N/A *configptr++ = '\n';
2N/A {
2N/A int i;
2N/A for (i = 1; i < argc; i++)
2N/A {
2N/A configptr = grub_stpcpy (configptr, argv[i]);
2N/A *configptr++ = '\n';
2N/A }
2N/A }
2N/A configptr = grub_stpcpy (configptr, pmap);
2N/A
2N/A {
2N/A grub_relocator_chunk_t ch;
2N/A grub_err_t err;
2N/A
2N/A err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_PLAN9_TARGET,
2N/A memsize);
2N/A if (err)
2N/A goto fail;
2N/A mem = get_virtual_current_address (ch);
2N/A }
2N/A
2N/A {
2N/A grub_uint8_t *ptr;
2N/A ptr = mem;
2N/A grub_memcpy (ptr, &hdr, sizeof (hdr));
2N/A ptr += sizeof (hdr);
2N/A
2N/A if (grub_file_read (file, ptr, grub_be_to_cpu32 (hdr.text_size))
2N/A != (grub_ssize_t) grub_be_to_cpu32 (hdr.text_size))
2N/A goto fail;
2N/A ptr += grub_be_to_cpu32 (hdr.text_size);
2N/A padsize = ALIGN_UP (grub_be_to_cpu32 (hdr.text_size) + sizeof (hdr),
2N/A GRUB_PLAN9_ALIGN) - grub_be_to_cpu32 (hdr.text_size)
2N/A - sizeof (hdr);
2N/A
2N/A grub_memset (ptr, 0, padsize);
2N/A ptr += padsize;
2N/A
2N/A if (grub_file_read (file, ptr, grub_be_to_cpu32 (hdr.data_size))
2N/A != (grub_ssize_t) grub_be_to_cpu32 (hdr.data_size))
2N/A goto fail;
2N/A ptr += grub_be_to_cpu32 (hdr.data_size);
2N/A padsize = ALIGN_UP (grub_be_to_cpu32 (hdr.data_size), GRUB_PLAN9_ALIGN)
2N/A - grub_be_to_cpu32 (hdr.data_size);
2N/A
2N/A grub_memset (ptr, 0, padsize);
2N/A ptr += padsize;
2N/A grub_memset (ptr, 0, ALIGN_UP(grub_be_to_cpu32 (hdr.bss_size),
2N/A GRUB_PLAN9_ALIGN));
2N/A }
2N/A grub_loader_set (grub_plan9_boot, grub_plan9_unload, 1);
2N/A return GRUB_ERR_NONE;
2N/A
2N/A fail:
2N/A grub_free (pmap);
2N/A
2N/A if (file)
2N/A grub_file_close (file);
2N/A
2N/A grub_plan9_unload ();
2N/A
2N/A return grub_errno;
2N/A}
2N/A
2N/Astatic grub_extcmd_t cmd;
2N/A
2N/AGRUB_MOD_INIT(plan9)
2N/A{
2N/A cmd = grub_register_extcmd ("plan9", grub_cmd_plan9,
2N/A GRUB_COMMAND_OPTIONS_AT_START,
2N/A N_("KERNEL ARGS"), N_("Load Plan9 kernel."),
2N/A options);
2N/A my_mod = mod;
2N/A}
2N/A
2N/AGRUB_MOD_FINI(plan9)
2N/A{
2N/A grub_unregister_extcmd (cmd);
2N/A}