merge mainline into hints

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-12-23 18:49:00 +01:00
commit 17785932df
448 changed files with 43023 additions and 10176 deletions

View file

@ -0,0 +1,89 @@
/*
* AFsplitter - Anti forensic information splitter
* Copyright 2004, Clemens Fruhwirth <clemens@endorphin.org>
*
* AFsplitter diffuses information over a large stripe of data,
* therefor supporting secure data destruction.
*
* This program is grub_free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the grub_free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the grub_free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <grub/crypto.h>
#include <grub/mm.h>
#include <grub/misc.h>
gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src,
grub_uint8_t * dst, grub_size_t blocksize,
grub_size_t blocknumbers);
static void
diffuse (const gcry_md_spec_t * hash, grub_uint8_t * src,
grub_uint8_t * dst, grub_size_t size)
{
grub_size_t i;
grub_uint32_t IV; /* host byte order independend hash IV */
grub_size_t fullblocks = size / hash->mdlen;
int padding = size % hash->mdlen;
grub_uint8_t final[hash->mdlen];
grub_uint8_t temp[sizeof (IV) + hash->mdlen];
/* hash block the whole data set with different IVs to produce
* more than just a single data block
*/
for (i = 0; i < fullblocks; i++)
{
IV = grub_cpu_to_be32 (i);
grub_memcpy (temp, &IV, sizeof (IV));
grub_memcpy (temp + sizeof (IV), src + hash->mdlen * i, hash->mdlen);
grub_crypto_hash (hash, dst + hash->mdlen * i, temp,
sizeof (IV) + hash->mdlen);
}
if (padding)
{
IV = grub_cpu_to_be32 (i);
grub_memcpy (temp, &IV, sizeof (IV));
grub_memcpy (temp + sizeof (IV), src + hash->mdlen * i, padding);
grub_crypto_hash (hash, final, temp, sizeof (IV) + padding);
grub_memcpy (dst + hash->mdlen * i, final, padding);
}
}
/**
* Merges the splitted master key stored on disk into the original key
*/
gcry_err_code_t
AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, grub_uint8_t * dst,
grub_size_t blocksize, grub_size_t blocknumbers)
{
grub_size_t i;
grub_uint8_t *bufblock;
bufblock = grub_zalloc (blocksize);
if (bufblock == NULL)
return GPG_ERR_OUT_OF_MEMORY;
grub_memset (bufblock, 0, blocksize);
for (i = 0; i < blocknumbers - 1; i++)
{
grub_crypto_xor (bufblock, src + (blocksize * i), bufblock, blocksize);
diffuse (hash, bufblock, bufblock, blocksize);
}
grub_crypto_xor (dst, src + (i * blocksize), bufblock, blocksize);
grub_free (bufblock);
return GPG_ERR_NO_ERROR;
}

738
grub-core/disk/ahci.c Normal file
View file

@ -0,0 +1,738 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/dl.h>
#include <grub/disk.h>
#include <grub/mm.h>
#include <grub/time.h>
#include <grub/pci.h>
#include <grub/ata.h>
#include <grub/scsi.h>
#include <grub/misc.h>
#include <grub/list.h>
#include <grub/loader.h>
GRUB_MOD_LICENSE ("GPLv3+");
struct grub_ahci_cmd_head
{
grub_uint32_t config;
grub_uint32_t transfered;
grub_uint64_t command_table_base;
grub_uint32_t unused[4];
};
struct grub_ahci_prdt_entry
{
grub_uint64_t data_base;
grub_uint32_t unused;
grub_uint32_t size;
};
struct grub_ahci_cmd_table
{
grub_uint8_t cfis[0x40];
grub_uint8_t command[0x10];
grub_uint8_t reserved[0x30];
struct grub_ahci_prdt_entry prdt[1];
};
struct grub_ahci_hba_port
{
grub_uint64_t command_list_base;
grub_uint64_t fis_base;
grub_uint32_t intstatus;
grub_uint32_t inten;
grub_uint32_t command;
grub_uint32_t unused1;
grub_uint32_t task_file_data;
grub_uint32_t sig;
grub_uint32_t status;
grub_uint32_t unused2;
grub_uint32_t sata_error;
grub_uint32_t sata_active;
grub_uint32_t command_issue;
grub_uint32_t unused3;
grub_uint32_t fbs;
grub_uint32_t unused4[15];
};
enum grub_ahci_hba_port_command
{
GRUB_AHCI_HBA_PORT_CMD_ST = 0x01,
GRUB_AHCI_HBA_PORT_CMD_FRE = 0x10,
GRUB_AHCI_HBA_PORT_CMD_CR = 0x8000,
GRUB_AHCI_HBA_PORT_CMD_FR = 0x4000,
};
struct grub_ahci_hba
{
grub_uint32_t cap;
grub_uint32_t global_control;
grub_uint32_t intr_status;
grub_uint32_t ports_implemented;
grub_uint32_t unused1[6];
grub_uint32_t bios_handoff;
grub_uint32_t unused2[53];
struct grub_ahci_hba_port ports[32];
};
struct grub_ahci_received_fis
{
char raw[4096];
};
enum
{
GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f
};
enum
{
GRUB_AHCI_HBA_GLOBAL_CONTROL_RESET = 0x00000001,
GRUB_AHCI_HBA_GLOBAL_CONTROL_INTR_EN = 0x00000002,
GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN = 0x80000000,
};
enum
{
GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED = 1,
GRUB_AHCI_BIOS_HANDOFF_OS_OWNED = 2,
GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED = 8,
GRUB_AHCI_BIOS_HANDOFF_RWC = 8
};
struct grub_ahci_device
{
struct grub_ahci_device *next;
volatile struct grub_ahci_hba *hba;
int port;
int num;
struct grub_pci_dma_chunk *command_list_chunk;
volatile struct grub_ahci_cmd_head *command_list;
struct grub_pci_dma_chunk *command_table_chunk;
volatile struct grub_ahci_cmd_table *command_table;
struct grub_pci_dma_chunk *rfis;
int present;
};
static grub_err_t
grub_ahci_readwrite_real (struct grub_ahci_device *dev,
struct grub_disk_ata_pass_through_parms *parms,
int spinup);
enum
{
GRUB_AHCI_CONFIG_READ = 0,
GRUB_AHCI_CONFIG_CFIS_LENGTH_MASK = 0x1f,
GRUB_AHCI_CONFIG_ATAPI = 0x20,
GRUB_AHCI_CONFIG_WRITE = 0x40,
GRUB_AHCI_CONFIG_PREFETCH = 0x80,
GRUB_AHCI_CONFIG_RESET = 0x100,
GRUB_AHCI_CONFIG_BIST = 0x200,
GRUB_AHCI_CONFIG_CLEAR_R_OK = 0x400,
GRUB_AHCI_CONFIG_PMP_MASK = 0xf000,
GRUB_AHCI_CONFIG_PRDT_LENGTH_MASK = 0xffff0000,
};
#define GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT 0
#define GRUB_AHCI_CONFIG_PMP_SHIFT 12
#define GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT 16
#define GRUB_AHCI_INTERRUPT_ON_COMPLETE 0x80000000
#define GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH 0x200000
static struct grub_ahci_device *grub_ahci_devices;
static int numdevs;
static int
init_port (struct grub_ahci_device *dev)
{
struct grub_pci_dma_chunk *command_list;
struct grub_pci_dma_chunk *command_table;
grub_uint64_t endtime;
command_list = grub_memalign_dma32 (1024, sizeof (struct grub_ahci_cmd_head));
if (!command_list)
return 1;
command_table = grub_memalign_dma32 (1024,
sizeof (struct grub_ahci_cmd_table));
if (!command_table)
{
grub_dma_free (command_list);
return 1;
}
grub_dprintf ("ahci", "found device ahci%d (port %d)\n", dev->num, dev->port);
dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
endtime = grub_get_time_ms () + 1000;
while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't stop FR\n");
goto out;
}
dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
endtime = grub_get_time_ms () + 1000;
while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't stop CR\n");
goto out;
}
dev->hba->ports[dev->port].fbs = 2;
dev->rfis = grub_memalign_dma32 (4096,
sizeof (struct grub_ahci_received_fis));
grub_memset ((char *) grub_dma_get_virt (dev->rfis), 0,
sizeof (struct grub_ahci_received_fis));
dev->hba->ports[dev->port].fis_base = grub_dma_get_phys (dev->rfis);
dev->hba->ports[dev->port].command_list_base
= grub_dma_get_phys (command_list);
dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_FRE;
while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't start FR\n");
dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
goto out;
}
dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST;
while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't start CR\n");
dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_CR;
goto out_stop_fr;
}
dev->hba->ports[dev->port].command
= (dev->hba->ports[dev->port].command & 0x0fffffff) | (1 << 28) | 2 | 4;
dev->command_list_chunk = command_list;
dev->command_list = grub_dma_get_virt (command_list);
dev->command_table_chunk = command_table;
dev->command_table = grub_dma_get_virt (command_table);
dev->command_list->command_table_base
= grub_dma_get_phys (command_table);
return 0;
out_stop_fr:
dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
endtime = grub_get_time_ms () + 1000;
while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't stop FR\n");
break;
}
out:
grub_dma_free (command_list);
grub_dma_free (command_table);
grub_dma_free (dev->rfis);
return 1;
}
static int NESTED_FUNC_ATTR
grub_ahci_pciinit (grub_pci_device_t dev,
grub_pci_id_t pciid __attribute__ ((unused)))
{
grub_pci_address_t addr;
grub_uint32_t class;
grub_uint32_t bar;
unsigned i, nports;
volatile struct grub_ahci_hba *hba;
/* Read class. */
addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
class = grub_pci_read (addr);
/* Check if this class ID matches that of a PCI IDE Controller. */
if (class >> 8 != 0x010601)
return 0;
addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG5);
bar = grub_pci_read (addr);
if ((bar & (GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_TYPE_MASK
| GRUB_PCI_ADDR_MEM_PREFETCH))
!= (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32))
return 0;
hba = grub_pci_device_map_range (dev, bar & GRUB_PCI_ADDR_MEM_MASK,
sizeof (hba));
if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED))
{
grub_uint64_t endtime;
grub_dprintf ("ahci", "Requesting AHCI ownership\n");
hba->bios_handoff = (hba->bios_handoff & ~GRUB_AHCI_BIOS_HANDOFF_RWC)
| GRUB_AHCI_BIOS_HANDOFF_OS_OWNED;
grub_dprintf ("ahci", "Waiting for BIOS to give up ownership\n");
endtime = grub_get_time_ms () + 1000;
while ((hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED)
&& grub_get_time_ms () < endtime);
if (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED)
{
grub_dprintf ("ahci", "Forcibly taking ownership\n");
hba->bios_handoff = GRUB_AHCI_BIOS_HANDOFF_OS_OWNED;
hba->bios_handoff |= GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED;
}
else
grub_dprintf ("ahci", "AHCI ownership obtained\n");
}
else
grub_dprintf ("ahci", "AHCI is already in OS mode\n");
if (!(hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN))
grub_dprintf ("ahci", "AHCI is in compat mode. Switching\n");
else
grub_dprintf ("ahci", "AHCI is in AHCI mode.\n");
for (i = 0; i < 5; i++)
{
hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
grub_millisleep (1);
if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN)
break;
}
if (i == 5)
{
grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n");
return 0;
}
/*
{
grub_uint64_t endtime;
hba->global_control |= 1;
endtime = grub_get_time_ms () + 1000;
while (hba->global_control & 1)
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't reset AHCI\n");
return 0;
}
}
for (i = 0; i < 5; i++)
{
hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
grub_millisleep (1);
if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN)
break;
}
if (i == 5)
{
grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n");
return 0;
}
*/
nports = (hba->cap & GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1;
grub_dprintf ("ahci", "%d AHCI ports\n", nports);
for (i = 0; i < nports; i++)
{
struct grub_ahci_device *adev;
grub_uint32_t st;
if (!(hba->ports_implemented & (1 << i)))
continue;
grub_dprintf ("ahci", "status %d:%x\n", i, hba->ports[i].status);
/* FIXME: support hotplugging. */
st = hba->ports[i].status;
if ((st & 0xf) != 0x3 && (st & 0xf) != 0x1)
continue;
adev = grub_malloc (sizeof (*adev));
if (!adev)
return 1;
adev->hba = hba;
adev->port = i;
adev->present = 1;
adev->num = numdevs++;
if (init_port (adev))
{
grub_free (adev);
return 1;
}
grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices),
GRUB_AS_LIST (adev));
}
return 0;
}
static grub_err_t
grub_ahci_initialize (void)
{
grub_pci_iterate (grub_ahci_pciinit);
return grub_errno;
}
static grub_err_t
grub_ahci_fini_hw (int noreturn __attribute__ ((unused)))
{
struct grub_ahci_device *dev;
for (dev = grub_ahci_devices; dev; dev = dev->next)
{
grub_uint64_t endtime;
dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
endtime = grub_get_time_ms () + 1000;
while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't stop FR\n");
break;
}
dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
endtime = grub_get_time_ms () + 1000;
while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't stop CR\n");
break;
}
grub_dma_free (dev->command_list_chunk);
grub_dma_free (dev->command_table_chunk);
grub_dma_free (dev->rfis);
dev->command_list_chunk = NULL;
dev->command_table_chunk = NULL;
dev->rfis = NULL;
}
return GRUB_ERR_NONE;
}
static grub_err_t
grub_ahci_restore_hw (void)
{
struct grub_ahci_device **pdev;
for (pdev = &grub_ahci_devices; *pdev; pdev = &((*pdev)->next))
if (init_port (*pdev))
{
struct grub_ahci_device *odev;
odev = *pdev;
*pdev = (*pdev)->next;
grub_free (odev);
}
return GRUB_ERR_NONE;
}
static int
grub_ahci_iterate (int (*hook) (int id, int bus),
grub_disk_pull_t pull)
{
struct grub_ahci_device *dev;
if (pull != GRUB_DISK_PULL_NONE)
return 0;
FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
if (hook (GRUB_SCSI_SUBSYSTEM_AHCI, dev->num))
return 1;
return 0;
}
#if 0
static int
find_free_cmd_slot (struct grub_ahci_device *dev)
{
int i;
for (i = 0; i < 32; i++)
{
if (dev->hda->ports[dev->port].command_issue & (1 << i))
continue;
if (dev->hda->ports[dev->port].sata_active & (1 << i))
continue;
return i;
}
return -1;
}
#endif
enum
{
GRUB_AHCI_FIS_REG_H2D = 0x27
};
static const int register_map[11] = { 3 /* Features */,
12 /* Sectors */,
4 /* LBA low */,
5 /* LBA mid */,
6 /* LBA high */,
7 /* Device */,
2 /* CMD register */,
13 /* Sectors 48 */,
8 /* LBA48 low */,
9 /* LBA48 mid */,
10 /* LBA48 high */ };
static grub_err_t
grub_ahci_readwrite_real (struct grub_ahci_device *dev,
struct grub_disk_ata_pass_through_parms *parms,
int spinup)
{
struct grub_pci_dma_chunk *bufc;
grub_uint64_t endtime;
unsigned i;
grub_err_t err = GRUB_ERR_NONE;
grub_dprintf ("ahci", "AHCI tfd = %x\n",
dev->hba->ports[dev->port].task_file_data);
if ((dev->hba->ports[dev->port].task_file_data & 0x80))
{
dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
endtime = grub_get_time_ms () + 1000;
while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't stop CR\n");
break;
}
dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST;
endtime = grub_get_time_ms () + (spinup ? 10000 : 1000);
while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "couldn't start CR\n");
break;
}
}
dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error;
grub_dprintf("ahci", "grub_ahci_read (size=%llu, cmdsize = %llu)\n",
(unsigned long long) parms->size,
(unsigned long long) parms->cmdsize);
if (parms->cmdsize != 0 && parms->cmdsize != 12 && parms->cmdsize != 16)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "incorrect ATAPI command size");
if (parms->size > GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "too big data buffer");
bufc = grub_memalign_dma32 (1024, parms->size + (parms->size & 1));
dev->hba->ports[dev->port].command |= 8;
grub_dprintf ("ahci", "AHCI tfd = %x\n",
dev->hba->ports[dev->port].task_file_data);
/* FIXME: support port multipliers. */
dev->command_list[0].config
= (5 << GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT)
// | GRUB_AHCI_CONFIG_CLEAR_R_OK
| (0 << GRUB_AHCI_CONFIG_PMP_SHIFT)
| (1 << GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT)
| (parms->cmdsize ? GRUB_AHCI_CONFIG_ATAPI : 0)
| (parms->write ? GRUB_AHCI_CONFIG_WRITE : GRUB_AHCI_CONFIG_READ)
| (parms->taskfile.cmd == 8 ? (1 << 8) : 0);
dev->command_list[0].transfered = 0;
dev->command_list[0].command_table_base
= grub_dma_get_phys (dev->command_table_chunk);
grub_memset ((char *) dev->command_list[0].unused, 0,
sizeof (dev->command_list[0].unused));
grub_memset ((char *) &dev->command_table[0], 0,
sizeof (dev->command_table[0]));
if (parms->cmdsize)
grub_memcpy ((char *) dev->command_table[0].command, parms->cmd,
parms->cmdsize);
dev->command_table[0].cfis[0] = GRUB_AHCI_FIS_REG_H2D;
dev->command_table[0].cfis[1] = 0x80;
for (i = 0; i < sizeof (parms->taskfile.raw); i++)
dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i];
grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n",
dev->command_table[0].cfis[0], dev->command_table[0].cfis[1],
dev->command_table[0].cfis[2], dev->command_table[0].cfis[3],
dev->command_table[0].cfis[4], dev->command_table[0].cfis[5],
dev->command_table[0].cfis[6], dev->command_table[0].cfis[7]);
grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n",
dev->command_table[0].cfis[8], dev->command_table[0].cfis[9],
dev->command_table[0].cfis[10], dev->command_table[0].cfis[11],
dev->command_table[0].cfis[12], dev->command_table[0].cfis[13],
dev->command_table[0].cfis[14], dev->command_table[0].cfis[15]);
dev->command_table[0].prdt[0].data_base = grub_dma_get_phys (bufc);
dev->command_table[0].prdt[0].unused = 0;
dev->command_table[0].prdt[0].size = (parms->size + (parms->size & 1) - 1)
| GRUB_AHCI_INTERRUPT_ON_COMPLETE;
grub_dprintf ("ahci", "PRDT = %" PRIxGRUB_UINT64_T ", %x, %x (%"
PRIuGRUB_SIZE ")\n",
dev->command_table[0].prdt[0].data_base,
dev->command_table[0].prdt[0].unused,
dev->command_table[0].prdt[0].size,
(char *) &dev->command_table[0].prdt[0]
- (char *) &dev->command_table[0]);
if (parms->write)
grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size);
grub_dprintf ("ahci", "AHCI command schedulded\n");
grub_dprintf ("ahci", "AHCI tfd = %x\n",
dev->hba->ports[dev->port].task_file_data);
dev->hba->ports[dev->port].inten = 0xffffffff;//(1 << 2) | (1 << 5);
dev->hba->ports[dev->port].intstatus = 0xffffffff;//(1 << 2) | (1 << 5);
grub_dprintf ("ahci", "AHCI tfd = %x\n",
dev->hba->ports[dev->port].task_file_data);
dev->hba->ports[dev->port].command_issue |= 1;
grub_dprintf ("ahci", "AHCI sig = %x\n", dev->hba->ports[dev->port].sig);
grub_dprintf ("ahci", "AHCI tfd = %x\n",
dev->hba->ports[dev->port].task_file_data);
endtime = grub_get_time_ms () + (spinup ? 10000 : 1000);
while ((dev->hba->ports[dev->port].command_issue & 1))
if (grub_get_time_ms () > endtime)
{
grub_dprintf ("ahci", "AHCI status <%x %x %x>\n",
dev->hba->ports[dev->port].command_issue,
dev->hba->ports[dev->port].intstatus,
dev->hba->ports[dev->port].task_file_data);
err = grub_error (GRUB_ERR_IO, "AHCI transfer timeouted");
break;
}
grub_dprintf ("ahci", "AHCI command completed <%x %x %x %x %x, %x %x>\n",
dev->hba->ports[dev->port].command_issue,
dev->hba->ports[dev->port].intstatus,
dev->hba->ports[dev->port].task_file_data,
dev->command_list[0].transfered,
dev->hba->ports[dev->port].sata_error,
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x00],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x18]);
grub_dprintf ("ahci",
"last PIO FIS %08x %08x %08x %08x %08x %08x %08x %08x\n",
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x08],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x09],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0a],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0b],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0c],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0d],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0e],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0f]);
grub_dprintf ("ahci",
"last REG FIS %08x %08x %08x %08x %08x %08x %08x %08x\n",
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x10],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x11],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x12],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x13],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x14],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x15],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x16],
((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x17]);
if (!parms->write)
grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size);
grub_dma_free (bufc);
return err;
}
static grub_err_t
grub_ahci_readwrite (grub_ata_t disk,
struct grub_disk_ata_pass_through_parms *parms,
int spinup)
{
return grub_ahci_readwrite_real (disk->data, parms, spinup);
}
static grub_err_t
grub_ahci_open (int id, int devnum, struct grub_ata *ata)
{
struct grub_ahci_device *dev;
if (id != GRUB_SCSI_SUBSYSTEM_AHCI)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an AHCI device");
FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
if (dev->num == devnum)
break;
if (! dev)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device");
grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num);
ata->data = dev;
ata->dma = 1;
ata->present = &dev->present;
return GRUB_ERR_NONE;
}
static struct grub_ata_dev grub_ahci_dev =
{
.iterate = grub_ahci_iterate,
.open = grub_ahci_open,
.readwrite = grub_ahci_readwrite,
};
static struct grub_preboot *fini_hnd;
GRUB_MOD_INIT(ahci)
{
/* To prevent two drivers operating on the same disks. */
grub_disk_firmware_is_tainted = 1;
if (grub_disk_firmware_fini)
{
grub_disk_firmware_fini ();
grub_disk_firmware_fini = NULL;
}
/* AHCI initialization. */
grub_ahci_initialize ();
/* AHCI devices are handled by scsi.mod. */
grub_ata_dev_register (&grub_ahci_dev);
fini_hnd = grub_loader_register_preboot_hook (grub_ahci_fini_hw,
grub_ahci_restore_hw,
GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
}
GRUB_MOD_FINI(ahci)
{
grub_ahci_fini_hw (0);
grub_loader_unregister_preboot_hook (fini_hnd);
grub_ata_dev_unregister (&grub_ahci_dev);
}

View file

@ -1,7 +1,7 @@
/* ofdisk.c - Open Firmware disk access. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2004,2006,2007,2008,2009 Free Software Foundation, Inc.
* Copyright (C) 2004,2006,2007,2008,2009,2011 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -79,7 +79,8 @@ arcdisk_hash_add (char *devpath)
static int
grub_arcdisk_iterate (int (*hook_in) (const char *name))
grub_arcdisk_iterate (int (*hook_in) (const char *name),
grub_disk_pull_t pull)
{
auto int hook (const char *name, const struct grub_arc_component *comp);
int hook (const char *name, const struct grub_arc_component *comp)
@ -90,6 +91,9 @@ grub_arcdisk_iterate (int (*hook_in) (const char *name))
return 0;
return hook_in (name);
}
if (pull != GRUB_DISK_PULL_NONE)
return 0;
return grub_arc_iterate_devs (hook, 1);
}
@ -105,11 +109,6 @@ reopen (const char *name)
grub_dprintf ("arcdisk", "using already opened %s\n", name);
return GRUB_ERR_NONE;
}
if (GRUB_ARC_FIRMWARE_VECTOR->open (name, 0, &handle))
{
grub_dprintf ("arcdisk", "couldn't open %s\n", name);
return grub_error (GRUB_ERR_IO, "couldn't open %s", name);
}
if (last_path)
{
GRUB_ARC_FIRMWARE_VECTOR->close (last_handle);
@ -117,6 +116,11 @@ reopen (const char *name)
last_path = NULL;
last_handle = 0;
}
if (GRUB_ARC_FIRMWARE_VECTOR->open (name, 0, &handle))
{
grub_dprintf ("arcdisk", "couldn't open %s\n", name);
return grub_error (GRUB_ERR_IO, "couldn't open %s", name);
}
last_path = grub_strdup (name);
if (!last_path)
return grub_errno;

File diff suppressed because it is too large Load diff

View file

@ -1,108 +0,0 @@
/* ata_pthru.c - ATA pass through for ata.mod. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/ata.h>
#include <grub/disk.h>
#include <grub/dl.h>
#include <grub/mm.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* ATA pass through support, used by hdparm.mod. */
static grub_err_t
grub_ata_pass_through (grub_disk_t disk,
struct grub_disk_ata_pass_through_parms *parms)
{
if (disk->dev->id != GRUB_DISK_DEVICE_ATA_ID)
return grub_error (GRUB_ERR_BAD_DEVICE,
"device not accessed via ata.mod");
struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
if (! (parms->size == 0 || parms->size == GRUB_DISK_SECTOR_SIZE))
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"ATA multi-sector read and DATA OUT not implemented");
grub_dprintf ("ata", "ata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
parms->taskfile[GRUB_ATA_REG_CMD],
parms->taskfile[GRUB_ATA_REG_FEATURES],
parms->taskfile[GRUB_ATA_REG_SECTORS]);
grub_dprintf ("ata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%d\n",
parms->taskfile[GRUB_ATA_REG_LBAHIGH],
parms->taskfile[GRUB_ATA_REG_LBAMID],
parms->taskfile[GRUB_ATA_REG_LBALOW], parms->size);
/* Set registers. */
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4
| (parms->taskfile[GRUB_ATA_REG_DISK] & 0xf));
if (grub_ata_check_ready (dev))
return grub_errno;
int i;
for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
grub_ata_regset (dev, i, parms->taskfile[i]);
/* Start command. */
grub_ata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile[GRUB_ATA_REG_CMD]);
/* Wait for !BSY. */
if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
return grub_errno;
/* Check status. */
grub_int8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
grub_dprintf ("ata", "status=0x%x\n", sts);
/* Transfer data. */
if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_DRQ)
{
if (parms->size != GRUB_DISK_SECTOR_SIZE)
return grub_error (GRUB_ERR_READ_ERROR, "DRQ unexpected");
grub_ata_pio_read (dev, parms->buffer, GRUB_DISK_SECTOR_SIZE);
}
/* Return registers. */
for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
parms->taskfile[i] = grub_ata_regget (dev, i);
grub_dprintf ("ata", "status=0x%x, error=0x%x, sectors=0x%x\n",
parms->taskfile[GRUB_ATA_REG_STATUS],
parms->taskfile[GRUB_ATA_REG_ERROR],
parms->taskfile[GRUB_ATA_REG_SECTORS]);
if (parms->taskfile[GRUB_ATA_REG_STATUS]
& (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
return grub_error (GRUB_ERR_READ_ERROR, "ATA passthrough failed");
return GRUB_ERR_NONE;
}
GRUB_MOD_INIT(ata_pthru)
{
/* Register ATA pass through function. */
grub_disk_ata_pass_through = grub_ata_pass_through;
}
GRUB_MOD_FINI(ata_pthru)
{
if (grub_disk_ata_pass_through == grub_ata_pass_through)
grub_disk_ata_pass_through = NULL;
}

883
grub-core/disk/cryptodisk.c Normal file
View file

@ -0,0 +1,883 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003,2007,2010,2011 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/cryptodisk.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/dl.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#ifdef GRUB_UTIL
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <grub/emu/hostdisk.h>
#include <unistd.h>
#include <string.h>
#endif
GRUB_MOD_LICENSE ("GPLv3+");
grub_cryptodisk_dev_t grub_cryptodisk_list;
static const struct grub_arg_option options[] =
{
{"uuid", 'u', 0, N_("Mount by UUID."), 0, 0},
{"all", 'a', 0, N_("Mount all."), 0, 0},
{"boot", 'b', 0, N_("Mount all volumes marked as boot."), 0, 0},
{0, 0, 0, 0, 0, 0}
};
/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */
#define GF_POLYNOM 0x87
static inline int GF_PER_SECTOR (const struct grub_cryptodisk *dev)
{
return 1U << (dev->log_sector_size - GRUB_CRYPTODISK_GF_LOG_BYTES);
}
static grub_cryptodisk_t cryptodisk_list = NULL;
static grub_uint8_t n = 0;
static void
gf_mul_x (grub_uint8_t *g)
{
int over = 0, over2 = 0;
unsigned j;
for (j = 0; j < GRUB_CRYPTODISK_GF_BYTES; j++)
{
over2 = !!(g[j] & 0x80);
g[j] <<= 1;
g[j] |= over;
over = over2;
}
if (over)
g[0] ^= GF_POLYNOM;
}
static void
gf_mul_x_be (grub_uint8_t *g)
{
int over = 0, over2 = 0;
int j;
for (j = (int) GRUB_CRYPTODISK_GF_BYTES - 1; j >= 0; j--)
{
over2 = !!(g[j] & 0x80);
g[j] <<= 1;
g[j] |= over;
over = over2;
}
if (over)
g[GRUB_CRYPTODISK_GF_BYTES - 1] ^= GF_POLYNOM;
}
static void
gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b)
{
unsigned i;
grub_uint8_t t[GRUB_CRYPTODISK_GF_BYTES];
grub_memset (o, 0, GRUB_CRYPTODISK_GF_BYTES);
grub_memcpy (t, b, GRUB_CRYPTODISK_GF_BYTES);
for (i = 0; i < GRUB_CRYPTODISK_GF_SIZE; i++)
{
if (((a[GRUB_CRYPTODISK_GF_BYTES - i / 8 - 1] >> (i % 8))) & 1)
grub_crypto_xor (o, o, t, GRUB_CRYPTODISK_GF_BYTES);
gf_mul_x_be (t);
}
}
static gcry_err_code_t
grub_crypto_pcbc_decrypt (grub_crypto_cipher_handle_t cipher,
void *out, void *in, grub_size_t size,
void *iv)
{
grub_uint8_t *inptr, *outptr, *end;
grub_uint8_t ivt[cipher->cipher->blocksize];
if (!cipher->cipher->decrypt)
return GPG_ERR_NOT_SUPPORTED;
if (size % cipher->cipher->blocksize != 0)
return GPG_ERR_INV_ARG;
end = (grub_uint8_t *) in + size;
for (inptr = in, outptr = out; inptr < end;
inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize)
{
grub_memcpy (ivt, inptr, cipher->cipher->blocksize);
cipher->cipher->decrypt (cipher->ctx, outptr, inptr);
grub_crypto_xor (outptr, outptr, iv, cipher->cipher->blocksize);
grub_crypto_xor (iv, ivt, outptr, cipher->cipher->blocksize);
}
return GPG_ERR_NO_ERROR;
}
struct lrw_sector
{
grub_uint8_t low[GRUB_CRYPTODISK_GF_BYTES];
grub_uint8_t high[GRUB_CRYPTODISK_GF_BYTES];
grub_uint8_t low_byte, low_byte_c;
};
static void
generate_lrw_sector (struct lrw_sector *sec,
const struct grub_cryptodisk *dev,
const grub_uint8_t *iv)
{
grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES];
grub_uint16_t c;
int j;
grub_memcpy (idx, iv, GRUB_CRYPTODISK_GF_BYTES);
sec->low_byte = (idx[GRUB_CRYPTODISK_GF_BYTES - 1]
& (GF_PER_SECTOR (dev) - 1));
sec->low_byte_c = (((GF_PER_SECTOR (dev) - 1) & ~sec->low_byte) + 1);
idx[GRUB_CRYPTODISK_GF_BYTES - 1] &= ~(GF_PER_SECTOR (dev) - 1);
gf_mul_be (sec->low, dev->lrw_key, idx);
if (!sec->low_byte)
return;
c = idx[GRUB_CRYPTODISK_GF_BYTES - 1] + GF_PER_SECTOR (dev);
if (c & 0x100)
{
for (j = GRUB_CRYPTODISK_GF_BYTES - 2; j >= 0; j--)
{
idx[j]++;
if (idx[j] != 0)
break;
}
}
idx[GRUB_CRYPTODISK_GF_BYTES - 1] = c;
gf_mul_be (sec->high, dev->lrw_key, idx);
}
static void __attribute__ ((unused))
lrw_xor (const struct lrw_sector *sec,
const struct grub_cryptodisk *dev,
grub_uint8_t *b)
{
unsigned i;
for (i = 0; i < sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES;
i += GRUB_CRYPTODISK_GF_BYTES)
grub_crypto_xor (b + i, b + i, sec->low, GRUB_CRYPTODISK_GF_BYTES);
grub_crypto_xor (b, b, dev->lrw_precalc + GRUB_CRYPTODISK_GF_BYTES * sec->low_byte,
sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES);
if (!sec->low_byte)
return;
for (i = sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES;
i < (1U << dev->log_sector_size); i += GRUB_CRYPTODISK_GF_BYTES)
grub_crypto_xor (b + i, b + i, sec->high, GRUB_CRYPTODISK_GF_BYTES);
grub_crypto_xor (b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES,
b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES,
dev->lrw_precalc, sec->low_byte * GRUB_CRYPTODISK_GF_BYTES);
}
gcry_err_code_t
grub_cryptodisk_decrypt (struct grub_cryptodisk *dev,
grub_uint8_t * data, grub_size_t len,
grub_disk_addr_t sector)
{
grub_size_t i;
gcry_err_code_t err;
/* The only mode without IV. */
if (dev->mode == GRUB_CRYPTODISK_MODE_ECB && !dev->rekey)
return grub_crypto_ecb_decrypt (dev->cipher, data, data, len);
for (i = 0; i < len; i += (1U << dev->log_sector_size))
{
grub_size_t sz = ((dev->cipher->cipher->blocksize
+ sizeof (grub_uint32_t) - 1)
/ sizeof (grub_uint32_t));
grub_uint32_t iv[sz];
if (dev->rekey)
{
grub_uint64_t zone = sector >> dev->rekey_shift;
if (zone != dev->last_rekey)
{
err = dev->rekey (dev, zone);
if (err)
return err;
dev->last_rekey = zone;
}
}
grub_memset (iv, 0, sz * sizeof (iv[0]));
switch (dev->mode_iv)
{
case GRUB_CRYPTODISK_MODE_IV_NULL:
break;
case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH:
{
grub_uint64_t tmp;
grub_uint64_t ctx[(dev->iv_hash->contextsize + 7) / 8];
grub_memset (ctx, 0, sizeof (ctx));
tmp = grub_cpu_to_le64 (sector << dev->log_sector_size);
dev->iv_hash->init (ctx);
dev->iv_hash->write (ctx, dev->iv_prefix, dev->iv_prefix_len);
dev->iv_hash->write (ctx, &tmp, sizeof (tmp));
dev->iv_hash->final (ctx);
grub_memcpy (iv, dev->iv_hash->read (ctx), sizeof (iv));
}
break;
case GRUB_CRYPTODISK_MODE_IV_PLAIN64:
iv[1] = grub_cpu_to_le32 (sector >> 32);
case GRUB_CRYPTODISK_MODE_IV_PLAIN:
iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF);
break;
case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64:
iv[1] = grub_cpu_to_le32 (sector >> (32 - dev->log_sector_size));
iv[0] = grub_cpu_to_le32 ((sector << dev->log_sector_size)
& 0xFFFFFFFF);
break;
case GRUB_CRYPTODISK_MODE_IV_BENBI:
{
grub_uint64_t num = (sector << dev->benbi_log) + 1;
iv[sz - 2] = grub_cpu_to_be32 (num >> 32);
iv[sz - 1] = grub_cpu_to_be32 (num & 0xFFFFFFFF);
}
break;
case GRUB_CRYPTODISK_MODE_IV_ESSIV:
iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF);
err = grub_crypto_ecb_encrypt (dev->essiv_cipher, iv, iv,
dev->cipher->cipher->blocksize);
if (err)
return err;
}
switch (dev->mode)
{
case GRUB_CRYPTODISK_MODE_CBC:
err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i,
(1U << dev->log_sector_size), iv);
if (err)
return err;
break;
case GRUB_CRYPTODISK_MODE_PCBC:
err = grub_crypto_pcbc_decrypt (dev->cipher, data + i, data + i,
(1U << dev->log_sector_size), iv);
if (err)
return err;
break;
case GRUB_CRYPTODISK_MODE_XTS:
{
unsigned j;
err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv,
dev->cipher->cipher->blocksize);
if (err)
return err;
for (j = 0; j < (1U << dev->log_sector_size);
j += dev->cipher->cipher->blocksize)
{
grub_crypto_xor (data + i + j, data + i + j, iv,
dev->cipher->cipher->blocksize);
err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j,
data + i + j,
dev->cipher->cipher->blocksize);
if (err)
return err;
grub_crypto_xor (data + i + j, data + i + j, iv,
dev->cipher->cipher->blocksize);
gf_mul_x ((grub_uint8_t *) iv);
}
}
break;
case GRUB_CRYPTODISK_MODE_LRW:
{
struct lrw_sector sec;
generate_lrw_sector (&sec, dev, (grub_uint8_t *) iv);
lrw_xor (&sec, dev, data + i);
err = grub_crypto_ecb_decrypt (dev->cipher, data + i,
data + i,
(1U << dev->log_sector_size));
if (err)
return err;
lrw_xor (&sec, dev, data + i);
}
break;
case GRUB_CRYPTODISK_MODE_ECB:
grub_crypto_ecb_decrypt (dev->cipher, data + i, data + i,
(1U << dev->log_sector_size));
break;
default:
return GPG_ERR_NOT_IMPLEMENTED;
}
sector++;
}
return GPG_ERR_NO_ERROR;
}
gcry_err_code_t
grub_cryptodisk_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, grub_size_t keysize)
{
gcry_err_code_t err;
int real_keysize;
real_keysize = keysize;
if (dev->mode == GRUB_CRYPTODISK_MODE_XTS)
real_keysize /= 2;
if (dev->mode == GRUB_CRYPTODISK_MODE_LRW)
real_keysize -= dev->cipher->cipher->blocksize;
/* Set the PBKDF2 output as the cipher key. */
err = grub_crypto_cipher_set_key (dev->cipher, key, real_keysize);
if (err)
return err;
/* Configure ESSIV if necessary. */
if (dev->mode_iv == GRUB_CRYPTODISK_MODE_IV_ESSIV)
{
grub_size_t essiv_keysize = dev->essiv_hash->mdlen;
grub_uint8_t hashed_key[essiv_keysize];
grub_crypto_hash (dev->essiv_hash, hashed_key, key, keysize);
err = grub_crypto_cipher_set_key (dev->essiv_cipher,
hashed_key, essiv_keysize);
if (err)
return err;
}
if (dev->mode == GRUB_CRYPTODISK_MODE_XTS)
{
err = grub_crypto_cipher_set_key (dev->secondary_cipher,
key + real_keysize,
keysize / 2);
if (err)
return err;
}
if (dev->mode == GRUB_CRYPTODISK_MODE_LRW)
{
unsigned i;
grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES];
grub_free (dev->lrw_precalc);
grub_memcpy (dev->lrw_key, key + real_keysize,
dev->cipher->cipher->blocksize);
dev->lrw_precalc = grub_malloc ((1U << dev->log_sector_size));
if (!dev->lrw_precalc)
return GPG_ERR_OUT_OF_MEMORY;
grub_memset (idx, 0, GRUB_CRYPTODISK_GF_BYTES);
for (i = 0; i < (1U << dev->log_sector_size);
i += GRUB_CRYPTODISK_GF_BYTES)
{
idx[GRUB_CRYPTODISK_GF_BYTES - 1] = i / GRUB_CRYPTODISK_GF_BYTES;
gf_mul_be (dev->lrw_precalc + i, idx, dev->lrw_key);
}
}
return GPG_ERR_NO_ERROR;
}
static int
grub_cryptodisk_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
grub_cryptodisk_t i;
if (pull != GRUB_DISK_PULL_NONE)
return 0;
for (i = cryptodisk_list; i != NULL; i = i->next)
{
char buf[30];
grub_snprintf (buf, sizeof (buf), "crypto%lu", i->id);
if (hook (buf))
return 1;
}
return GRUB_ERR_NONE;
}
static grub_err_t
grub_cryptodisk_open (const char *name, grub_disk_t disk)
{
grub_cryptodisk_t dev;
if (grub_memcmp (name, "crypto", sizeof ("crypto") - 1) != 0)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device");
if (grub_memcmp (name, "cryptouuid/", sizeof ("cryptouuid/") - 1) == 0)
{
for (dev = cryptodisk_list; dev != NULL; dev = dev->next)
if (grub_strcasecmp (name + sizeof ("cryptouuid/") - 1, dev->uuid) == 0)
break;
}
else
{
unsigned long id = grub_strtoul (name + sizeof ("crypto") - 1, 0, 0);
if (grub_errno)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device");
/* Search for requested device in the list of CRYPTODISK devices. */
for (dev = cryptodisk_list; dev != NULL; dev = dev->next)
if (dev->id == id)
break;
}
if (!dev)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device");
disk->log_sector_size = dev->log_sector_size;
#ifdef GRUB_UTIL
if (dev->cheat)
{
if (dev->cheat_fd == -1)
dev->cheat_fd = open (dev->cheat, O_RDONLY);
if (dev->cheat_fd == -1)
return grub_error (GRUB_ERR_IO, "couldn't open %s: %s",
dev->cheat, strerror (errno));
}
#endif
if (!dev->source_disk)
{
grub_dprintf ("cryptodisk", "Opening device %s\n", name);
/* Try to open the source disk and populate the requested disk. */
dev->source_disk = grub_disk_open (dev->source);
if (!dev->source_disk)
return grub_errno;
}
disk->data = dev;
disk->total_sectors = dev->total_length;
disk->id = dev->id;
dev->ref++;
return GRUB_ERR_NONE;
}
static void
grub_cryptodisk_close (grub_disk_t disk)
{
grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
grub_dprintf ("cryptodisk", "Closing disk\n");
dev->ref--;
if (dev->ref != 0)
return;
#ifdef GRUB_UTIL
if (dev->cheat)
{
close (dev->cheat_fd);
dev->cheat_fd = -1;
}
#endif
grub_disk_close (dev->source_disk);
dev->source_disk = NULL;
}
static grub_err_t
grub_cryptodisk_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf)
{
grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
grub_err_t err;
gcry_err_code_t gcry_err;
#ifdef GRUB_UTIL
if (dev->cheat)
{
err = grub_util_fd_seek (dev->cheat_fd, dev->cheat,
sector << disk->log_sector_size);
if (err)
return err;
if (grub_util_fd_read (dev->cheat_fd, buf, size << disk->log_sector_size)
!= (ssize_t) (size << disk->log_sector_size))
return grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'",
dev->cheat);
return GRUB_ERR_NONE;
}
#endif
grub_dprintf ("cryptodisk",
"Reading %" PRIuGRUB_SIZE " sectors from sector 0x%"
PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT64_T "\n",
size, sector, dev->offset);
err = grub_disk_read (dev->source_disk,
(sector << (disk->log_sector_size
- GRUB_DISK_SECTOR_BITS)) + dev->offset, 0,
size << disk->log_sector_size, buf);
if (err)
{
grub_dprintf ("cryptodisk", "grub_disk_read failed with error %d\n", err);
return err;
}
gcry_err = grub_cryptodisk_decrypt (dev, (grub_uint8_t *) buf,
size << disk->log_sector_size,
sector);
return grub_crypto_gcry_error (gcry_err);
}
static grub_err_t
grub_cryptodisk_write (grub_disk_t disk __attribute ((unused)),
grub_disk_addr_t sector __attribute ((unused)),
grub_size_t size __attribute ((unused)),
const char *buf __attribute ((unused)))
{
return GRUB_ERR_NOT_IMPLEMENTED_YET;
}
#ifdef GRUB_UTIL
static grub_disk_memberlist_t
grub_cryptodisk_memberlist (grub_disk_t disk)
{
grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
grub_disk_memberlist_t list = NULL;
list = grub_malloc (sizeof (*list));
if (list)
{
list->disk = dev->source_disk;
list->next = NULL;
}
return list;
}
#endif
static void
cryptodisk_cleanup (void)
{
grub_cryptodisk_t dev = cryptodisk_list;
grub_cryptodisk_t tmp;
while (dev != NULL)
{
grub_free (dev->source);
grub_free (dev->cipher);
grub_free (dev->secondary_cipher);
grub_free (dev->essiv_cipher);
tmp = dev->next;
grub_free (dev);
dev = tmp;
}
}
grub_err_t
grub_cryptodisk_insert (grub_cryptodisk_t newdev, const char *name,
grub_disk_t source)
{
newdev->source = grub_strdup (name);
if (!newdev->source)
{
grub_free (newdev);
return grub_errno;
}
newdev->id = n++;
newdev->source_id = source->id;
newdev->source_dev_id = source->dev->id;
newdev->next = cryptodisk_list;
cryptodisk_list = newdev;
return GRUB_ERR_NONE;
}
grub_cryptodisk_t
grub_cryptodisk_get_by_uuid (const char *uuid)
{
grub_cryptodisk_t dev;
for (dev = cryptodisk_list; dev != NULL; dev = dev->next)
if (grub_strcasecmp (dev->uuid, uuid) == 0)
return dev;
return NULL;
}
grub_cryptodisk_t
grub_cryptodisk_get_by_source_disk (grub_disk_t disk)
{
grub_cryptodisk_t dev;
for (dev = cryptodisk_list; dev != NULL; dev = dev->next)
if (dev->source_id == disk->id && dev->source_dev_id == disk->dev->id)
return dev;
return NULL;
}
#ifdef GRUB_UTIL
grub_err_t
grub_cryptodisk_cheat_insert (grub_cryptodisk_t newdev, const char *name,
grub_disk_t source, const char *cheat)
{
newdev->cheat = grub_strdup (cheat);
newdev->source = grub_strdup (name);
if (!newdev->source || !newdev->cheat)
{
grub_free (newdev->source);
grub_free (newdev->cheat);
return grub_errno;
}
newdev->cheat_fd = -1;
newdev->source_id = source->id;
newdev->source_dev_id = source->dev->id;
newdev->id = n++;
newdev->next = cryptodisk_list;
cryptodisk_list = newdev;
return GRUB_ERR_NONE;
}
void
grub_util_cryptodisk_print_abstraction (grub_disk_t disk)
{
grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
grub_printf ("cryptodisk %s ", dev->modname);
if (dev->cipher)
grub_printf ("%s ", dev->cipher->cipher->modname);
if (dev->secondary_cipher)
grub_printf ("%s ", dev->secondary_cipher->cipher->modname);
if (dev->essiv_cipher)
grub_printf ("%s ", dev->essiv_cipher->cipher->modname);
if (dev->hash)
grub_printf ("%s ", dev->hash->modname);
if (dev->essiv_hash)
grub_printf ("%s ", dev->essiv_hash->modname);
if (dev->iv_hash)
grub_printf ("%s ", dev->iv_hash->modname);
}
void
grub_util_cryptodisk_print_uuid (grub_disk_t disk)
{
grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
grub_printf ("%s ", dev->uuid);
}
#endif
static int check_boot, have_it;
static char *search_uuid;
static void
cryptodisk_close (grub_cryptodisk_t dev)
{
grub_crypto_cipher_close (dev->cipher);
grub_crypto_cipher_close (dev->secondary_cipher);
grub_crypto_cipher_close (dev->essiv_cipher);
grub_free (dev);
}
static grub_err_t
grub_cryptodisk_scan_device_real (const char *name, grub_disk_t source)
{
grub_err_t err;
grub_cryptodisk_t dev;
grub_cryptodisk_dev_t cr;
dev = grub_cryptodisk_get_by_source_disk (source);
if (dev)
return GRUB_ERR_NONE;
FOR_CRYPTODISK_DEVS (cr)
{
dev = cr->scan (source, search_uuid, check_boot);
if (grub_errno)
return grub_errno;
if (!dev)
continue;
err = cr->recover_key (source, dev);
if (err)
{
cryptodisk_close (dev);
return err;
}
grub_cryptodisk_insert (dev, name, source);
have_it = 1;
return GRUB_ERR_NONE;
}
return GRUB_ERR_NONE;
}
#ifdef GRUB_UTIL
#include <grub/util/misc.h>
grub_err_t
grub_cryptodisk_cheat_mount (const char *sourcedev, const char *cheat)
{
grub_err_t err;
grub_cryptodisk_t dev;
grub_cryptodisk_dev_t cr;
grub_disk_t source;
/* Try to open disk. */
source = grub_disk_open (sourcedev);
if (!source)
return grub_errno;
dev = grub_cryptodisk_get_by_source_disk (source);
if (dev)
{
grub_disk_close (source);
return GRUB_ERR_NONE;
}
FOR_CRYPTODISK_DEVS (cr)
{
dev = cr->scan (source, search_uuid, check_boot);
if (grub_errno)
return grub_errno;
if (!dev)
continue;
grub_util_info ("cheatmounted %s (%s) at %s", sourcedev, dev->modname,
cheat);
err = grub_cryptodisk_cheat_insert (dev, sourcedev, source, cheat);
grub_disk_close (source);
if (err)
grub_free (dev);
return GRUB_ERR_NONE;
}
grub_disk_close (source);
return GRUB_ERR_NONE;
}
#endif
static int
grub_cryptodisk_scan_device (const char *name)
{
grub_err_t err;
grub_disk_t source;
/* Try to open disk. */
source = grub_disk_open (name);
if (!source)
return grub_errno;
err = grub_cryptodisk_scan_device_real (name, source);
grub_disk_close (source);
if (err)
grub_print_error ();
return have_it && search_uuid ? 1 : 0;
}
static grub_err_t
grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
if (argc < 1 && !state[1].set && !state[2].set)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
have_it = 0;
if (state[0].set)
{
grub_cryptodisk_t dev;
dev = grub_cryptodisk_get_by_uuid (args[0]);
if (dev)
{
grub_dprintf ("cryptodisk",
"already mounted as crypto%lu\n", dev->id);
return GRUB_ERR_NONE;
}
check_boot = state[2].set;
search_uuid = args[0];
grub_device_iterate (&grub_cryptodisk_scan_device);
search_uuid = NULL;
if (!have_it)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such cryptodisk found");
return GRUB_ERR_NONE;
}
else if (state[1].set || (argc == 0 && state[2].set))
{
search_uuid = NULL;
check_boot = state[2].set;
grub_device_iterate (&grub_cryptodisk_scan_device);
search_uuid = NULL;
return GRUB_ERR_NONE;
}
else
{
grub_err_t err;
grub_disk_t disk;
grub_cryptodisk_t dev;
search_uuid = NULL;
check_boot = state[2].set;
disk = grub_disk_open (args[0]);
if (!disk)
return grub_errno;
dev = grub_cryptodisk_get_by_source_disk (disk);
if (dev)
{
grub_dprintf ("cryptodisk", "already mounted as crypto%lu\n", dev->id);
grub_disk_close (disk);
return GRUB_ERR_NONE;
}
err = grub_cryptodisk_scan_device_real (args[0], disk);
grub_disk_close (disk);
return err;
}
}
static struct grub_disk_dev grub_cryptodisk_dev = {
.name = "cryptodisk",
.id = GRUB_DISK_DEVICE_CRYPTODISK_ID,
.iterate = grub_cryptodisk_iterate,
.open = grub_cryptodisk_open,
.close = grub_cryptodisk_close,
.read = grub_cryptodisk_read,
.write = grub_cryptodisk_write,
#ifdef GRUB_UTIL
.memberlist = grub_cryptodisk_memberlist,
#endif
.next = 0
};
static grub_extcmd_t cmd;
GRUB_MOD_INIT (cryptodisk)
{
grub_disk_dev_register (&grub_cryptodisk_dev);
cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0,
N_("SOURCE|-u UUID|-a|-b"),
N_("Mount a crypto device."), options);
}
GRUB_MOD_FINI (cryptodisk)
{
grub_disk_dev_unregister (&grub_cryptodisk_dev);
cryptodisk_cleanup ();
}

View file

@ -33,12 +33,10 @@ struct grub_efidisk_data
grub_efi_device_path_t *device_path;
grub_efi_device_path_t *last_device_path;
grub_efi_block_io_t *block_io;
grub_efi_disk_io_t *disk_io;
struct grub_efidisk_data *next;
};
/* GUIDs. */
static grub_efi_guid_t disk_io_guid = GRUB_EFI_DISK_IO_GUID;
/* GUID. */
static grub_efi_guid_t block_io_guid = GRUB_EFI_BLOCK_IO_GUID;
static struct grub_efidisk_data *fd_devices;
@ -86,54 +84,6 @@ find_last_device_path (const grub_efi_device_path_t *dp)
return p;
}
/* Compare device paths. */
static int
compare_device_paths (const grub_efi_device_path_t *dp1,
const grub_efi_device_path_t *dp2)
{
if (! dp1 || ! dp2)
/* Return non-zero. */
return 1;
while (1)
{
grub_efi_uint8_t type1, type2;
grub_efi_uint8_t subtype1, subtype2;
grub_efi_uint16_t len1, len2;
int ret;
type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1);
type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2);
if (type1 != type2)
return (int) type2 - (int) type1;
subtype1 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1);
subtype2 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2);
if (subtype1 != subtype2)
return (int) subtype1 - (int) subtype2;
len1 = GRUB_EFI_DEVICE_PATH_LENGTH (dp1);
len2 = GRUB_EFI_DEVICE_PATH_LENGTH (dp2);
if (len1 != len2)
return (int) len1 - (int) len2;
ret = grub_memcmp (dp1, dp2, len1);
if (ret != 0)
return ret;
if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1))
break;
dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1);
dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2);
}
return 0;
}
static struct grub_efidisk_data *
make_devices (void)
{
@ -143,7 +93,7 @@ make_devices (void)
struct grub_efidisk_data *devices = 0;
/* Find handles which support the disk io interface. */
handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &disk_io_guid,
handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &block_io_guid,
0, &num_handles);
if (! handles)
return 0;
@ -155,7 +105,6 @@ make_devices (void)
grub_efi_device_path_t *ldp;
struct grub_efidisk_data *d;
grub_efi_block_io_t *bio;
grub_efi_disk_io_t *dio;
dp = grub_efi_get_device_path (*handle);
if (! dp)
@ -168,9 +117,7 @@ make_devices (void)
bio = grub_efi_open_protocol (*handle, &block_io_guid,
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
dio = grub_efi_open_protocol (*handle, &disk_io_guid,
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (! bio || ! dio)
if (! bio)
/* This should not happen... Why? */
continue;
@ -186,7 +133,6 @@ make_devices (void)
d->device_path = dp;
d->last_device_path = ldp;
d->block_io = bio;
d->disk_io = dio;
d->next = devices;
devices = d;
}
@ -220,7 +166,7 @@ find_parent_device (struct grub_efidisk_data *devices,
if (parent == d)
continue;
if (compare_device_paths (parent->device_path, dp) == 0)
if (grub_efi_compare_device_paths (parent->device_path, dp) == 0)
{
/* Found. */
if (! parent->last_device_path)
@ -255,7 +201,7 @@ iterate_child_devices (struct grub_efidisk_data *devices,
ldp->length[0] = sizeof (*ldp);
ldp->length[1] = 0;
if (compare_device_paths (dp, d->device_path) == 0)
if (grub_efi_compare_device_paths (dp, d->device_path) == 0)
if (hook (p))
{
grub_free (dp);
@ -279,11 +225,11 @@ add_device (struct grub_efidisk_data **devices, struct grub_efidisk_data *d)
{
int ret;
ret = compare_device_paths (find_last_device_path ((*p)->device_path),
find_last_device_path (d->device_path));
ret = grub_efi_compare_device_paths (find_last_device_path ((*p)->device_path),
find_last_device_path (d->device_path));
if (ret == 0)
ret = compare_device_paths ((*p)->device_path,
d->device_path);
ret = grub_efi_compare_device_paths ((*p)->device_path,
d->device_path);
if (ret == 0)
return;
else if (ret > 0)
@ -432,34 +378,43 @@ enumerate_disks (void)
}
static int
grub_efidisk_iterate (int (*hook) (const char *name))
grub_efidisk_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
struct grub_efidisk_data *d;
char buf[16];
int count;
for (d = fd_devices, count = 0; d; d = d->next, count++)
switch (pull)
{
grub_snprintf (buf, sizeof (buf), "fd%d", count);
grub_dprintf ("efidisk", "iterating %s\n", buf);
if (hook (buf))
return 1;
}
case GRUB_DISK_PULL_NONE:
for (d = hd_devices, count = 0; d; d = d->next, count++)
{
grub_snprintf (buf, sizeof (buf), "hd%d", count);
grub_dprintf ("efidisk", "iterating %s\n", buf);
if (hook (buf))
return 1;
}
break;
case GRUB_DISK_PULL_REMOVABLE:
for (d = fd_devices, count = 0; d; d = d->next, count++)
{
grub_snprintf (buf, sizeof (buf), "fd%d", count);
grub_dprintf ("efidisk", "iterating %s\n", buf);
if (hook (buf))
return 1;
}
for (d = hd_devices, count = 0; d; d = d->next, count++)
{
grub_snprintf (buf, sizeof (buf), "hd%d", count);
grub_dprintf ("efidisk", "iterating %s\n", buf);
if (hook (buf))
return 1;
}
for (d = cd_devices, count = 0; d; d = d->next, count++)
{
grub_snprintf (buf, sizeof (buf), "cd%d", count);
grub_dprintf ("efidisk", "iterating %s\n", buf);
if (hook (buf))
return 1;
for (d = cd_devices, count = 0; d; d = d->next, count++)
{
grub_snprintf (buf, sizeof (buf), "cd%d", count);
grub_dprintf ("efidisk", "iterating %s\n", buf);
if (hook (buf))
return 1;
}
break;
default:
return 0;
}
return 0;
@ -536,8 +491,13 @@ grub_efidisk_open (const char *name, struct grub_disk *disk)
and total sectors should be replaced with total blocks. */
grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n",
m, (unsigned long long) m->last_block, m->block_size);
disk->total_sectors = (m->last_block
* (m->block_size >> GRUB_DISK_SECTOR_BITS));
disk->total_sectors = m->last_block + 1;
if (m->block_size & (m->block_size - 1) || !m->block_size)
return grub_error (GRUB_ERR_IO, "invalid sector size %d",
m->block_size);
for (disk->log_sector_size = 0;
(1U << disk->log_sector_size) < m->block_size;
disk->log_sector_size++);
disk->data = d;
grub_dprintf ("efidisk", "opening %s succeeded\n", name);
@ -558,22 +518,20 @@ grub_efidisk_read (struct grub_disk *disk, grub_disk_addr_t sector,
{
/* For now, use the disk io interface rather than the block io's. */
struct grub_efidisk_data *d;
grub_efi_disk_io_t *dio;
grub_efi_block_io_t *bio;
grub_efi_status_t status;
d = disk->data;
dio = d->disk_io;
bio = d->block_io;
grub_dprintf ("efidisk",
"reading 0x%lx sectors at the sector 0x%llx from %s\n",
(unsigned long) size, (unsigned long long) sector, disk->name);
status = efi_call_5 (dio->read, dio, bio->media->media_id,
(grub_efi_uint64_t) sector << GRUB_DISK_SECTOR_BITS,
(grub_efi_uintn_t) size << GRUB_DISK_SECTOR_BITS,
buf);
status = efi_call_5 (bio->read_blocks, bio, bio->media->media_id,
(grub_efi_uint64_t) sector,
(grub_efi_uintn_t) size << disk->log_sector_size,
buf);
if (status != GRUB_EFI_SUCCESS)
return grub_error (GRUB_ERR_READ_ERROR, "efidisk read error");
@ -586,21 +544,19 @@ grub_efidisk_write (struct grub_disk *disk, grub_disk_addr_t sector,
{
/* For now, use the disk io interface rather than the block io's. */
struct grub_efidisk_data *d;
grub_efi_disk_io_t *dio;
grub_efi_block_io_t *bio;
grub_efi_status_t status;
d = disk->data;
dio = d->disk_io;
bio = d->block_io;
grub_dprintf ("efidisk",
"writing 0x%lx sectors at the sector 0x%llx to %s\n",
(unsigned long) size, (unsigned long long) sector, disk->name);
status = efi_call_5 (dio->write, dio, bio->media->media_id,
(grub_efi_uint64_t) sector << GRUB_DISK_SECTOR_BITS,
(grub_efi_uintn_t) size << GRUB_DISK_SECTOR_BITS,
status = efi_call_5 (bio->write_blocks, bio, bio->media->media_id,
(grub_efi_uint64_t) sector,
(grub_efi_uintn_t) size << disk->log_sector_size,
(void *) buf);
if (status != GRUB_EFI_SUCCESS)
return grub_error (GRUB_ERR_WRITE_ERROR, "efidisk write error");
@ -711,7 +667,35 @@ grub_efidisk_get_device_handle (grub_disk_t disk)
char *
grub_efidisk_get_device_name (grub_efi_handle_t *handle)
{
grub_efi_device_path_t *dp, *ldp;
grub_efi_device_path_t *dp, *ldp, *sdp;
/* This is a hard disk partition. */
grub_disk_t parent = 0;
auto int find_parent_disk (const char *name);
/* Find the disk which is the parent of a given hard disk partition. */
int find_parent_disk (const char *name)
{
grub_disk_t disk;
disk = grub_disk_open (name);
if (! disk)
return 1;
if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID)
{
struct grub_efidisk_data *d;
d = disk->data;
if (grub_efi_compare_device_paths (d->device_path, sdp) == 0)
{
parent = disk;
return 1;
}
}
grub_disk_close (disk);
return 0;
}
dp = grub_efi_get_device_path (handle);
if (! dp)
@ -725,40 +709,12 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle)
&& (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp)
== GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE))
{
/* This is a hard disk partition. */
grub_disk_t parent = 0;
grub_partition_t tpart = NULL;
char *partition_name = NULL;
char *device_name;
grub_efi_device_path_t *dup_dp, *dup_ldp;
grub_efi_hard_drive_device_path_t hd;
auto int find_parent_disk (const char *name);
auto int find_partition (grub_disk_t disk, const grub_partition_t part);
/* Find the disk which is the parent of a given hard disk partition. */
int find_parent_disk (const char *name)
{
grub_disk_t disk;
disk = grub_disk_open (name);
if (! disk)
return 1;
if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID)
{
struct grub_efidisk_data *d;
d = disk->data;
if (compare_device_paths (d->device_path, dup_dp) == 0)
{
parent = disk;
return 1;
}
}
grub_disk_close (disk);
return 0;
}
/* Find the identical partition. */
int find_partition (grub_disk_t disk __attribute__ ((unused)),
const grub_partition_t part)
@ -766,7 +722,7 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle)
if (grub_partition_get_start (part) == hd.partition_start
&& grub_partition_get_len (part) == hd.partition_size)
{
tpart = part;
partition_name = grub_partition_get_name (part);
return 1;
}
@ -785,7 +741,11 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle)
dup_ldp->length[0] = sizeof (*dup_ldp);
dup_ldp->length[1] = 0;
grub_efidisk_iterate (find_parent_disk);
sdp = dup_dp;
grub_efidisk_iterate (find_parent_disk, GRUB_DISK_PULL_NONE);
if (!parent)
grub_efidisk_iterate (find_parent_disk, GRUB_DISK_PULL_REMOVABLE);
grub_free (dup_dp);
if (! parent)
@ -793,19 +753,24 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle)
/* Find a partition which matches the hard drive device path. */
grub_memcpy (&hd, ldp, sizeof (hd));
grub_partition_iterate (parent, find_partition);
if (! tpart)
if (hd.partition_start == 0
&& hd.partition_size == grub_disk_get_size (parent))
{
grub_disk_close (parent);
return 0;
device_name = grub_strdup (parent->name);
}
else
{
grub_partition_iterate (parent, find_partition);
{
char *partition_name = grub_partition_get_name (tpart);
device_name = grub_xasprintf ("%s,%s", parent->name, partition_name);
grub_free (partition_name);
}
if (! partition_name)
{
grub_disk_close (parent);
return 0;
}
device_name = grub_xasprintf ("%s,%s", parent->name, partition_name);
grub_free (partition_name);
}
grub_disk_close (parent);
return device_name;
@ -813,38 +778,17 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle)
else
{
/* This should be an entire disk. */
auto int find_disk (const char *name);
char *device_name = 0;
int find_disk (const char *name)
{
grub_disk_t disk;
sdp = dp;
disk = grub_disk_open (name);
if (! disk)
return 1;
if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID)
{
struct grub_efidisk_data *d;
d = disk->data;
if (compare_device_paths (d->device_path, dp) == 0)
{
device_name = grub_strdup (disk->name);
grub_disk_close (disk);
return 1;
}
}
grub_disk_close (disk);
return 0;
}
grub_efidisk_iterate (find_disk);
grub_efidisk_iterate (find_parent_disk, GRUB_DISK_PULL_NONE);
if (!parent)
grub_efidisk_iterate (find_parent_disk, GRUB_DISK_PULL_REMOVABLE);
if (!parent)
return NULL;
device_name = grub_strdup (parent->name);
grub_disk_close (parent);
return device_name;
}
return 0;
}

568
grub-core/disk/geli.c Normal file
View file

@ -0,0 +1,568 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003,2007,2010,2011 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file is loosely based on FreeBSD geli implementation
(but no code was directly copied). FreeBSD geli is distributed under
following terms: */
/*-
* Copyright (c) 2005-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* 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 AUTHORS 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 AUTHORS 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 <grub/cryptodisk.h>
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/dl.h>
#include <grub/err.h>
#include <grub/disk.h>
#include <grub/crypto.h>
#include <grub/partition.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* Dirty trick to solve circular dependency. */
#ifdef GRUB_UTIL
#include <grub/util/misc.h>
#undef GRUB_MD_SHA256
#undef GRUB_MD_SHA512
static const gcry_md_spec_t *
grub_md_sha256_real (void)
{
const gcry_md_spec_t *ret;
ret = grub_crypto_lookup_md_by_name ("sha256");
if (!ret)
grub_util_error ("Coulnd't load sha256");
return ret;
}
static const gcry_md_spec_t *
grub_md_sha512_real (void)
{
const gcry_md_spec_t *ret;
ret = grub_crypto_lookup_md_by_name ("sha512");
if (!ret)
grub_util_error ("Coulnd't load sha512");
return ret;
}
#define GRUB_MD_SHA256 grub_md_sha256_real()
#define GRUB_MD_SHA512 grub_md_sha512_real()
#endif
struct grub_geli_key
{
grub_uint8_t iv_key[64];
grub_uint8_t cipher_key[64];
grub_uint8_t hmac[64];
} __attribute__ ((packed));
struct grub_geli_phdr
{
grub_uint8_t magic[16];
#define GELI_MAGIC "GEOM::ELI"
grub_uint32_t version;
grub_uint32_t flags;
grub_uint16_t alg;
grub_uint16_t keylen;
grub_uint16_t unused3[5];
grub_uint32_t sector_size;
grub_uint8_t keys_used;
grub_uint32_t niter;
grub_uint8_t salt[64];
struct grub_geli_key keys[2];
} __attribute__ ((packed));
enum
{
GRUB_GELI_FLAGS_ONETIME = 1,
GRUB_GELI_FLAGS_BOOT = 2,
};
/* FIXME: support version 0. */
/* FIXME: support big-endian pre-version-4 volumes. */
/* FIXME: support for keyfiles. */
/* FIXME: support for HMAC. */
const char *algorithms[] = {
[0x01] = "des",
[0x02] = "3des",
[0x03] = "blowfish",
[0x04] = "cast5",
/* FIXME: 0x05 is skipjack, but we don't have it. */
[0x0b] = "aes",
/* FIXME: 0x10 is null. */
[0x15] = "camellia128",
[0x16] = "aes"
};
#define MAX_PASSPHRASE 256
static gcry_err_code_t
geli_rekey (struct grub_cryptodisk *dev, grub_uint64_t zoneno)
{
gcry_err_code_t gcry_err;
const struct {
char magic[4];
grub_uint64_t zone;
} __attribute__ ((packed)) tohash
= { {'e', 'k', 'e', 'y'}, grub_cpu_to_le64 (zoneno) };
grub_uint64_t key[(dev->hash->mdlen + 7) / 8];
grub_dprintf ("geli", "rekeying %" PRIuGRUB_UINT64_T " keysize=%d\n",
zoneno, dev->rekey_derived_size);
gcry_err = grub_crypto_hmac_buffer (dev->hash, dev->rekey_key, 64,
&tohash, sizeof (tohash), key);
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
return grub_cryptodisk_setkey (dev, (grub_uint8_t *) key,
dev->rekey_derived_size);
}
static inline int
ascii2hex (char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return 0;
}
static inline gcry_err_code_t
make_uuid (const struct grub_geli_phdr *header,
char *uuid)
{
grub_uint8_t uuidbin[GRUB_MD_SHA256->mdlen];
gcry_err_code_t err;
grub_uint8_t *iptr;
char *optr;
err = grub_crypto_hmac_buffer (GRUB_MD_SHA256,
header->salt, sizeof (header->salt),
"uuid", sizeof ("uuid") - 1, uuidbin);
if (err)
return err;
optr = uuid;
for (iptr = uuidbin; iptr < &uuidbin[ARRAY_SIZE (uuidbin)]; iptr++)
{
grub_snprintf (optr, 3, "%02x", *iptr);
optr += 2;
}
*optr = 0;
return GPG_ERR_NO_ERROR;
}
#ifdef GRUB_UTIL
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <grub/emu/hostdisk.h>
#include <unistd.h>
#include <string.h>
#include <grub/emu/misc.h>
char *
grub_util_get_geli_uuid (const char *dev)
{
int fd = open (dev, O_RDONLY);
grub_uint64_t s;
unsigned log_secsize;
grub_uint8_t hdr[512];
struct grub_geli_phdr *header;
char *uuid;
gcry_err_code_t err;
if (fd < 0)
return NULL;
s = grub_util_get_fd_sectors (fd, &log_secsize);
grub_util_fd_seek (fd, dev, (s << log_secsize) - 512);
uuid = xmalloc (GRUB_MD_SHA256->mdlen * 2 + 1);
if (grub_util_fd_read (fd, (void *) &hdr, 512) < 0)
grub_util_error (_("couldn't read ELI metadata"));
COMPILE_TIME_ASSERT (sizeof (header) <= 512);
header = (void *) &hdr;
/* Look for GELI magic sequence. */
if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC))
|| grub_le_to_cpu32 (header->version) > 5
|| grub_le_to_cpu32 (header->version) < 1)
grub_util_error (_("wrong ELI magic or version"));
err = make_uuid ((void *) &hdr, uuid);
if (err)
return NULL;
return uuid;
}
#endif
static grub_cryptodisk_t
configure_ciphers (grub_disk_t disk, const char *check_uuid,
int boot_only)
{
grub_cryptodisk_t newdev;
struct grub_geli_phdr header;
grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL;
const struct gcry_cipher_spec *ciph;
const char *ciphername = NULL;
gcry_err_code_t gcry_err;
char uuid[GRUB_MD_SHA256->mdlen * 2 + 1];
grub_disk_addr_t sector;
grub_err_t err;
sector = grub_disk_get_size (disk);
if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0)
return NULL;
/* Read the GELI header. */
err = grub_disk_read (disk, sector - 1, 0, sizeof (header), &header);
if (err)
return NULL;
/* Look for GELI magic sequence. */
if (grub_memcmp (header.magic, GELI_MAGIC, sizeof (GELI_MAGIC))
|| grub_le_to_cpu32 (header.version) > 5
|| grub_le_to_cpu32 (header.version) < 1)
{
grub_dprintf ("geli", "wrong magic %02x\n", header.magic[0]);
return NULL;
}
if ((grub_le_to_cpu32 (header.sector_size)
& (grub_le_to_cpu32 (header.sector_size) - 1))
|| grub_le_to_cpu32 (header.sector_size) == 0)
{
grub_dprintf ("geli", "incorrect sector size %d\n",
grub_le_to_cpu32 (header.sector_size));
return NULL;
}
if (grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_ONETIME)
{
grub_dprintf ("geli", "skipping one-time volume\n");
return NULL;
}
if (boot_only && !(grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_BOOT))
{
grub_dprintf ("geli", "not a boot volume\n");
return NULL;
}
gcry_err = make_uuid (&header, uuid);
if (gcry_err)
{
grub_crypto_gcry_error (gcry_err);
return NULL;
}
if (check_uuid && grub_strcasecmp (check_uuid, uuid) != 0)
{
grub_dprintf ("geli", "%s != %s\n", uuid, check_uuid);
return NULL;
}
if (grub_le_to_cpu16 (header.alg) >= ARRAY_SIZE (algorithms)
|| algorithms[grub_le_to_cpu16 (header.alg)] == NULL)
{
grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher 0x%x unknown",
grub_le_to_cpu16 (header.alg));
return NULL;
}
ciphername = algorithms[grub_le_to_cpu16 (header.alg)];
ciph = grub_crypto_lookup_cipher_by_name (ciphername);
if (!ciph)
{
grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available",
ciphername);
return NULL;
}
/* Configure the cipher used for the bulk data. */
cipher = grub_crypto_cipher_open (ciph);
if (!cipher)
return NULL;
if (grub_le_to_cpu16 (header.alg) == 0x16)
{
secondary_cipher = grub_crypto_cipher_open (ciph);
if (!secondary_cipher)
return NULL;
}
if (grub_le_to_cpu16 (header.keylen) > 1024)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d",
grub_le_to_cpu16 (header.keylen));
return NULL;
}
newdev = grub_zalloc (sizeof (struct grub_cryptodisk));
if (!newdev)
return NULL;
newdev->cipher = cipher;
newdev->secondary_cipher = secondary_cipher;
newdev->offset = 0;
newdev->source_disk = NULL;
newdev->benbi_log = 0;
if (grub_le_to_cpu16 (header.alg) == 0x16)
{
newdev->mode = GRUB_CRYPTODISK_MODE_XTS;
newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64;
}
else
{
newdev->mode = GRUB_CRYPTODISK_MODE_CBC;
newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH;
}
newdev->essiv_cipher = NULL;
newdev->essiv_hash = NULL;
newdev->hash = GRUB_MD_SHA512;
newdev->iv_hash = GRUB_MD_SHA256;
for (newdev->log_sector_size = 0;
(1U << newdev->log_sector_size) < grub_le_to_cpu32 (header.sector_size);
newdev->log_sector_size++);
if (grub_le_to_cpu32 (header.version) >= 5)
{
newdev->rekey = geli_rekey;
newdev->rekey_shift = 20;
}
#ifdef GRUB_UTIL
newdev->modname = "geli";
#endif
newdev->total_length = grub_disk_get_size (disk) - 1;
grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid));
COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= 32 * 2 + 1);
return newdev;
}
static grub_err_t
recover_key (grub_disk_t source, grub_cryptodisk_t dev)
{
grub_size_t keysize;
grub_uint8_t digest[dev->hash->mdlen];
grub_uint8_t geomkey[dev->hash->mdlen];
grub_uint8_t verify_key[dev->hash->mdlen];
grub_uint8_t zero[dev->cipher->cipher->blocksize];
char passphrase[MAX_PASSPHRASE] = "";
unsigned i;
gcry_err_code_t gcry_err;
struct grub_geli_phdr header;
char *tmp;
grub_disk_addr_t sector;
grub_err_t err;
sector = grub_disk_get_size (source);
if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0)
return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli");
/* Read the GELI header. */
err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header);
if (err)
return err;
keysize = grub_le_to_cpu16 (header.keylen) / 8;
grub_memset (zero, 0, sizeof (zero));
grub_puts_ (N_("Attempting to decrypt master key..."));
/* Get the passphrase from the user. */
tmp = NULL;
if (source->partition)
tmp = grub_partition_get_name (source->partition);
grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name,
source->partition ? "," : "", tmp ? : "",
dev->uuid);
grub_free (tmp);
if (!grub_password_get (passphrase, MAX_PASSPHRASE))
return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied");
/* Calculate the PBKDF2 of the user supplied passphrase. */
if (grub_le_to_cpu32 (header.niter) != 0)
{
grub_uint8_t pbkdf_key[64];
gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase,
grub_strlen (passphrase),
header.salt,
sizeof (header.salt),
grub_le_to_cpu32 (header.niter),
pbkdf_key, sizeof (pbkdf_key));
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
gcry_err = grub_crypto_hmac_buffer (dev->hash, NULL, 0, pbkdf_key,
sizeof (pbkdf_key), geomkey);
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
}
else
{
struct grub_crypto_hmac_handle *hnd;
hnd = grub_crypto_hmac_init (dev->hash, NULL, 0);
if (!hnd)
return grub_crypto_gcry_error (GPG_ERR_OUT_OF_MEMORY);
grub_crypto_hmac_write (hnd, header.salt, sizeof (header.salt));
grub_crypto_hmac_write (hnd, passphrase, grub_strlen (passphrase));
gcry_err = grub_crypto_hmac_fini (hnd, geomkey);
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
}
gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
sizeof (geomkey), "\1", 1, digest);
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
sizeof (geomkey), "\0", 1, verify_key);
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
grub_dprintf ("geli", "keylen = %" PRIuGRUB_SIZE "\n", keysize);
/* Try to recover master key from each active keyslot. */
for (i = 0; i < ARRAY_SIZE (header.keys); i++)
{
struct grub_geli_key candidate_key;
grub_uint8_t key_hmac[dev->hash->mdlen];
/* Check if keyslot is enabled. */
if (! (header.keys_used & (1 << i)))
continue;
grub_dprintf ("geli", "Trying keyslot %d\n", i);
gcry_err = grub_crypto_cipher_set_key (dev->cipher,
digest, keysize);
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
gcry_err = grub_crypto_cbc_decrypt (dev->cipher, &candidate_key,
&header.keys[i],
sizeof (candidate_key),
zero);
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
gcry_err = grub_crypto_hmac_buffer (dev->hash, verify_key,
sizeof (verify_key),
&candidate_key,
(sizeof (candidate_key)
- sizeof (candidate_key.hmac)),
key_hmac);
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
if (grub_memcmp (candidate_key.hmac, key_hmac, dev->hash->mdlen) != 0)
continue;
grub_printf_ (N_("Slot %d opened\n"), i);
/* Set the master key. */
if (!dev->rekey)
{
grub_size_t real_keysize = keysize;
if (grub_le_to_cpu16 (header.alg) == 0x16)
real_keysize *= 2;
gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key,
real_keysize);
if (gcry_err)
return grub_crypto_gcry_error (gcry_err);
}
else
{
grub_size_t real_keysize = keysize;
if (grub_le_to_cpu16 (header.alg) == 0x16)
real_keysize *= 2;
/* For a reason I don't know, the IV key is used in rekeying. */
grub_memcpy (dev->rekey_key, candidate_key.iv_key,
sizeof (candidate_key.iv_key));
dev->rekey_derived_size = real_keysize;
dev->last_rekey = -1;
COMPILE_TIME_ASSERT (sizeof (dev->rekey_key)
>= sizeof (candidate_key.iv_key));
}
dev->iv_prefix_len = sizeof (candidate_key.iv_key);
grub_memcpy (dev->iv_prefix, candidate_key.iv_key,
sizeof (candidate_key.iv_key));
COMPILE_TIME_ASSERT (sizeof (dev->iv_prefix) >= sizeof (candidate_key.iv_key));
return GRUB_ERR_NONE;
}
return GRUB_ACCESS_DENIED;
}
struct grub_cryptodisk_dev geli_crypto = {
.scan = configure_ciphers,
.recover_key = recover_key
};
GRUB_MOD_INIT (geli)
{
grub_cryptodisk_dev_register (&geli_crypto);
}
GRUB_MOD_FINI (geli)
{
grub_cryptodisk_dev_unregister (&geli_crypto);
}

View file

@ -27,8 +27,12 @@
int grub_disk_host_i_want_a_reference;
static int
grub_host_iterate (int (*hook) (const char *name))
grub_host_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
if (pull != GRUB_DISK_PULL_NONE)
return 0;
if (hook ("host"))
return 1;
return 0;

View file

@ -27,6 +27,7 @@
#include <grub/misc.h>
#include <grub/err.h>
#include <grub/term.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -284,37 +285,46 @@ grub_biosdisk_call_hook (int (*hook) (const char *name), int drive)
}
static int
grub_biosdisk_iterate (int (*hook) (const char *name))
grub_biosdisk_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull __attribute__ ((unused)))
{
int drive;
int num_floppies;
int drive;
/* For hard disks, attempt to read the MBR. */
for (drive = 0x80; drive < 0x90; drive++)
switch (pull)
{
if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1,
GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0)
case GRUB_DISK_PULL_NONE:
for (drive = 0x80; drive < 0x90; drive++)
{
grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive);
break;
if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1,
GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0)
{
grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive);
break;
}
if (grub_biosdisk_call_hook (hook, drive))
return 1;
}
return 0;
case GRUB_DISK_PULL_REMOVABLE:
if (cd_drive)
{
if (grub_biosdisk_call_hook (hook, cd_drive))
return 1;
}
if (grub_biosdisk_call_hook (hook, drive))
return 1;
/* For floppy disks, we can get the number safely. */
num_floppies = grub_biosdisk_get_num_floppies ();
for (drive = 0; drive < num_floppies; drive++)
if (grub_biosdisk_call_hook (hook, drive))
return 1;
return 0;
default:
return 0;
}
if (cd_drive)
{
if (grub_biosdisk_call_hook (hook, cd_drive))
return 1;
}
/* For floppy disks, we can get the number safely. */
num_floppies = grub_biosdisk_get_num_floppies ();
for (drive = 0; drive < num_floppies; drive++)
if (grub_biosdisk_call_hook (hook, drive))
return 1;
return 0;
}
@ -340,7 +350,8 @@ grub_biosdisk_open (const char *name, grub_disk_t disk)
if ((cd_drive) && (drive == cd_drive))
{
data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM;
data->sectors = 32;
data->sectors = 8;
disk->log_sector_size = 11;
/* TODO: get the correct size. */
total_sectors = GRUB_DISK_SIZE_UNKNOWN;
}
@ -349,6 +360,8 @@ grub_biosdisk_open (const char *name, grub_disk_t disk)
/* HDD */
int version;
disk->log_sector_size = 9;
version = grub_biosdisk_check_int13_extensions (drive);
if (version)
{
@ -369,6 +382,15 @@ grub_biosdisk_open (const char *name, grub_disk_t disk)
correctly but returns zero. So if it is zero, compute
it by C/H/S returned by the LBA BIOS call. */
total_sectors = drp->cylinders * drp->heads * drp->sectors;
if (drp->bytes_per_sector
&& !(drp->bytes_per_sector & (drp->bytes_per_sector - 1))
&& drp->bytes_per_sector >= 512
&& drp->bytes_per_sector <= 16384)
{
for (disk->log_sector_size = 0;
(1 << disk->log_sector_size) < drp->bytes_per_sector;
disk->log_sector_size++);
}
}
}
}
@ -431,7 +453,7 @@ grub_biosdisk_rw (int cmd, grub_disk_t disk,
dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR
+ (data->sectors
<< GRUB_DISK_SECTOR_BITS));
<< disk->log_sector_size));
dap->length = sizeof (*dap);
dap->reserved = 0;
dap->blocks = size;
@ -445,9 +467,6 @@ grub_biosdisk_rw (int cmd, grub_disk_t disk,
if (cmd)
return grub_error (GRUB_ERR_WRITE_ERROR, "can\'t write to cdrom");
dap->blocks = ALIGN_UP (dap->blocks, 4) >> 2;
dap->block >>= 2;
for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++)
if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap))
break;
@ -503,10 +522,12 @@ grub_biosdisk_rw (int cmd, grub_disk_t disk,
/* Return the number of sectors which can be read safely at a time. */
static grub_size_t
get_safe_sectors (grub_disk_addr_t sector, grub_uint32_t sectors)
get_safe_sectors (grub_disk_t disk, grub_disk_addr_t sector)
{
grub_size_t size;
grub_uint64_t offset;
struct grub_biosdisk_data *data = disk->data;
grub_uint32_t sectors = data->sectors;
/* OFFSET = SECTOR % SECTORS */
grub_divmod64 (sector, sectors, &offset);
@ -514,8 +535,8 @@ get_safe_sectors (grub_disk_addr_t sector, grub_uint32_t sectors)
size = sectors - offset;
/* Limit the max to 0x7f because of Phoenix EDD. */
if (size > 0x7f)
size = 0x7f;
if (size > ((0x7fU << GRUB_DISK_SECTOR_BITS) >> disk->log_sector_size))
size = ((0x7fU << GRUB_DISK_SECTOR_BITS) >> disk->log_sector_size);
return size;
}
@ -524,21 +545,11 @@ static grub_err_t
grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf)
{
struct grub_biosdisk_data *data = disk->data;
while (size)
{
grub_size_t len;
grub_size_t cdoff = 0;
len = get_safe_sectors (sector, data->sectors);
if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
{
cdoff = (sector & 3) << GRUB_DISK_SECTOR_BITS;
len = ALIGN_UP (sector + len, 4) - (sector & ~3);
sector &= ~3;
}
len = get_safe_sectors (disk, sector);
if (len > size)
len = size;
@ -547,9 +558,10 @@ grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
GRUB_MEMORY_MACHINE_SCRATCH_SEG))
return grub_errno;
grub_memcpy (buf, (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + cdoff),
len << GRUB_DISK_SECTOR_BITS);
buf += len << GRUB_DISK_SECTOR_BITS;
grub_memcpy (buf, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
len << disk->log_sector_size);
buf += len << disk->log_sector_size;
sector += len;
size -= len;
}
@ -570,18 +582,18 @@ grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
{
grub_size_t len;
len = get_safe_sectors (sector, data->sectors);
len = get_safe_sectors (disk, sector);
if (len > size)
len = size;
grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf,
len << GRUB_DISK_SECTOR_BITS);
len << disk->log_sector_size);
if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len,
GRUB_MEMORY_MACHINE_SCRATCH_SEG))
return grub_errno;
buf += len << GRUB_DISK_SECTOR_BITS;
buf += len << disk->log_sector_size;
sector += len;
size -= len;
}
@ -610,11 +622,12 @@ grub_disk_biosdisk_fini (void)
GRUB_MOD_INIT(biosdisk)
{
struct grub_biosdisk_cdrp *cdrp
= (struct grub_biosdisk_cdrp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
= (struct grub_biosdisk_cdrp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
grub_uint8_t boot_drive;
if (grub_disk_firmware_is_tainted)
{
grub_printf ("Firmware is marked as tainted, refusing to initialize.\n");
grub_puts_ (N_("Firmware is marked as tainted, refusing to initialize."));
return;
}
grub_disk_firmware_fini = grub_disk_biosdisk_fini;
@ -622,15 +635,16 @@ GRUB_MOD_INIT(biosdisk)
grub_memset (cdrp, 0, sizeof (*cdrp));
cdrp->size = sizeof (*cdrp);
cdrp->media_type = 0xFF;
if ((! grub_biosdisk_get_cdinfo_int13_extensions (grub_boot_drive, cdrp)) &&
((cdrp->media_type & GRUB_BIOSDISK_CDTYPE_MASK)
== GRUB_BIOSDISK_CDTYPE_NO_EMUL))
boot_drive = (grub_boot_device >> 24);
if ((! grub_biosdisk_get_cdinfo_int13_extensions (boot_drive, cdrp))
&& ((cdrp->media_type & GRUB_BIOSDISK_CDTYPE_MASK)
== GRUB_BIOSDISK_CDTYPE_NO_EMUL))
cd_drive = cdrp->drive_no;
/* Since diskboot.S rejects devices over 0x90 it must be a CD booted with
cdboot.S
*/
if (grub_boot_drive >= 0x90)
cd_drive = grub_boot_drive;
if (boot_drive >= 0x90)
cd_drive = boot_drive;
grub_disk_dev_register (&grub_biosdisk_dev);
}

View file

@ -32,7 +32,8 @@ struct grub_nand_data
};
static int
grub_nand_iterate (int (*hook) (const char *name))
grub_nand_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
auto int dev_iterate (struct grub_ieee1275_devalias *alias);
int dev_iterate (struct grub_ieee1275_devalias *alias)
@ -46,6 +47,9 @@ grub_nand_iterate (int (*hook) (const char *name))
return 0;
}
if (pull != GRUB_DISK_PULL_NONE)
return 0;
return grub_devalias_iterate (dev_iterate);
}

View file

@ -152,9 +152,14 @@ scan (void)
}
static int
grub_ofdisk_iterate (int (*hook) (const char *name))
grub_ofdisk_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
unsigned i;
if (pull != GRUB_DISK_PULL_NONE)
return 0;
scan ();
for (i = 0; i < ARRAY_SIZE (ofdisk_hash); i++)
@ -252,14 +257,24 @@ grub_ofdisk_open (const char *name, grub_disk_t disk)
grub_dprintf ("disk", "Opening `%s'.\n", devpath);
if (grub_ieee1275_finddevice (devpath, &dev))
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't read device properties");
{
grub_free (devpath);
return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
"can't read device properties");
}
if (grub_ieee1275_get_property (dev, "device_type", prop, sizeof (prop),
&actual))
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't read the device type");
{
grub_free (devpath);
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't read the device type");
}
if (grub_strcmp (prop, "block"))
return grub_error (GRUB_ERR_BAD_DEVICE, "not a block device");
{
grub_free (devpath);
return grub_error (GRUB_ERR_BAD_DEVICE, "not a block device");
}
/* XXX: There is no property to read the number of blocks. There
should be a property `#blocks', but it is not there. Perhaps it

View file

@ -133,9 +133,12 @@ fail:
static int
grub_loopback_iterate (int (*hook) (const char *name))
grub_loopback_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
struct grub_loopback *d;
if (pull != GRUB_DISK_PULL_NONE)
return 0;
for (d = loopback_list; d; d = d->next)
{
if (hook (d->devname))

470
grub-core/disk/luks.c Normal file
View file

@ -0,0 +1,470 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003,2007,2010,2011 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/cryptodisk.h>
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/dl.h>
#include <grub/err.h>
#include <grub/disk.h>
#include <grub/crypto.h>
#include <grub/partition.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define MAX_PASSPHRASE 256
#define LUKS_KEY_ENABLED 0x00AC71F3
/* On disk LUKS header */
struct grub_luks_phdr
{
grub_uint8_t magic[6];
#define LUKS_MAGIC "LUKS\xBA\xBE"
grub_uint16_t version;
char cipherName[32];
char cipherMode[32];
char hashSpec[32];
grub_uint32_t payloadOffset;
grub_uint32_t keyBytes;
grub_uint8_t mkDigest[20];
grub_uint8_t mkDigestSalt[32];
grub_uint32_t mkDigestIterations;
char uuid[40];
struct
{
grub_uint32_t active;
grub_uint32_t passwordIterations;
grub_uint8_t passwordSalt[32];
grub_uint32_t keyMaterialOffset;
grub_uint32_t stripes;
} keyblock[8];
} __attribute__ ((packed));
typedef struct grub_luks_phdr *grub_luks_phdr_t;
gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src,
grub_uint8_t * dst, grub_size_t blocksize,
grub_size_t blocknumbers);
static grub_cryptodisk_t
configure_ciphers (grub_disk_t disk, const char *check_uuid,
int check_boot)
{
grub_cryptodisk_t newdev;
const char *iptr;
struct grub_luks_phdr header;
char *optr;
char uuid[sizeof (header.uuid) + 1];
char ciphername[sizeof (header.cipherName) + 1];
char ciphermode[sizeof (header.cipherMode) + 1];
char *cipheriv = NULL;
char hashspec[sizeof (header.hashSpec) + 1];
grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL;
grub_crypto_cipher_handle_t essiv_cipher = NULL;
const gcry_md_spec_t *hash = NULL, *essiv_hash = NULL;
const struct gcry_cipher_spec *ciph;
grub_cryptodisk_mode_t mode;
grub_cryptodisk_mode_iv_t mode_iv;
int benbi_log = 0;
grub_err_t err;
if (check_boot)
return NULL;
/* Read the LUKS header. */
err = grub_disk_read (disk, 0, 0, sizeof (header), &header);
if (err)
{
if (err == GRUB_ERR_OUT_OF_RANGE)
grub_errno = GRUB_ERR_NONE;
return NULL;
}
/* Look for LUKS magic sequence. */
if (grub_memcmp (header.magic, LUKS_MAGIC, sizeof (header.magic))
|| grub_be_to_cpu16 (header.version) != 1)
return NULL;
optr = uuid;
for (iptr = header.uuid; iptr < &header.uuid[ARRAY_SIZE (header.uuid)];
iptr++)
{
if (*iptr != '-')
*optr++ = *iptr;
}
*optr = 0;
if (check_uuid && grub_strcasecmp (check_uuid, uuid) != 0)
{
grub_dprintf ("luks", "%s != %s\n", uuid, check_uuid);
return NULL;
}
/* Make sure that strings are null terminated. */
grub_memcpy (ciphername, header.cipherName, sizeof (header.cipherName));
ciphername[sizeof (header.cipherName)] = 0;
grub_memcpy (ciphermode, header.cipherMode, sizeof (header.cipherMode));
ciphermode[sizeof (header.cipherMode)] = 0;
grub_memcpy (hashspec, header.hashSpec, sizeof (header.hashSpec));
hashspec[sizeof (header.hashSpec)] = 0;
ciph = grub_crypto_lookup_cipher_by_name (ciphername);
if (!ciph)
{
grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available",
ciphername);
return NULL;
}
/* Configure the cipher used for the bulk data. */
cipher = grub_crypto_cipher_open (ciph);
if (!cipher)
return NULL;
if (grub_be_to_cpu32 (header.keyBytes) > 1024)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d",
grub_be_to_cpu32 (header.keyBytes));
return NULL;
}
/* Configure the cipher mode. */
if (grub_strcmp (ciphermode, "ecb") == 0)
{
mode = GRUB_CRYPTODISK_MODE_ECB;
mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN;
cipheriv = NULL;
}
else if (grub_strcmp (ciphermode, "plain") == 0)
{
mode = GRUB_CRYPTODISK_MODE_CBC;
mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN;
cipheriv = NULL;
}
else if (grub_memcmp (ciphermode, "cbc-", sizeof ("cbc-") - 1) == 0)
{
mode = GRUB_CRYPTODISK_MODE_CBC;
cipheriv = ciphermode + sizeof ("cbc-") - 1;
}
else if (grub_memcmp (ciphermode, "pcbc-", sizeof ("pcbc-") - 1) == 0)
{
mode = GRUB_CRYPTODISK_MODE_PCBC;
cipheriv = ciphermode + sizeof ("pcbc-") - 1;
}
else if (grub_memcmp (ciphermode, "xts-", sizeof ("xts-") - 1) == 0)
{
mode = GRUB_CRYPTODISK_MODE_XTS;
cipheriv = ciphermode + sizeof ("xts-") - 1;
secondary_cipher = grub_crypto_cipher_open (ciph);
if (!secondary_cipher)
{
grub_crypto_cipher_close (cipher);
return NULL;
}
if (cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES)
{
grub_crypto_cipher_close (cipher);
grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d",
cipher->cipher->blocksize);
return NULL;
}
if (secondary_cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES)
{
grub_crypto_cipher_close (cipher);
grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d",
secondary_cipher->cipher->blocksize);
return NULL;
}
}
else if (grub_memcmp (ciphermode, "lrw-", sizeof ("lrw-") - 1) == 0)
{
mode = GRUB_CRYPTODISK_MODE_LRW;
cipheriv = ciphermode + sizeof ("lrw-") - 1;
if (cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES)
{
grub_crypto_cipher_close (cipher);
grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported LRW block size: %d",
cipher->cipher->blocksize);
return NULL;
}
}
else
{
grub_crypto_cipher_close (cipher);
grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown cipher mode: %s",
ciphermode);
return NULL;
}
if (cipheriv == NULL);
else if (grub_memcmp (cipheriv, "plain", sizeof ("plain") - 1) == 0)
mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN;
else if (grub_memcmp (cipheriv, "plain64", sizeof ("plain64") - 1) == 0)
mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN64;
else if (grub_memcmp (cipheriv, "benbi", sizeof ("benbi") - 1) == 0)
{
if (cipher->cipher->blocksize & (cipher->cipher->blocksize - 1)
|| cipher->cipher->blocksize == 0)
grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported benbi blocksize: %d",
cipher->cipher->blocksize);
for (benbi_log = 0;
(cipher->cipher->blocksize << benbi_log) < GRUB_DISK_SECTOR_SIZE;
benbi_log++);
mode_iv = GRUB_CRYPTODISK_MODE_IV_BENBI;
}
else if (grub_memcmp (cipheriv, "null", sizeof ("null") - 1) == 0)
mode_iv = GRUB_CRYPTODISK_MODE_IV_NULL;
else if (grub_memcmp (cipheriv, "essiv:", sizeof ("essiv:") - 1) == 0)
{
char *hash_str = cipheriv + 6;
mode_iv = GRUB_CRYPTODISK_MODE_IV_ESSIV;
/* Configure the hash and cipher used for ESSIV. */
essiv_hash = grub_crypto_lookup_md_by_name (hash_str);
if (!essiv_hash)
{
grub_crypto_cipher_close (cipher);
grub_error (GRUB_ERR_FILE_NOT_FOUND,
"Couldn't load %s hash", hash_str);
return NULL;
}
essiv_cipher = grub_crypto_cipher_open (ciph);
if (!essiv_cipher)
{
grub_crypto_cipher_close (cipher);
return NULL;
}
}
else
{
grub_crypto_cipher_close (cipher);
grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown IV mode: %s",
cipheriv);
return NULL;
}
/* Configure the hash used for the AF splitter and HMAC. */
hash = grub_crypto_lookup_md_by_name (hashspec);
if (!hash)
{
grub_crypto_cipher_close (cipher);
grub_crypto_cipher_close (essiv_cipher);
grub_crypto_cipher_close (secondary_cipher);
grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash",
hashspec);
return NULL;
}
newdev = grub_zalloc (sizeof (struct grub_cryptodisk));
if (!newdev)
return NULL;
newdev->cipher = cipher;
newdev->offset = grub_be_to_cpu32 (header.payloadOffset);
newdev->source_disk = NULL;
newdev->benbi_log = benbi_log;
newdev->mode = mode;
newdev->mode_iv = mode_iv;
newdev->secondary_cipher = secondary_cipher;
newdev->essiv_cipher = essiv_cipher;
newdev->essiv_hash = essiv_hash;
newdev->hash = hash;
newdev->log_sector_size = 9;
newdev->total_length = grub_disk_get_size (disk) - newdev->offset;
grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid));
#ifdef GRUB_UTIL
newdev->modname = "luks";
#endif
COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= sizeof (uuid));
return newdev;
}
static grub_err_t
luks_recover_key (grub_disk_t source,
grub_cryptodisk_t dev)
{
struct grub_luks_phdr header;
grub_size_t keysize;
grub_uint8_t *split_key = NULL;
char passphrase[MAX_PASSPHRASE] = "";
grub_uint8_t candidate_digest[sizeof (header.mkDigest)];
unsigned i;
grub_size_t length;
grub_err_t err;
grub_size_t max_stripes = 1;
char *tmp;
err = grub_disk_read (source, 0, 0, sizeof (header), &header);
if (err)
return err;
grub_puts_ (N_("Attempting to decrypt master key..."));
keysize = grub_be_to_cpu32 (header.keyBytes);
for (i = 0; i < ARRAY_SIZE (header.keyblock); i++)
if (grub_be_to_cpu32 (header.keyblock[i].active) == LUKS_KEY_ENABLED
&& grub_be_to_cpu32 (header.keyblock[i].stripes) > max_stripes)
max_stripes = grub_be_to_cpu32 (header.keyblock[i].stripes);
split_key = grub_malloc (keysize * max_stripes);
if (!split_key)
return grub_errno;
/* Get the passphrase from the user. */
tmp = NULL;
if (source->partition)
tmp = grub_partition_get_name (source->partition);
grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name,
source->partition ? "," : "", tmp ? : "",
dev->uuid);
grub_free (tmp);
if (!grub_password_get (passphrase, MAX_PASSPHRASE))
{
grub_free (split_key);
return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied");
}
/* Try to recover master key from each active keyslot. */
for (i = 0; i < ARRAY_SIZE (header.keyblock); i++)
{
gcry_err_code_t gcry_err;
grub_uint8_t candidate_key[keysize];
grub_uint8_t digest[keysize];
/* Check if keyslot is enabled. */
if (grub_be_to_cpu32 (header.keyblock[i].active) != LUKS_KEY_ENABLED)
continue;
grub_dprintf ("luks", "Trying keyslot %d\n", i);
/* Calculate the PBKDF2 of the user supplied passphrase. */
gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase,
grub_strlen (passphrase),
header.keyblock[i].passwordSalt,
sizeof (header.keyblock[i].passwordSalt),
grub_be_to_cpu32 (header.keyblock[i].
passwordIterations),
digest, keysize);
if (gcry_err)
{
grub_free (split_key);
return grub_crypto_gcry_error (gcry_err);
}
grub_dprintf ("luks", "PBKDF2 done\n");
gcry_err = grub_cryptodisk_setkey (dev, digest, keysize);
if (gcry_err)
{
grub_free (split_key);
return grub_crypto_gcry_error (gcry_err);
}
length = (keysize * grub_be_to_cpu32 (header.keyblock[i].stripes));
/* Read and decrypt the key material from the disk. */
err = grub_disk_read (source,
grub_be_to_cpu32 (header.keyblock
[i].keyMaterialOffset), 0,
length, split_key);
if (err)
{
grub_free (split_key);
return err;
}
gcry_err = grub_cryptodisk_decrypt (dev, split_key, length, 0);
if (gcry_err)
{
grub_free (split_key);
return grub_crypto_gcry_error (gcry_err);
}
/* Merge the decrypted key material to get the candidate master key. */
gcry_err = AF_merge (dev->hash, split_key, candidate_key, keysize,
grub_be_to_cpu32 (header.keyblock[i].stripes));
if (gcry_err)
{
grub_free (split_key);
return grub_crypto_gcry_error (gcry_err);
}
grub_dprintf ("luks", "candidate key recovered\n");
/* Calculate the PBKDF2 of the candidate master key. */
gcry_err = grub_crypto_pbkdf2 (dev->hash, candidate_key,
grub_be_to_cpu32 (header.keyBytes),
header.mkDigestSalt,
sizeof (header.mkDigestSalt),
grub_be_to_cpu32
(header.mkDigestIterations),
candidate_digest,
sizeof (candidate_digest));
if (gcry_err)
{
grub_free (split_key);
return grub_crypto_gcry_error (gcry_err);
}
/* Compare the calculated PBKDF2 to the digest stored
in the header to see if it's correct. */
if (grub_memcmp (candidate_digest, header.mkDigest,
sizeof (header.mkDigest)) != 0)
{
grub_dprintf ("luks", "bad digest\n");
continue;
}
grub_printf_ (N_("Slot %d opened\n"), i);
/* Set the master key. */
gcry_err = grub_cryptodisk_setkey (dev, candidate_key, keysize);
if (gcry_err)
{
grub_free (split_key);
return grub_crypto_gcry_error (gcry_err);
}
grub_free (split_key);
return GRUB_ERR_NONE;
}
return GRUB_ACCESS_DENIED;
}
struct grub_cryptodisk_dev luks_crypto = {
.scan = configure_ciphers,
.recover_key = luks_recover_key
};
GRUB_MOD_INIT (luks)
{
COMPILE_TIME_ASSERT (sizeof (((struct grub_luks_phdr *) 0)->uuid)
< GRUB_CRYPTODISK_MAX_UUID_LENGTH);
grub_cryptodisk_dev_register (&luks_crypto);
}
GRUB_MOD_FINI (luks)
{
grub_cryptodisk_dev_unregister (&luks_crypto);
}

File diff suppressed because it is too large Load diff

View file

@ -216,11 +216,11 @@ grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array,
array->chunk_size = grub_le_to_cpu32 (sb.chunk_size) >> 9;
array->index = grub_le_to_cpu32 (sb.this_disk.number);
array->uuid_len = 16;
array->uuid = grub_malloc (16);
uuid = grub_malloc (16);
array->uuid = (char *) uuid;
if (!array->uuid)
return grub_errno;
uuid = (grub_uint32_t *) array->uuid;
uuid[0] = grub_swap_bytes32 (sb.set_uuid0);
uuid[1] = grub_swap_bytes32 (sb.set_uuid1);
uuid[2] = grub_swap_bytes32 (sb.set_uuid2);

View file

@ -30,8 +30,12 @@ static char *memdisk_addr;
static grub_off_t memdisk_size = 0;
static int
grub_memdisk_iterate (int (*hook) (const char *name))
grub_memdisk_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
if (pull != GRUB_DISK_PULL_NONE)
return 0;
return hook ("memdisk");
}
@ -82,30 +86,24 @@ static struct grub_disk_dev grub_memdisk_dev =
GRUB_MOD_INIT(memdisk)
{
auto int hook (struct grub_module_header *);
int hook (struct grub_module_header *header)
{
if (header->type == OBJ_TYPE_MEMDISK)
{
char *memdisk_orig_addr;
memdisk_orig_addr = (char *) header + sizeof (struct grub_module_header);
struct grub_module_header *header;
FOR_MODULES (header)
if (header->type == OBJ_TYPE_MEMDISK)
{
char *memdisk_orig_addr;
memdisk_orig_addr = (char *) header + sizeof (struct grub_module_header);
grub_dprintf ("memdisk", "Found memdisk image at %p\n", memdisk_orig_addr);
grub_dprintf ("memdisk", "Found memdisk image at %p\n", memdisk_orig_addr);
memdisk_size = header->size - sizeof (struct grub_module_header);
memdisk_addr = grub_malloc (memdisk_size);
memdisk_size = header->size - sizeof (struct grub_module_header);
memdisk_addr = grub_malloc (memdisk_size);
grub_dprintf ("memdisk", "Copying memdisk image to dynamic memory\n");
grub_memmove (memdisk_addr, memdisk_orig_addr, memdisk_size);
grub_dprintf ("memdisk", "Copying memdisk image to dynamic memory\n");
grub_memmove (memdisk_addr, memdisk_orig_addr, memdisk_size);
grub_disk_dev_register (&grub_memdisk_dev);
return 1;
}
return 0;
}
grub_module_iterate (hook);
grub_disk_dev_register (&grub_memdisk_dev);
break;
}
}
GRUB_MOD_FINI(memdisk)

538
grub-core/disk/pata.c Normal file
View file

@ -0,0 +1,538 @@
/* ata_pthru.c - ATA pass through for ata.mod. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/ata.h>
#include <grub/scsi.h>
#include <grub/disk.h>
#include <grub/dl.h>
#include <grub/mm.h>
#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
#include <grub/pci.h>
#include <grub/cs5536.h>
#else
#define GRUB_MACHINE_PCI_IO_BASE 0xb4000000
#endif
#include <grub/time.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* At the moment, only two IDE ports are supported. */
static const grub_port_t grub_pata_ioaddress[] = { GRUB_ATA_CH0_PORT1,
GRUB_ATA_CH1_PORT1 };
struct grub_pata_device
{
/* IDE port to use. */
int port;
/* IO addresses on which the registers for this device can be
found. */
grub_port_t ioaddress;
/* Two devices can be connected to a single cable. Use this field
to select device 0 (commonly known as "master") or device 1
(commonly known as "slave"). */
int device;
int present;
struct grub_pata_device *next;
};
static struct grub_pata_device *grub_pata_devices;
static inline void
grub_pata_regset (struct grub_pata_device *dev, int reg, int val)
{
grub_outb (val, dev->ioaddress + reg);
}
static inline grub_uint8_t
grub_pata_regget (struct grub_pata_device *dev, int reg)
{
return grub_inb (dev->ioaddress + reg);
}
/* Wait for !BSY. */
static grub_err_t
grub_pata_wait_not_busy (struct grub_pata_device *dev, int milliseconds)
{
/* ATA requires 400ns (after a write to CMD register) or
1 PIO cycle (after a DRQ block transfer) before
first check of BSY. */
grub_millisleep (1);
int i = 1;
grub_uint8_t sts;
while ((sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS))
& GRUB_ATA_STATUS_BUSY)
{
if (i >= milliseconds)
{
grub_dprintf ("pata", "timeout: %dms, status=0x%x\n",
milliseconds, sts);
return grub_error (GRUB_ERR_TIMEOUT, "PATA timeout");
}
grub_millisleep (1);
i++;
}
return GRUB_ERR_NONE;
}
static inline grub_err_t
grub_pata_check_ready (struct grub_pata_device *dev, int spinup)
{
if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
return grub_pata_wait_not_busy (dev, spinup ? GRUB_ATA_TOUT_SPINUP
: GRUB_ATA_TOUT_STD);
return GRUB_ERR_NONE;
}
static inline void
grub_pata_wait (void)
{
grub_millisleep (50);
}
static void
grub_pata_pio_read (struct grub_pata_device *dev, char *buf, grub_size_t size)
{
unsigned int i;
/* Read in the data, word by word. */
for (i = 0; i < size / 2; i++)
grub_set_unaligned16 (buf + 2 * i,
grub_le_to_cpu16 (grub_inw(dev->ioaddress
+ GRUB_ATA_REG_DATA)));
if (size & 1)
buf[size - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress
+ GRUB_ATA_REG_DATA));
}
static void
grub_pata_pio_write (struct grub_pata_device *dev, char *buf, grub_size_t size)
{
unsigned int i;
/* Write the data, word by word. */
for (i = 0; i < size / 2; i++)
grub_outw(grub_cpu_to_le16 (grub_get_unaligned16 (buf + 2 * i)), dev->ioaddress + GRUB_ATA_REG_DATA);
}
/* ATA pass through support, used by hdparm.mod. */
static grub_err_t
grub_pata_readwrite (struct grub_ata *disk,
struct grub_disk_ata_pass_through_parms *parms,
int spinup)
{
struct grub_pata_device *dev = (struct grub_pata_device *) disk->data;
grub_size_t nread = 0;
int i;
if (! (parms->cmdsize == 0 || parms->cmdsize == 12))
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"ATAPI non-12 byte commands not supported");
grub_dprintf ("pata", "pata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
parms->taskfile.cmd,
parms->taskfile.features,
parms->taskfile.sectors);
grub_dprintf ("pata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%"
PRIuGRUB_SIZE "\n",
parms->taskfile.lba_high,
parms->taskfile.lba_mid,
parms->taskfile.lba_low, parms->size);
/* Set registers. */
grub_pata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4)
| (parms->taskfile.disk & 0xef));
if (grub_pata_check_ready (dev, spinup))
return grub_errno;
for (i = GRUB_ATA_REG_SECTORS; i <= GRUB_ATA_REG_LBAHIGH; i++)
grub_pata_regset (dev, i,
parms->taskfile.raw[7 + (i - GRUB_ATA_REG_SECTORS)]);
for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
grub_pata_regset (dev, i, parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES]);
/* Start command. */
grub_pata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile.cmd);
/* Wait for !BSY. */
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
return grub_errno;
/* Check status. */
grub_int8_t sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS);
grub_dprintf ("pata", "status=0x%x\n", sts);
if (parms->cmdsize)
{
grub_uint8_t irs;
/* Wait for !BSY. */
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
return grub_errno;
irs = grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON);
/* OK if DRQ is asserted and interrupt reason is as expected. */
if (!((sts & GRUB_ATA_STATUS_DRQ)
&& (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_CMD_OUT))
return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error");
/* Write the packet. */
grub_pata_pio_write (dev, parms->cmd, parms->cmdsize);
}
/* Transfer data. */
while (nread < parms->size
&& ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
== GRUB_ATA_STATUS_DRQ)
&& (!parms->cmdsize
|| ((grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON)
& GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_DATA_IN)))
{
unsigned cnt;
/* Wait for !BSY. */
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
return grub_errno;
if (parms->cmdsize)
{
cnt = grub_pata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8
| grub_pata_regget (dev, GRUB_ATAPI_REG_CNTLOW);
grub_dprintf("pata", "DRQ count=%u\n", cnt);
/* Count of last transfer may be uneven. */
if (! (0 < cnt && cnt <= parms->size - nread
&& (! (cnt & 1) || cnt == parms->size - nread)))
return grub_error (GRUB_ERR_READ_ERROR,
"invalid ATAPI transfer count");
}
else
cnt = GRUB_DISK_SECTOR_SIZE;
if (cnt > parms->size - nread)
cnt = parms->size - nread;
if (parms->write)
grub_pata_pio_write (dev, (char *) parms->buffer + nread, cnt);
else
grub_pata_pio_read (dev, (char *) parms->buffer + nread, cnt);
nread += cnt;
}
if (parms->write)
{
/* Check for write error. */
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
return grub_errno;
if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS)
& (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
}
parms->size = nread;
/* Wait for !BSY. */
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
return grub_errno;
/* Return registers. */
for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES] = grub_pata_regget (dev, i);
grub_dprintf ("pata", "status=0x%x, error=0x%x, sectors=0x%x\n",
parms->taskfile.status,
parms->taskfile.error,
parms->taskfile.sectors);
if (parms->taskfile.status
& (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
return grub_error (GRUB_ERR_READ_ERROR, "PATA passthrough failed");
return GRUB_ERR_NONE;
}
static grub_err_t
check_device (struct grub_pata_device *dev)
{
grub_pata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
grub_pata_wait ();
/* Try to detect if the port is in use by writing to it,
waiting for a while and reading it again. If the value
was preserved, there is a device connected. */
grub_pata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
grub_pata_wait ();
grub_uint8_t sec = grub_pata_regget (dev, GRUB_ATA_REG_SECTORS);
grub_dprintf ("ata", "sectors=0x%x\n", sec);
if (sec != 0x5A)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no device connected");
/* The above test may detect a second (slave) device
connected to a SATA controller which supports only one
(master) device. It is not safe to use the status register
READY bit to check for controller channel existence. Some
ATAPI commands (RESET, DIAGNOSTIC) may clear this bit. */
return GRUB_ERR_NONE;
}
static grub_err_t
grub_pata_device_initialize (int port, int device, int addr)
{
struct grub_pata_device *dev;
struct grub_pata_device **devp;
grub_err_t err;
grub_dprintf ("pata", "detecting device %d,%d (0x%x)\n",
port, device, addr);
dev = grub_malloc (sizeof(*dev));
if (! dev)
return grub_errno;
/* Setup the device information. */
dev->port = port;
dev->device = device;
dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE;
dev->present = 1;
dev->next = NULL;
/* Register the device. */
for (devp = &grub_pata_devices; *devp; devp = &(*devp)->next);
*devp = dev;
err = check_device (dev);
if (err)
grub_print_error ();
return 0;
}
#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
static int NESTED_FUNC_ATTR
grub_pata_pciinit (grub_pci_device_t dev,
grub_pci_id_t pciid)
{
static int compat_use[2] = { 0 };
grub_pci_address_t addr;
grub_uint32_t class;
grub_uint32_t bar1;
grub_uint32_t bar2;
int rega;
int i;
static int controller = 0;
int cs5536 = 0;
int nports = 2;
/* Read class. */
addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
class = grub_pci_read (addr);
/* AMD CS5536 Southbridge. */
if (pciid == GRUB_CS5536_PCIID)
{
cs5536 = 1;
nports = 1;
}
/* Check if this class ID matches that of a PCI IDE Controller. */
if (!cs5536 && (class >> 16 != 0x0101))
return 0;
for (i = 0; i < nports; i++)
{
/* Set to 0 when the channel operated in compatibility mode. */
int compat;
/* We don't support non-compatibility mode for CS5536. */
if (cs5536)
compat = 0;
else
compat = (class >> (8 + 2 * i)) & 1;
rega = 0;
/* If the channel is in compatibility mode, just assign the
default registers. */
if (compat == 0 && !compat_use[i])
{
rega = grub_pata_ioaddress[i];
compat_use[i] = 1;
}
else if (compat)
{
/* Read the BARs, which either contain a mmapped IO address
or the IO port address. */
addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
+ sizeof (grub_uint64_t) * i);
bar1 = grub_pci_read (addr);
addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
+ sizeof (grub_uint64_t) * i
+ sizeof (grub_uint32_t));
bar2 = grub_pci_read (addr);
/* Check if the BARs describe an IO region. */
if ((bar1 & 1) && (bar2 & 1))
{
rega = bar1 & ~3;
}
}
grub_dprintf ("pata",
"PCI dev (%d,%d,%d) compat=%d rega=0x%x\n",
grub_pci_get_bus (dev), grub_pci_get_device (dev),
grub_pci_get_function (dev), compat, rega);
if (rega)
{
grub_errno = GRUB_ERR_NONE;
grub_pata_device_initialize (controller * 2 + i, 0, rega);
/* Most errors raised by grub_ata_device_initialize() are harmless.
They just indicate this particular drive is not responding, most
likely because it doesn't exist. We might want to ignore specific
error types here, instead of printing them. */
if (grub_errno)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
}
grub_pata_device_initialize (controller * 2 + i, 1, rega);
/* Likewise. */
if (grub_errno)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
}
}
}
controller++;
return 0;
}
static grub_err_t
grub_pata_initialize (void)
{
grub_pci_iterate (grub_pata_pciinit);
return 0;
}
#else
static grub_err_t
grub_pata_initialize (void)
{
int i;
for (i = 0; i < 2; i++)
{
grub_pata_device_initialize (i, 0, grub_pata_ioaddress[i]);
grub_pata_device_initialize (i, 1, grub_pata_ioaddress[i]);
}
return 0;
}
#endif
static grub_err_t
grub_pata_open (int id, int devnum, struct grub_ata *ata)
{
struct grub_pata_device *dev;
struct grub_pata_device *devfnd = 0;
grub_err_t err;
if (id != GRUB_SCSI_SUBSYSTEM_PATA)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a PATA device");
for (dev = grub_pata_devices; dev; dev = dev->next)
{
if (dev->port * 2 + dev->device == devnum)
{
devfnd = dev;
break;
}
}
grub_dprintf ("pata", "opening PATA dev `ata%d'\n", devnum);
if (! devfnd)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such PATA device");
err = check_device (devfnd);
if (err)
return err;
ata->data = devfnd;
ata->dma = 0;
ata->present = &devfnd->present;
return GRUB_ERR_NONE;
}
static int
grub_pata_iterate (int (*hook) (int id, int bus),
grub_disk_pull_t pull)
{
struct grub_pata_device *dev;
if (pull != GRUB_DISK_PULL_NONE)
return 0;
for (dev = grub_pata_devices; dev; dev = dev->next)
if (hook (GRUB_SCSI_SUBSYSTEM_PATA, dev->port * 2 + dev->device))
return 1;
return 0;
}
static struct grub_ata_dev grub_pata_dev =
{
.iterate = grub_pata_iterate,
.open = grub_pata_open,
.readwrite = grub_pata_readwrite,
};
GRUB_MOD_INIT(ata_pthru)
{
/* To prevent two drivers operating on the same disks. */
grub_disk_firmware_is_tainted = 1;
if (grub_disk_firmware_fini)
{
grub_disk_firmware_fini ();
grub_disk_firmware_fini = NULL;
}
/* ATA initialization. */
grub_pata_initialize ();
grub_ata_dev_register (&grub_pata_dev);
}
GRUB_MOD_FINI(ata_pthru)
{
grub_ata_dev_unregister (&grub_pata_dev);
}

View file

@ -23,6 +23,7 @@
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/raid.h>
#include <grub/partition.h>
#ifdef GRUB_UTIL
#include <grub/util/misc.h>
#endif
@ -33,6 +34,11 @@ GRUB_MOD_LICENSE ("GPLv3+");
static struct grub_raid_array *array_list;
grub_raid5_recover_func_t grub_raid5_recover_func;
grub_raid6_recover_func_t grub_raid6_recover_func;
static grub_raid_t grub_raid_list;
static int inscnt = 0;
static struct grub_raid_array *
find_array (const char *name);
static char
@ -78,14 +84,102 @@ grub_is_array_readable (struct grub_raid_array *array)
return 0;
}
static grub_err_t
insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
grub_disk_addr_t start_sector, const char *scanner_name,
grub_raid_t raid __attribute__ ((unused)));
static int scan_depth = 0;
static void
scan_devices (const char *arname)
{
grub_raid_t raid;
auto int hook (const char *name);
int hook (const char *name)
{
grub_disk_t disk;
struct grub_raid_array array;
struct grub_raid_array *arr;
grub_disk_addr_t start_sector;
grub_dprintf ("raid", "Scanning for %s RAID devices on disk %s\n",
raid->name, name);
#ifdef GRUB_UTIL
grub_util_info ("Scanning for %s RAID devices on disk %s",
raid->name, name);
#endif
disk = grub_disk_open (name);
if (!disk)
return 0;
for (arr = array_list; arr != NULL; arr = arr->next)
{
struct grub_raid_member *m;
for (m = arr->members; m < arr->members + arr->nr_devs; m++)
if (m->device && m->device->id == disk->id
&& m->device->dev->id == disk->dev->id
&& grub_partition_get_start (m->device->partition)
== grub_partition_get_start (disk->partition)
&& grub_disk_get_size (m->device)
== grub_disk_get_size (disk))
{
grub_disk_close (disk);
return 0;
}
}
if ((disk->total_sectors != GRUB_ULONG_MAX) &&
(! raid->detect (disk, &array, &start_sector)) &&
(! insert_array (disk, &array, start_sector, raid->name,
raid)))
return 0;
/* This error usually means it's not raid, no need to display
it. */
if (grub_errno != GRUB_ERR_OUT_OF_RANGE)
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
grub_disk_close (disk);
if (arname && find_array (arname))
return 1;
return 0;
}
if (scan_depth)
return;
scan_depth++;
for (raid = grub_raid_list; raid; raid = raid->next)
grub_device_iterate (&hook);
scan_depth--;
}
static int
grub_raid_iterate (int (*hook) (const char *name))
grub_raid_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
struct grub_raid_array *array;
int islcnt = 0;
if (pull == GRUB_DISK_PULL_RESCAN)
{
islcnt = inscnt;
scan_devices (NULL);
}
if (pull != GRUB_DISK_PULL_NONE && pull != GRUB_DISK_PULL_RESCAN)
return 0;
for (array = array_list; array != NULL; array = array->next)
{
if (grub_is_array_readable (array))
if (grub_is_array_readable (array) && array->became_readable_at >= islcnt)
if (hook (array->name))
return 1;
}
@ -134,11 +228,10 @@ ascii2hex (char c)
return 0;
}
static grub_err_t
grub_raid_open (const char *name, grub_disk_t disk)
static struct grub_raid_array *
find_array (const char *name)
{
struct grub_raid_array *array;
unsigned n;
if (grub_memcmp (name, "mduuid/", sizeof ("mduuid/") - 1) == 0)
{
@ -155,7 +248,7 @@ grub_raid_open (const char *name, grub_disk_t disk)
if (uuid_len == (unsigned) array->uuid_len
&& grub_memcmp (uuidbin, array->uuid, uuid_len) == 0)
if (grub_is_array_readable (array))
break;
return array;
}
}
else
@ -163,8 +256,33 @@ grub_raid_open (const char *name, grub_disk_t disk)
{
if (!grub_strcmp (array->name, name))
if (grub_is_array_readable (array))
break;
return array;
}
return NULL;
}
static grub_err_t
grub_raid_open (const char *name, grub_disk_t disk)
{
struct grub_raid_array *array;
unsigned n;
if (grub_memcmp (name, "md", sizeof ("md") - 1) != 0)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown RAID device %s",
name);
array = find_array (name);
if (! array)
{
scan_devices (name);
if (grub_errno)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
}
array = find_array (name);
}
if (!array)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown RAID device %s",
@ -214,23 +332,6 @@ grub_raid_close (grub_disk_t disk __attribute ((unused)))
return;
}
void
grub_raid_block_xor (char *buf1, const char *buf2, int size)
{
grub_size_t *p1;
const grub_size_t *p2;
p1 = (grub_size_t *) buf1;
p2 = (const grub_size_t *) buf2;
size /= GRUB_CPU_SIZEOF_VOID_P;
while (size)
{
*(p1++) ^= *(p2++);
size--;
}
}
static grub_err_t
grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf)
@ -524,6 +625,7 @@ insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
grub_raid_t raid __attribute__ ((unused)))
{
struct grub_raid_array *array = 0, *p;
int was_readable = 0;
/* See whether the device is part of an array we have already seen a
device from. */
@ -534,6 +636,8 @@ insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
grub_free (new_array->uuid);
array = p;
was_readable = grub_is_array_readable (array);
/* Do some checks before adding the device to the array. */
if (new_array->index >= array->allocated_devs)
@ -679,6 +783,49 @@ insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
scanner_name);
#endif
{
int max_used_number = 0, len, need_new_name = 0;
int add_us = 0;
len = grub_strlen (array->name);
if (len && grub_isdigit (array->name[len-1]))
add_us = 1;
for (p = array_list; p != NULL; p = p->next)
{
int cur_num;
char *num, *end;
if (grub_strncmp (p->name, array->name, len) != 0)
continue;
if (p->name[len] == 0)
{
need_new_name = 1;
continue;
}
if (add_us && p->name[len] != '_')
continue;
if (add_us)
num = p->name + len + 1;
else
num = p->name + len;
if (!grub_isdigit (num[0]))
continue;
cur_num = grub_strtoull (num, &end, 10);
if (end[0])
continue;
if (cur_num > max_used_number)
max_used_number = cur_num;
}
if (need_new_name)
{
char *tmp;
tmp = grub_xasprintf ("%s%s%d", array->name, add_us ? "_" : "",
max_used_number + 1);
if (!tmp)
return grub_errno;
grub_free (array->name);
array->name = tmp;
}
}
/* Add our new array to the list. */
array->next = array_list;
array_list = array;
@ -693,12 +840,12 @@ insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
array->members[new_array->index].device = disk;
array->members[new_array->index].start_sector = start_sector;
array->nr_devs++;
if (!was_readable && grub_is_array_readable (array))
array->became_readable_at = inscnt++;
return 0;
}
static grub_raid_t grub_raid_list;
static void
free_array (void)
{
@ -729,45 +876,8 @@ free_array (void)
void
grub_raid_register (grub_raid_t raid)
{
auto int hook (const char *name);
int hook (const char *name)
{
grub_disk_t disk;
struct grub_raid_array array;
grub_disk_addr_t start_sector;
grub_dprintf ("raid", "Scanning for %s RAID devices on disk %s\n",
grub_raid_list->name, name);
#ifdef GRUB_UTIL
grub_util_info ("Scanning for %s RAID devices on disk %s",
grub_raid_list->name, name);
#endif
disk = grub_disk_open (name);
if (!disk)
return 0;
if ((disk->total_sectors != GRUB_ULONG_MAX) &&
(! grub_raid_list->detect (disk, &array, &start_sector)) &&
(! insert_array (disk, &array, start_sector, grub_raid_list->name,
grub_raid_list)))
return 0;
/* This error usually means it's not raid, no need to display
it. */
if (grub_errno != GRUB_ERR_OUT_OF_RANGE)
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
grub_disk_close (disk);
return 0;
}
raid->next = grub_raid_list;
grub_raid_list = raid;
grub_device_iterate (&hook);
}
void

View file

@ -23,6 +23,7 @@
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/raid.h>
#include <grub/crypto.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -57,7 +58,7 @@ grub_raid5_recover (struct grub_raid_array *array, int disknr,
return err;
}
grub_raid_block_xor (buf, buf2, size);
grub_crypto_xor (buf, buf2, buf2, size);
}
grub_free (buf2);

View file

@ -23,70 +23,44 @@
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/raid.h>
#include <grub/crypto.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_uint8_t raid6_table1[256][256];
static grub_uint8_t raid6_table2[256][256];
/* x**y. */
static grub_uint8_t powx[255 * 2];
/* Such an s that x**s = y */
static int powx_inv[256];
static const grub_uint8_t poly = 0x1d;
static void
grub_raid_block_mul (grub_uint8_t mul, char *buf, int size)
grub_raid_block_mulx (int mul, char *buf, int size)
{
int i;
grub_uint8_t *p;
p = (grub_uint8_t *) buf;
for (i = 0; i < size; i++, p++)
*p = raid6_table1[mul][*p];
if (*p)
*p = powx[mul + powx_inv[*p]];
}
static void
grub_raid6_init_table (void)
{
int i, j;
int i;
for (i = 0; i < 256; i++)
raid6_table1[i][1] = raid6_table1[1][i] = i;
for (i = 2; i < 256; i++)
for (j = i; j < 256; j++)
{
int n;
grub_uint8_t c;
n = i >> 1;
c = raid6_table1[n][j];
c = (c << 1) ^ ((c & 0x80) ? 0x1d : 0);
if (i & 1)
c ^= j;
raid6_table1[j][i] = raid6_table1[i][j] = c;
}
raid6_table2[0][0] = 1;
for (i = 1; i < 256; i++)
raid6_table2[i][i] = raid6_table1[raid6_table2[i - 1][i - 1]][2];
for (i = 0; i < 254; i++)
for (j = 0; j < 254; j++)
{
grub_uint8_t c, n;
int k;
if (i == j)
continue;
k = i - j;
if (k < 0)
k += 255;
c = n = raid6_table2[k][k] ^ 1;
for (k = 0; k < 253; k++)
c = raid6_table1[c][n];
raid6_table2[i][j] = raid6_table1[raid6_table2[255 - j][255 - j]][c];
}
grub_uint8_t cur = 1;
for (i = 0; i < 255; i++)
{
powx[i] = cur;
powx[i + 255] = cur;
powx_inv[cur] = i;
if (cur & 0x80)
cur = (cur << 1) ^ poly;
else
cur <<= 1;
}
}
static grub_err_t
@ -122,12 +96,12 @@ grub_raid6_recover (struct grub_raid_array *array, int disknr, int p,
{
if ((array->members[pos].device) &&
(! grub_disk_read (array->members[pos].device,
array->members[i].start_sector + sector,
array->members[pos].start_sector + sector,
0, size, buf)))
{
grub_raid_block_xor (pbuf, buf, size);
grub_raid_block_mul (raid6_table2[i][i], buf, size);
grub_raid_block_xor (qbuf, buf, size);
grub_crypto_xor (pbuf, pbuf, buf, size);
grub_raid_block_mulx (i, buf, size);
grub_crypto_xor (qbuf, qbuf, buf, size);
}
else
{
@ -154,10 +128,10 @@ grub_raid6_recover (struct grub_raid_array *array, int disknr, int p,
/* One bad device */
if ((array->members[p].device) &&
(! grub_disk_read (array->members[p].device,
array->members[i].start_sector + sector,
array->members[p].start_sector + sector,
0, size, buf)))
{
grub_raid_block_xor (buf, pbuf, size);
grub_crypto_xor (buf, buf, pbuf, size);
goto quit;
}
@ -169,17 +143,17 @@ grub_raid6_recover (struct grub_raid_array *array, int disknr, int p,
grub_errno = GRUB_ERR_NONE;
if (grub_disk_read (array->members[q].device,
array->members[i].start_sector + sector, 0, size, buf))
array->members[q].start_sector + sector, 0, size, buf))
goto quit;
grub_raid_block_xor (buf, qbuf, size);
grub_raid_block_mul (raid6_table2[255 - bad1][255 - bad1], buf,
grub_crypto_xor (buf, buf, qbuf, size);
grub_raid_block_mulx (255 - bad1, buf,
size);
}
else
{
/* Two bad devices */
grub_uint8_t c;
int c;
if ((! array->members[p].device) || (! array->members[q].device))
{
@ -188,26 +162,26 @@ grub_raid6_recover (struct grub_raid_array *array, int disknr, int p,
}
if (grub_disk_read (array->members[p].device,
array->members[i].start_sector + sector,
array->members[p].start_sector + sector,
0, size, buf))
goto quit;
grub_raid_block_xor (pbuf, buf, size);
grub_crypto_xor (pbuf, pbuf, buf, size);
if (grub_disk_read (array->members[q].device,
array->members[i].start_sector + sector,
array->members[q].start_sector + sector,
0, size, buf))
goto quit;
grub_raid_block_xor (qbuf, buf, size);
grub_crypto_xor (qbuf, qbuf, buf, size);
c = raid6_table2[bad2][bad1];
grub_raid_block_mul (c, qbuf, size);
c = (255 - bad1 + (255 - powx_inv[(powx[bad2 - bad1 + 255] ^ 1)])) % 255;
grub_raid_block_mulx (c, qbuf, size);
c = raid6_table1[raid6_table2[bad2][bad2]][c];
grub_raid_block_mul (c, pbuf, size);
c = (bad2 + c) % 255;
grub_raid_block_mulx (c, pbuf, size);
grub_raid_block_xor (pbuf, qbuf, size);
grub_crypto_xor (pbuf, pbuf, qbuf, size);
grub_memcpy (buf, pbuf, size);
}

View file

@ -32,6 +32,12 @@ GRUB_MOD_LICENSE ("GPLv3+");
static grub_scsi_dev_t grub_scsi_dev_list;
const char grub_scsi_names[GRUB_SCSI_NUM_SUBSYSTEMS][5] = {
[GRUB_SCSI_SUBSYSTEM_USBMS] = "usb",
[GRUB_SCSI_SUBSYSTEM_PATA] = "ata",
[GRUB_SCSI_SUBSYSTEM_AHCI] = "ahci"
};
void
grub_scsi_dev_register (grub_scsi_dev_t dev)
{
@ -316,13 +322,14 @@ grub_scsi_write12 (grub_disk_t disk, grub_disk_addr_t sector,
static int
grub_scsi_iterate (int (*hook) (const char *name))
grub_scsi_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
grub_scsi_dev_t p;
auto int scsi_iterate (int bus, int luns);
auto int NESTED_FUNC_ATTR scsi_iterate (int id, int bus, int luns);
int scsi_iterate (int bus, int luns)
int NESTED_FUNC_ATTR scsi_iterate (int id, int bus, int luns)
{
int i;
@ -331,7 +338,7 @@ grub_scsi_iterate (int (*hook) (const char *name))
{
char *sname;
int ret;
sname = grub_xasprintf ("%s%d", p->name, bus);
sname = grub_xasprintf ("%s%d", grub_scsi_names[id], bus);
if (!sname)
return 1;
ret = hook (sname);
@ -345,7 +352,7 @@ grub_scsi_iterate (int (*hook) (const char *name))
{
char *sname;
int ret;
sname = grub_xasprintf ("%s%d%c", p->name, bus, 'a' + i);
sname = grub_xasprintf ("%s%d%c", grub_scsi_names[id], bus, 'a' + i);
if (!sname)
return 1;
ret = hook (sname);
@ -357,7 +364,7 @@ grub_scsi_iterate (int (*hook) (const char *name))
}
for (p = grub_scsi_dev_list; p; p = p->next)
if (p->iterate && (p->iterate) (scsi_iterate))
if (p->iterate && (p->iterate) (scsi_iterate, pull))
return 1;
return 0;
@ -372,6 +379,7 @@ grub_scsi_open (const char *name, grub_disk_t disk)
int lun, bus;
grub_uint64_t maxtime;
const char *nameend;
unsigned id;
nameend = name + grub_strlen (name) - 1;
/* Try to detect a LUN ('a'-'z'), otherwise just use the first
@ -396,15 +404,25 @@ grub_scsi_open (const char *name, grub_disk_t disk)
if (! scsi)
return grub_errno;
for (id = 0; id < ARRAY_SIZE (grub_scsi_names); id++)
if (grub_strncmp (grub_scsi_names[id], name, nameend - name) == 0)
break;
if (id == ARRAY_SIZE (grub_scsi_names))
{
grub_free (scsi);
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk");
}
for (p = grub_scsi_dev_list; p; p = p->next)
{
if (grub_strncmp (p->name, name, nameend - name) != 0)
continue;
if (p->open (id, bus, scsi))
{
grub_errno = GRUB_ERR_NONE;
continue;
}
if (p->open (bus, scsi))
continue;
disk->id = grub_make_scsi_id (p->id, bus, lun);
disk->id = grub_make_scsi_id (id, bus, lun);
disk->data = scsi;
scsi->dev = p;
scsi->lun = lun;
@ -465,22 +483,26 @@ grub_scsi_open (const char *name, grub_disk_t disk)
return err;
}
/* SCSI blocks can be something else than 512, although GRUB
wants 512 byte blocks. */
disk->total_sectors = ((grub_uint64_t)scsi->size
* (grub_uint64_t)scsi->blocksize)
>> GRUB_DISK_SECTOR_BITS;
disk->total_sectors = scsi->size;
if (scsi->blocksize & (scsi->blocksize - 1) || !scsi->blocksize)
{
grub_free (scsi);
return grub_error (GRUB_ERR_IO, "invalid sector size %d",
scsi->blocksize);
}
for (disk->log_sector_size = 0;
(1 << disk->log_sector_size) < scsi->blocksize;
disk->log_sector_size++);
grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
scsi->size, scsi->blocksize);
grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n",
grub_dprintf ("scsi", "Disk total sectors = %llu\n",
(unsigned long long) disk->total_sectors);
return GRUB_ERR_NONE;
}
grub_free (scsi);
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk");
}
@ -503,36 +525,36 @@ grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector,
scsi = disk->data;
/* SCSI sectors are variable in size. GRUB uses 512 byte
sectors. */
if (scsi->blocksize != GRUB_DISK_SECTOR_SIZE)
while (size)
{
unsigned spb = scsi->blocksize >> GRUB_DISK_SECTOR_BITS;
if (spb == 0 || (scsi->blocksize & (GRUB_DISK_SECTOR_SIZE - 1)) != 0)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"unsupported SCSI block size");
/* PATA doesn't support more than 32K reads.
Not sure about AHCI and USB. If it's confirmed that either of
them can do bigger reads reliably this value can be moved to 'scsi'
structure. */
grub_size_t len = 32768 >> disk->log_sector_size;
grub_err_t err;
if (len > size)
len = size;
/* Depending on the type, select a read function. */
switch (scsi->devtype)
{
case grub_scsi_devtype_direct:
err = grub_scsi_read10 (disk, sector, len, buf);
if (err)
return err;
break;
grub_uint64_t sector_mod = 0;
sector = grub_divmod64 (sector, spb, &sector_mod);
if (! (sector_mod == 0 && size % spb == 0))
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"unaligned SCSI read not supported");
size /= spb;
case grub_scsi_devtype_cdrom:
err = grub_scsi_read12 (disk, sector, len, buf);
if (err)
return err;
break;
}
size -= len;
sector += len;
buf += len << disk->log_sector_size;
}
/* Depending on the type, select a read function. */
switch (scsi->devtype)
{
case grub_scsi_devtype_direct:
return grub_scsi_read10 (disk, sector, size, buf);
case grub_scsi_devtype_cdrom:
return grub_scsi_read12 (disk, sector, size, buf);
}
/* XXX: Never reached. */
return GRUB_ERR_NONE;
#if 0 /* Workaround - it works - but very slowly, from some reason

View file

@ -211,16 +211,20 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno)
static int
grub_usbms_iterate (int (*hook) (int bus, int luns))
grub_usbms_iterate (int NESTED_FUNC_ATTR (*hook) (int id, int bus, int luns),
grub_disk_pull_t pull)
{
unsigned i;
if (pull != GRUB_DISK_PULL_NONE)
return 0;
grub_usb_poll_devices ();
for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++)
if (grub_usbms_devices[i])
{
if (hook (i, grub_usbms_devices[i]->luns))
if (hook (GRUB_SCSI_SUBSYSTEM_USBMS, i, grub_usbms_devices[i]->luns))
return 1;
}
@ -390,8 +394,12 @@ grub_usbms_write (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
}
static grub_err_t
grub_usbms_open (int devnum, struct grub_scsi *scsi)
grub_usbms_open (int id, int devnum, struct grub_scsi *scsi)
{
if (id != GRUB_SCSI_SUBSYSTEM_USBMS)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
"not USB Mass Storage device");
grub_usb_poll_devices ();
if (!grub_usbms_devices[devnum])
@ -406,8 +414,6 @@ grub_usbms_open (int devnum, struct grub_scsi *scsi)
static struct grub_scsi_dev grub_usbms_dev =
{
.name = "usb",
.id = GRUB_SCSI_SUBSYSTEM_USBMS,
.iterate = grub_usbms_iterate,
.open = grub_usbms_open,
.read = grub_usbms_read,