From 7e8b77c03359b2537dee39ded103b2f126157dd8 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 19 Jul 2010 21:22:54 +0200 Subject: [PATCH 01/49] AHCI skeleton --- conf/i386.rmk | 5 + disk/ahci.c | 311 ++++++++++++++++++++++++++++++++++++++++++++ include/grub/scsi.h | 3 +- 3 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 disk/ahci.c diff --git a/conf/i386.rmk b/conf/i386.rmk index b1df584a6..54edb468b 100644 --- a/conf/i386.rmk +++ b/conf/i386.rmk @@ -36,6 +36,11 @@ ata_mod_SOURCES = disk/ata.c ata_mod_CFLAGS = $(COMMON_CFLAGS) ata_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES += ahci.mod +ahci_mod_SOURCES = disk/ahci.c +ahci_mod_CFLAGS = $(COMMON_CFLAGS) +ahci_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For setpci.mod pkglib_MODULES += setpci.mod setpci_mod_SOURCES = commands/setpci.c diff --git a/disk/ahci.c b/disk/ahci.c new file mode 100644 index 000000000..1372fd651 --- /dev/null +++ b/disk/ahci.c @@ -0,0 +1,311 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +struct grub_ahci_cmd_head +{ + grub_uint32_t unused[8]; +}; + +struct grub_ahci_hba_port +{ + grub_uint64_t command_list_base; + grub_uint32_t unused[12]; + grub_uint32_t sata_active; + grub_uint32_t command_issue; + grub_uint32_t unused[16]; +}; + +struct grub_ahci_hba +{ + grub_uint32_t cap; +#define GRUB_AHCI_HBA_CAP_MASK 0x1f + grub_uint32_t global_control; +#define GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN 0x80000000 + grub_uint32_t ports_implemented; + grub_uint32_t unused1[7]; + grub_uint32_t bios_handoff; +#define GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED 1 +#define GRUB_AHCI_BIOS_HANDOFF_OS_OWNED 2 +#define GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED 8 +#define GRUB_AHCI_BIOS_HANDOFF_RWC 8 + grub_uint32_t unused2[53]; + struct grub_ahci_hba_port ports[32]; +}; + +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; +}; + +static struct grub_ahci_device *grub_ahci_devices; +static int numdevs; + +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; + int 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)); + + hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN; + + nports = hba->cap & GRUB_AHCI_HBA_CAP_MASK; + + 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"); + + for (i = 0; i < nports; i++) + { + struct grub_ahci_device *adev; + struct grub_pci_dma_chunk *command_list; + + if (!(hba->ports_implemented & (1 << i))) + continue; + + command_list = grub_memalign_dma32 (1024, + sizeof (struct grub_ahci_cmd_head)); + if (!command_list) + return 1; + + adev = grub_malloc (sizeof (*adev)); + if (!adev) + { + grub_dma_free (command_list); + return 1; + } + + adev->hba = hba; + adev->port = i; + adev->num = numdevs++; + adev->command_list_chunk = command_list; + adev->command_list = grub_dma_get_virt (command_list); + adev->hba->ports[i].command_list_base = grub_dma_get_phys (command_list); + 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 int +grub_ahci_iterate (int (*hook) (int bus, int luns)) +{ + struct grub_ahci_device *dev; + + FOR_LIST_ELEMENTS(dev, grub_ahci_devices) + if (hook (dev->num, 1)) + 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 + +static grub_err_t +grub_ahci_packet (struct grub_ahci_device *dev, char *packet, + grub_size_t size) +{ + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_ahci_read (struct grub_scsi *scsi, + grub_size_t cmdsize __attribute__((unused)), + char *cmd, grub_size_t size, char *buf) +{ + struct grub_ahci_device *dev = (struct grub_ahci_device *) scsi->data; + + grub_dprintf("ahci", "grub_ahci_read (size=%llu)\n", (unsigned long long) size); + + if (grub_atapi_packet (dev, cmd, size)) + return grub_errno; + + grub_size_t nread = 0; + while (nread < size) + { + /* Wait for !BSY, DRQ, I/O, !C/D. */ + if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_DATA_IN, GRUB_ATA_TOUT_DATA)) + return grub_errno; + + /* Get byte count for this DRQ assertion. */ + unsigned cnt = grub_ata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8 + | grub_ata_regget (dev, GRUB_ATAPI_REG_CNTLOW); + grub_dprintf("ata", "DRQ count=%u\n", cnt); + + /* Count of last transfer may be uneven. */ + if (! (0 < cnt && cnt <= size - nread && (! (cnt & 1) || cnt == size - nread))) + return grub_error (GRUB_ERR_READ_ERROR, "invalid ATAPI transfer count"); + + /* Read the data. */ + grub_ata_pio_read (dev, buf + nread, cnt); + + if (cnt & 1) + buf[nread + cnt - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress + GRUB_ATA_REG_DATA)); + + nread += cnt; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_ahci_write (struct grub_scsi *scsi __attribute__((unused)), + grub_size_t cmdsize __attribute__((unused)), + char *cmd __attribute__((unused)), + grub_size_t size __attribute__((unused)), + char *buf __attribute__((unused))) +{ + // XXX: scsi.mod does not use write yet. + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "AHCI write not implemented"); +} + +static grub_err_t +grub_ahci_open (int devnum, struct grub_scsi *scsi) +{ + struct grub_ahci_device *dev; + + FOR_LIST_ELEMENTS(dev, grub_ahci_devices) + { + if (dev->num == devnum) + break; + } + + grub_dprintf ("ata", "opening AHCI dev `ahci%d'\n", devnum); + + if (! dev) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device"); + + scsi->data = dev; + + return GRUB_ERR_NONE; +} + + +static struct grub_scsi_dev grub_ahci_dev = + { + .name = "ahci", + .id = GRUB_SCSI_SUBSYSTEM_AHCI, + .iterate = grub_ahci_iterate, + .open = grub_ahci_open, + .read = grub_ahci_read, + .write = grub_ahci_write + }; + + + +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_scsi_dev_register (&grub_ahci_dev); +} + +GRUB_MOD_FINI(ahci) +{ + grub_scsi_dev_unregister (&grub_ahci_dev); +} diff --git a/include/grub/scsi.h b/include/grub/scsi.h index b3c60f3e8..289cd8e4a 100644 --- a/include/grub/scsi.h +++ b/include/grub/scsi.h @@ -29,7 +29,8 @@ struct grub_scsi; enum { GRUB_SCSI_SUBSYSTEM_USBMS, - GRUB_SCSI_SUBSYSTEM_ATAPI + GRUB_SCSI_SUBSYSTEM_ATAPI, + GRUB_SCSI_SUBSYSTEM_AHCI }; #define GRUB_SCSI_ID_SUBSYSTEM_SHIFT 24 From c7336d912c656c4b7c7162e8dee6f5001e732be5 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Dec 2010 16:07:53 +0100 Subject: [PATCH 02/49] splitting generic ata from pata. --- commands/hdparm.c | 95 +++-- disk/ahci.c | 289 ++++++++++----- disk/ata.c | 829 ++++++++++++++------------------------------ disk/ata_pthru.c | 520 ++++++++++++++++++++++++--- disk/scsi.c | 38 +- disk/usbms.c | 12 +- include/grub/ata.h | 122 ++++--- include/grub/disk.h | 13 +- include/grub/scsi.h | 16 +- kern/disk.c | 4 - 10 files changed, 1095 insertions(+), 843 deletions(-) diff --git a/commands/hdparm.c b/commands/hdparm.c index a3f8bbff0..02ae8f36b 100644 --- a/commands/hdparm.c +++ b/commands/hdparm.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -61,60 +62,60 @@ enum grub_ata_smart_commands static int quiet = 0; static grub_err_t -grub_hdparm_do_ata_cmd (grub_disk_t disk, grub_uint8_t cmd, +grub_hdparm_do_ata_cmd (grub_ata_t ata, grub_uint8_t cmd, grub_uint8_t features, grub_uint8_t sectors, void * buffer, int size) { struct grub_disk_ata_pass_through_parms apt; grub_memset (&apt, 0, sizeof (apt)); - apt.taskfile[GRUB_ATA_REG_CMD] = cmd; - apt.taskfile[GRUB_ATA_REG_FEATURES] = features; - apt.taskfile[GRUB_ATA_REG_SECTORS] = sectors; + apt.taskfile.cmd = cmd; + apt.taskfile.features = features; + apt.taskfile.sectors = sectors; apt.buffer = buffer; apt.size = size; - if (grub_disk_ata_pass_through (disk, &apt)) + if (ata->dev->readwrite (ata, &apt)) return grub_errno; return GRUB_ERR_NONE; } static int -grub_hdparm_do_check_powermode_cmd (grub_disk_t disk) +grub_hdparm_do_check_powermode_cmd (grub_ata_t ata) { struct grub_disk_ata_pass_through_parms apt; grub_memset (&apt, 0, sizeof (apt)); - apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_CHECK_POWER_MODE; + apt.taskfile.cmd = GRUB_ATA_CMD_CHECK_POWER_MODE; - if (grub_disk_ata_pass_through (disk, &apt)) + if (ata->dev->readwrite (ata, &apt)) return -1; - return apt.taskfile[GRUB_ATA_REG_SECTORS]; + return apt.taskfile.sectors; } static int -grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features) +grub_hdparm_do_smart_cmd (grub_ata_t ata, grub_uint8_t features) { struct grub_disk_ata_pass_through_parms apt; grub_memset (&apt, 0, sizeof (apt)); - apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_SMART; - apt.taskfile[GRUB_ATA_REG_FEATURES] = features; - apt.taskfile[GRUB_ATA_REG_LBAMID] = 0x4f; - apt.taskfile[GRUB_ATA_REG_LBAHIGH] = 0xc2; + apt.taskfile.cmd = GRUB_ATA_CMD_SMART; + apt.taskfile.features = features; + apt.taskfile.lba_mid = 0x4f; + apt.taskfile.lba_high = 0xc2; - if (grub_disk_ata_pass_through (disk, &apt)) + if (ata->dev->readwrite (ata, &apt)) return -1; if (features == GRUB_ATA_FEAT_SMART_STATUS) { - if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0x4f - && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0xc2) + if ( apt.taskfile.lba_mid == 0x4f + && apt.taskfile.lba_high == 0xc2) return 0; /* Good SMART status. */ - else if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0xf4 - && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0x2c) + else if ( apt.taskfile.lba_mid == 0xf4 + && apt.taskfile.lba_high == 0x2c) return 1; /* Bad SMART status. */ else return -1; @@ -124,12 +125,12 @@ grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features) static grub_err_t grub_hdparm_simple_cmd (const char * msg, - grub_disk_t disk, grub_uint8_t cmd) + grub_ata_t ata, grub_uint8_t cmd) { if (! quiet && msg) grub_printf ("%s", msg); - grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, 0, 0, NULL, 0); + grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, 0, 0, NULL, 0); if (! quiet && msg) grub_printf ("%s\n", ! err ? "" : ": not supported"); @@ -138,7 +139,7 @@ grub_hdparm_simple_cmd (const char * msg, static grub_err_t grub_hdparm_set_val_cmd (const char * msg, int val, - grub_disk_t disk, grub_uint8_t cmd, + grub_ata_t ata, grub_uint8_t cmd, grub_uint8_t features, grub_uint8_t sectors) { if (! quiet && msg && *msg) @@ -149,7 +150,7 @@ grub_hdparm_set_val_cmd (const char * msg, int val, grub_printf ("Disable %s", msg); } - grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, features, sectors, + grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, features, sectors, NULL, 0); if (! quiet && msg) @@ -273,6 +274,7 @@ static grub_err_t grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? { struct grub_arg_list *state = cmd->state; + struct grub_ata *ata; /* Check command line. */ if (argc != 1) @@ -283,9 +285,6 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? return grub_error (GRUB_ERR_BAD_ARGUMENT, "argument is not a device name"); args[0][len - 1] = 0; - if (! grub_disk_ata_pass_through) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "ATA pass through not available"); - int i = 0; int apm = get_int_arg (&state[i++]); int power = state[i++].set; @@ -311,15 +310,37 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed"); } + switch (disk->dev->id) + { + case GRUB_DISK_DEVICE_ATA_ID: + ata = disk->data; + break; + case GRUB_DISK_DEVICE_SCSI_ID: + if (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF) + == GRUB_SCSI_SUBSYSTEM_PATA + || (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF) + == GRUB_SCSI_SUBSYSTEM_AHCI)) + { + ata = ((struct grub_scsi *) disk->data)->data; + break; + } + default: + return grub_error (GRUB_ERR_IO, "not an ATA device"); + } + + /* Change settings. */ if (aam >= 0) grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1), - disk, GRUB_ATA_CMD_SET_FEATURES, (aam ? 0x42 : 0xc2), aam); + ata, GRUB_ATA_CMD_SET_FEATURES, + (aam ? 0x42 : 0xc2), aam); if (apm >= 0) grub_hdparm_set_val_cmd ("Advanced Power Management", - (apm != 255 ? apm : -1), disk, GRUB_ATA_CMD_SET_FEATURES, - (apm != 255 ? 0x05 : 0x85), (apm != 255 ? apm : 0)); + (apm != 255 ? apm : -1), ata, + GRUB_ATA_CMD_SET_FEATURES, + (apm != 255 ? 0x05 : 0x85), + (apm != 255 ? apm : 0)); if (standby_tout >= 0) { @@ -330,28 +351,28 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? grub_printf (")"); } /* The IDLE cmd sets disk to idle mode and configures standby timer. */ - grub_hdparm_set_val_cmd ("", -1, disk, GRUB_ATA_CMD_IDLE, 0, standby_tout); + grub_hdparm_set_val_cmd ("", -1, ata, GRUB_ATA_CMD_IDLE, 0, standby_tout); } if (enable_smart >= 0) { if (! quiet) grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis")); - int err = grub_hdparm_do_smart_cmd (disk, (enable_smart ? + int err = grub_hdparm_do_smart_cmd (ata, (enable_smart ? GRUB_ATA_FEAT_SMART_ENABLE : GRUB_ATA_FEAT_SMART_DISABLE)); if (! quiet) grub_printf ("%s\n", err ? ": not supported" : ""); } if (sec_freeze) - grub_hdparm_simple_cmd ("Freeze security settings", disk, + grub_hdparm_simple_cmd ("Freeze security settings", ata, GRUB_ATA_CMD_SECURITY_FREEZE_LOCK); /* Print/dump IDENTIFY. */ if (ident || dumpid) { char buf[GRUB_DISK_SECTOR_SIZE]; - if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_IDENTIFY_DEVICE, + if (grub_hdparm_do_ata_cmd (ata, GRUB_ATA_CMD_IDENTIFY_DEVICE, 0, 0, buf, sizeof (buf))) grub_printf ("Cannot read ATA IDENTIFY data\n"); else @@ -367,7 +388,7 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? if (power) { grub_printf ("Disk power mode is: "); - int mode = grub_hdparm_do_check_powermode_cmd (disk); + int mode = grub_hdparm_do_check_powermode_cmd (ata); if (mode < 0) grub_printf ("unknown\n"); else @@ -383,7 +404,7 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? { if (! quiet) grub_printf ("SMART status is: "); - int err = grub_hdparm_do_smart_cmd (disk, GRUB_ATA_FEAT_SMART_STATUS); + int err = grub_hdparm_do_smart_cmd (ata, GRUB_ATA_FEAT_SMART_STATUS); if (! quiet) grub_printf ("%s\n", (err < 0 ? "unknown" : err == 0 ? "OK" : "*BAD*")); @@ -392,11 +413,11 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? /* Change power mode. */ if (standby_now) - grub_hdparm_simple_cmd ("Set disk to standby mode", disk, + grub_hdparm_simple_cmd ("Set disk to standby mode", ata, GRUB_ATA_CMD_STANDBY_IMMEDIATE); if (sleep_now) - grub_hdparm_simple_cmd ("Set disk to sleep mode", disk, + grub_hdparm_simple_cmd ("Set disk to sleep mode", ata, GRUB_ATA_CMD_SLEEP); grub_disk_close (disk); diff --git a/disk/ahci.c b/disk/ahci.c index 1372fd651..946db82e1 100644 --- a/disk/ahci.c +++ b/disk/ahci.c @@ -21,39 +21,79 @@ #include #include #include +#include #include +#include +#include struct grub_ahci_cmd_head { - grub_uint32_t unused[8]; + 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[16]; + grub_uint32_t reserved[0xc]; + struct grub_ahci_prdt_entry prdt[1]; }; struct grub_ahci_hba_port { grub_uint64_t command_list_base; - grub_uint32_t unused[12]; + grub_uint64_t fis_base; + grub_uint32_t intstatus; + grub_uint32_t inten; + grub_uint32_t command; + grub_uint32_t unused1[6]; grub_uint32_t sata_active; grub_uint32_t command_issue; - grub_uint32_t unused[16]; + grub_uint32_t unused2[17]; }; struct grub_ahci_hba { grub_uint32_t cap; -#define GRUB_AHCI_HBA_CAP_MASK 0x1f grub_uint32_t global_control; -#define GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN 0x80000000 + grub_uint32_t intr_status; grub_uint32_t ports_implemented; - grub_uint32_t unused1[7]; + grub_uint32_t unused1[6]; grub_uint32_t bios_handoff; -#define GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED 1 -#define GRUB_AHCI_BIOS_HANDOFF_OS_OWNED 2 -#define GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED 8 -#define GRUB_AHCI_BIOS_HANDOFF_RWC 8 grub_uint32_t unused2[53]; struct grub_ahci_hba_port ports[32]; }; +enum + { + GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f + }; + +enum + { + 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; @@ -62,19 +102,41 @@ struct grub_ahci_device 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; }; +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 NESTED_FUNC_ATTR grub_ahci_pciinit (grub_pci_device_t dev, - grub_pci_id_t pciid __attribute__ ((unused))) + grub_pci_id_t pciid __attribute__ ((unused))) { grub_pci_address_t addr; grub_uint32_t class; grub_uint32_t bar; - int nports; + unsigned i, nports; volatile struct grub_ahci_hba *hba; /* Read class. */ @@ -88,8 +150,8 @@ grub_ahci_pciinit (grub_pci_device_t dev, 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) + 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; @@ -98,7 +160,7 @@ grub_ahci_pciinit (grub_pci_device_t dev, hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN; - nports = hba->cap & GRUB_AHCI_HBA_CAP_MASK; + nports = (hba->cap & GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1; if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED)) { @@ -122,11 +184,13 @@ grub_ahci_pciinit (grub_pci_device_t dev, } else grub_dprintf ("ahci", "AHCI is already in OS mode\n"); + grub_dprintf ("ahci", "%d AHCI ports\n", nports); for (i = 0; i < nports; i++) { struct grub_ahci_device *adev; struct grub_pci_dma_chunk *command_list; + struct grub_pci_dma_chunk *command_table; if (!(hba->ports_implemented & (1 << i))) continue; @@ -136,18 +200,34 @@ grub_ahci_pciinit (grub_pci_device_t dev, if (!command_list) return 1; - adev = grub_malloc (sizeof (*adev)); - if (!adev) + command_table = grub_memalign_dma32 (1024, + sizeof (struct grub_ahci_cmd_table)); + if (!command_table) { grub_dma_free (command_list); return 1; } + adev = grub_malloc (sizeof (*adev)); + if (!adev) + { + grub_dma_free (command_list); + grub_dma_free (command_table); + return 1; + } + + grub_dprintf ("ahci", "found device ahci%d (port %d)\n", numdevs, i); + adev->hba = hba; adev->port = i; adev->num = numdevs++; adev->command_list_chunk = command_list; adev->command_list = grub_dma_get_virt (command_list); + adev->command_table_chunk = command_table; + adev->command_table = grub_dma_get_virt (command_table); + adev->command_list->command_table_base + = grub_dma_get_phys (command_table); + adev->hba->ports[i].command_list_base = grub_dma_get_phys (command_list); grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices), GRUB_AS_LIST (adev)); @@ -167,12 +247,12 @@ grub_ahci_initialize (void) static int -grub_ahci_iterate (int (*hook) (int bus, int luns)) +grub_ahci_iterate (int (*hook) (int id, int bus)) { struct grub_ahci_device *dev; FOR_LIST_ELEMENTS(dev, grub_ahci_devices) - if (hook (dev->num, 1)) + if (hook (GRUB_SCSI_SUBSYSTEM_AHCI, dev->num)) return 1; return 0; @@ -195,95 +275,130 @@ find_free_cmd_slot (struct grub_ahci_device *dev) } #endif -static grub_err_t -grub_ahci_packet (struct grub_ahci_device *dev, char *packet, - grub_size_t size) +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 (grub_ata_t disk, + struct grub_disk_ata_pass_through_parms *parms) { + struct grub_ahci_device *dev = (struct grub_ahci_device *) disk->data; + struct grub_pci_dma_chunk *bufc; + grub_uint64_t endtime; + unsigned i; + + 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)); + + /* FIXME: support port multipliers. */ + dev->command_list[0].config + = (4 << 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); + 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; + for (i = 0; i < sizeof (parms->taskfile.raw); i++) + dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i]; + + 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; + + if (parms->write) + grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size); + + grub_dprintf ("ahci", "AHCI command schedulded\n"); + dev->hba->ports[dev->port].inten = (1 << 5); + dev->hba->ports[dev->port].intstatus = (1 << 5); + dev->hba->ports[dev->port].command_issue |= 1; + dev->hba->ports[dev->port].command |= 1; + + endtime = grub_get_time_ms () + 1000; + while (!(dev->hba->ports[dev->port].intstatus & (1 << 5))) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "AHCI timeout\n"); + dev->hba->ports[dev->port].command &= ~1; + /* FIXME: free resources. */ + return grub_error (GRUB_ERR_IO, "AHCI transfer timeouted"); + } + + grub_dprintf ("ahci", "AHCI command completed succesfully\n"); + dev->hba->ports[dev->port].command &= ~1; + + if (!parms->write) + grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size); + grub_dma_free (bufc); return GRUB_ERR_NONE; } static grub_err_t -grub_ahci_read (struct grub_scsi *scsi, - grub_size_t cmdsize __attribute__((unused)), - char *cmd, grub_size_t size, char *buf) -{ - struct grub_ahci_device *dev = (struct grub_ahci_device *) scsi->data; - - grub_dprintf("ahci", "grub_ahci_read (size=%llu)\n", (unsigned long long) size); - - if (grub_atapi_packet (dev, cmd, size)) - return grub_errno; - - grub_size_t nread = 0; - while (nread < size) - { - /* Wait for !BSY, DRQ, I/O, !C/D. */ - if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_DATA_IN, GRUB_ATA_TOUT_DATA)) - return grub_errno; - - /* Get byte count for this DRQ assertion. */ - unsigned cnt = grub_ata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8 - | grub_ata_regget (dev, GRUB_ATAPI_REG_CNTLOW); - grub_dprintf("ata", "DRQ count=%u\n", cnt); - - /* Count of last transfer may be uneven. */ - if (! (0 < cnt && cnt <= size - nread && (! (cnt & 1) || cnt == size - nread))) - return grub_error (GRUB_ERR_READ_ERROR, "invalid ATAPI transfer count"); - - /* Read the data. */ - grub_ata_pio_read (dev, buf + nread, cnt); - - if (cnt & 1) - buf[nread + cnt - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress + GRUB_ATA_REG_DATA)); - - nread += cnt; - } - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_ahci_write (struct grub_scsi *scsi __attribute__((unused)), - grub_size_t cmdsize __attribute__((unused)), - char *cmd __attribute__((unused)), - grub_size_t size __attribute__((unused)), - char *buf __attribute__((unused))) -{ - // XXX: scsi.mod does not use write yet. - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "AHCI write not implemented"); -} - -static grub_err_t -grub_ahci_open (int devnum, struct grub_scsi *scsi) +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; } - grub_dprintf ("ata", "opening AHCI dev `ahci%d'\n", devnum); - if (! dev) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device"); - scsi->data = dev; + grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num); + + ata->data = dev; return GRUB_ERR_NONE; } - -static struct grub_scsi_dev grub_ahci_dev = +static struct grub_ata_dev grub_ahci_dev = { - .name = "ahci", - .id = GRUB_SCSI_SUBSYSTEM_AHCI, .iterate = grub_ahci_iterate, .open = grub_ahci_open, - .read = grub_ahci_read, - .write = grub_ahci_write + .readwrite = grub_ahci_readwrite, }; @@ -291,8 +406,8 @@ static struct grub_scsi_dev grub_ahci_dev = 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_is_tainted = 1; + if (0 && grub_disk_firmware_fini) { grub_disk_firmware_fini (); grub_disk_firmware_fini = NULL; @@ -302,10 +417,10 @@ GRUB_MOD_INIT(ahci) grub_ahci_initialize (); /* AHCI devices are handled by scsi.mod. */ - grub_scsi_dev_register (&grub_ahci_dev); + grub_ata_dev_register (&grub_ahci_dev); } GRUB_MOD_FINI(ahci) { - grub_scsi_dev_unregister (&grub_ahci_dev); + grub_ata_dev_unregister (&grub_ahci_dev); } diff --git a/disk/ata.c b/disk/ata.c index cfd58a6d5..3d9de23dd 100644 --- a/disk/ata.c +++ b/disk/ata.c @@ -21,76 +21,9 @@ #include #include #include -#include -#include #include -#include -/* At the moment, only two IDE ports are supported. */ -static const grub_port_t grub_ata_ioaddress[] = { GRUB_ATA_CH0_PORT1, - GRUB_ATA_CH1_PORT1 }; -static const grub_port_t grub_ata_ioaddress2[] = { GRUB_ATA_CH0_PORT2, - GRUB_ATA_CH1_PORT2 }; - -static struct grub_ata_device *grub_ata_devices; - -/* Wait for !BSY. */ -grub_err_t -grub_ata_wait_not_busy (struct grub_ata_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_ata_regget (dev, GRUB_ATA_REG_STATUS)) - & GRUB_ATA_STATUS_BUSY) - { - if (i >= milliseconds) - { - grub_dprintf ("ata", "timeout: %dms, status=0x%x\n", - milliseconds, sts); - return grub_error (GRUB_ERR_TIMEOUT, "ATA timeout"); - } - - grub_millisleep (1); - i++; - } - - return GRUB_ERR_NONE; -} - -static inline void -grub_ata_wait (void) -{ - grub_millisleep (50); -} - -/* Wait for !BSY, DRQ. */ -grub_err_t -grub_ata_wait_drq (struct grub_ata_device *dev, int rw, - int milliseconds) -{ - if (grub_ata_wait_not_busy (dev, milliseconds)) - return grub_errno; - - /* !DRQ implies error condition. */ - grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) - != GRUB_ATA_STATUS_DRQ) - { - grub_dprintf ("ata", "ata error: status=0x%x, error=0x%x\n", - sts, grub_ata_regget (dev, GRUB_ATA_REG_ERROR)); - if (! rw) - return grub_error (GRUB_ERR_READ_ERROR, "ATA read error"); - else - return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error"); - } - - return GRUB_ERR_NONE; -} +static grub_ata_dev_t grub_ata_dev_list; /* Byteorder has to be changed before strings can be read. */ static void @@ -105,30 +38,8 @@ grub_ata_strncpy (char *dst, char *src, grub_size_t len) dst[len] = '\0'; } -void -grub_ata_pio_read (struct grub_ata_device *dev, char *buf, grub_size_t size) -{ - grub_uint16_t *buf16 = (grub_uint16_t *) buf; - unsigned int i; - - /* Read in the data, word by word. */ - for (i = 0; i < size / 2; i++) - buf16[i] = grub_le_to_cpu16 (grub_inw(dev->ioaddress + GRUB_ATA_REG_DATA)); -} - static void -grub_ata_pio_write (struct grub_ata_device *dev, char *buf, grub_size_t size) -{ - grub_uint16_t *buf16 = (grub_uint16_t *) buf; - unsigned int i; - - /* Write the data, word by word. */ - for (i = 0; i < size / 2; i++) - grub_outw(grub_cpu_to_le16 (buf16[i]), dev->ioaddress + GRUB_ATA_REG_DATA); -} - -static void -grub_ata_dumpinfo (struct grub_ata_device *dev, char *info) +grub_ata_dumpinfo (struct grub_ata *dev, char *info) { char text[41]; @@ -148,31 +59,27 @@ grub_ata_dumpinfo (struct grub_ata_device *dev, char *info) } static grub_err_t -grub_atapi_identify (struct grub_ata_device *dev) +grub_atapi_identify (struct grub_ata *dev) { + struct grub_disk_ata_pass_through_parms parms; char *info; + grub_err_t err; info = grub_malloc (GRUB_DISK_SECTOR_SIZE); if (! info) return grub_errno; - grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4); - grub_ata_wait (); - if (grub_ata_check_ready (dev)) - { - grub_free (info); - return grub_errno; - } + grub_memset (&parms, 0, sizeof (parms)); + parms.taskfile.disk = 0; + parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE; - grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE); - grub_ata_wait (); + err = dev->dev->readwrite (dev, &parms); + if (err) + return err; - if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD)) - { - grub_free (info); - return grub_errno; - } - grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE); + if (parms.size != GRUB_DISK_SECTOR_SIZE) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "device cannot be identified"); dev->atapi = 1; @@ -184,96 +91,35 @@ grub_atapi_identify (struct grub_ata_device *dev) } static grub_err_t -grub_atapi_wait_drq (struct grub_ata_device *dev, - grub_uint8_t ireason, - int milliseconds) -{ - /* Wait for !BSY, DRQ, ireason */ - if (grub_ata_wait_not_busy (dev, milliseconds)) - return grub_errno; - - grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - grub_uint8_t irs = grub_ata_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) == ireason) - return GRUB_ERR_NONE; - - /* !DRQ implies error condition. */ - grub_dprintf ("ata", "atapi error: status=0x%x, ireason=0x%x, error=0x%x\n", - sts, irs, grub_ata_regget (dev, GRUB_ATA_REG_ERROR)); - - if (! (sts & GRUB_ATA_STATUS_DRQ) - && (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_ERROR) - { - if (ireason == GRUB_ATAPI_IREASON_CMD_OUT) - return grub_error (GRUB_ERR_READ_ERROR, "ATA PACKET command error"); - else - return grub_error (GRUB_ERR_READ_ERROR, "ATAPI read error"); - } - - return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error"); -} - -static grub_err_t -grub_atapi_packet (struct grub_ata_device *dev, char *packet, - grub_size_t size) -{ - grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4); - if (grub_ata_check_ready (dev)) - return grub_errno; - - /* Send ATA PACKET command. */ - grub_ata_regset (dev, GRUB_ATA_REG_FEATURES, 0); - grub_ata_regset (dev, GRUB_ATAPI_REG_IREASON, 0); - grub_ata_regset (dev, GRUB_ATAPI_REG_CNTHIGH, size >> 8); - grub_ata_regset (dev, GRUB_ATAPI_REG_CNTLOW, size & 0xFF); - - grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_PACKET); - - /* Wait for !BSY, DRQ, !I/O, C/D. */ - if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_CMD_OUT, GRUB_ATA_TOUT_STD)) - return grub_errno; - - /* Write the packet. */ - grub_ata_pio_write (dev, packet, 12); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_ata_identify (struct grub_ata_device *dev) +grub_ata_identify (struct grub_ata *dev) { + struct grub_disk_ata_pass_through_parms parms; char *info; grub_uint16_t *info16; + grub_err_t err; info = grub_malloc (GRUB_DISK_SECTOR_SIZE); if (! info) return grub_errno; info16 = (grub_uint16_t *) info; + grub_memset (&parms, 0, sizeof (parms)); + parms.buffer = info; + parms.taskfile.disk = 0; - grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4); - grub_ata_wait (); - if (grub_ata_check_ready (dev)) + parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE; + + err = dev->dev->readwrite (dev, &parms); + if (err) + return err; + + if (parms.size != GRUB_DISK_SECTOR_SIZE) { + grub_uint8_t sts = parms.taskfile.status; grub_free (info); - return grub_errno; - } - - grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_DEVICE); - grub_ata_wait (); - - if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD)) - { - grub_free (info); - grub_errno = GRUB_ERR_NONE; - grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR - && (grub_ata_regget (dev, GRUB_ATA_REG_ERROR) & 0x04 /* ABRT */)) + && (parms.taskfile.error & 0x04 /* ABRT */)) /* Device without ATA IDENTIFY, try ATAPI. */ return grub_atapi_identify (dev); @@ -287,19 +133,6 @@ grub_ata_identify (struct grub_ata_device *dev) "device cannot be identified"); } - grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE); - - /* Re-check status to avoid bogus identify data due to stuck DRQ. */ - grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - if (sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) - { - grub_dprintf ("ata", "bad status=0x%x\n", sts); - grub_free (info); - /* No device, return error but don't print message. */ - grub_errno = GRUB_ERR_NONE; - return GRUB_ERR_UNKNOWN_DEVICE; - } - /* Now it is certain that this is not an ATAPI device. */ dev->atapi = 0; @@ -335,196 +168,12 @@ grub_ata_identify (struct grub_ata_device *dev) } static grub_err_t -check_device (struct grub_ata_device *dev) -{ - grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4); - grub_ata_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_ata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A); - grub_ata_wait (); - grub_uint8_t sec = grub_ata_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. */ - - /* Use the IDENTIFY DEVICE command to query the device. */ - return grub_ata_identify (dev); -} - -static grub_err_t -grub_ata_device_initialize (int port, int device, int addr, int addr2) -{ - struct grub_ata_device *dev; - struct grub_ata_device **devp; - grub_err_t err; - - grub_dprintf ("ata", "detecting device %d,%d (0x%x, 0x%x)\n", - port, device, addr, addr2); - - 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->ioaddress2 = addr2 + GRUB_MACHINE_PCI_IO_BASE; - dev->next = NULL; - - /* Register the device. */ - for (devp = &grub_ata_devices; *devp; devp = &(*devp)->next); - *devp = dev; - - err = check_device (dev); - if (err) - grub_print_error (); - - return 0; -} - -static int NESTED_FUNC_ATTR -grub_ata_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 regb; - 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; - regb = 0; - - /* If the channel is in compatibility mode, just assign the - default registers. */ - if (compat == 0 && !compat_use[i]) - { - rega = grub_ata_ioaddress[i]; - regb = grub_ata_ioaddress2[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; - regb = bar2 & ~3; - } - } - - grub_dprintf ("ata", - "PCI dev (%d,%d,%d) compat=%d rega=0x%x regb=0x%x\n", - grub_pci_get_bus (dev), grub_pci_get_device (dev), - grub_pci_get_function (dev), compat, rega, regb); - - if (rega && regb) - { - grub_errno = GRUB_ERR_NONE; - grub_ata_device_initialize (controller * 2 + i, 0, rega, regb); - - /* 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_ata_device_initialize (controller * 2 + i, 1, rega, regb); - - /* Likewise. */ - if (grub_errno) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - } - } - } - - controller++; - - return 0; -} - -static grub_err_t -grub_ata_initialize (void) -{ - grub_pci_iterate (grub_ata_pciinit); - return 0; -} - -static void -grub_ata_setlba (struct grub_ata_device *dev, grub_disk_addr_t sector, - grub_size_t size) -{ - grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, size); - grub_ata_regset (dev, GRUB_ATA_REG_LBALOW, sector & 0xFF); - grub_ata_regset (dev, GRUB_ATA_REG_LBAMID, (sector >> 8) & 0xFF); - grub_ata_regset (dev, GRUB_ATA_REG_LBAHIGH, (sector >> 16) & 0xFF); -} - -static grub_err_t -grub_ata_setaddress (struct grub_ata_device *dev, - grub_ata_addressing_t addressing, +grub_ata_setaddress (struct grub_ata *dev, + struct grub_disk_ata_pass_through_parms *parms, grub_disk_addr_t sector, grub_size_t size) { - switch (addressing) + switch (dev->addr) { case GRUB_ATA_CHS: { @@ -544,14 +193,11 @@ grub_ata_setaddress (struct grub_ata_device *dev, return grub_error (GRUB_ERR_OUT_OF_RANGE, "sector %d cannot be addressed " "using CHS addressing", sector); - - grub_ata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4) | head); - if (grub_ata_check_ready (dev)) - return grub_errno; - - grub_ata_regset (dev, GRUB_ATA_REG_SECTNUM, sect); - grub_ata_regset (dev, GRUB_ATA_REG_CYLLSB, cylinder & 0xFF); - grub_ata_regset (dev, GRUB_ATA_REG_CYLMSB, cylinder >> 8); + + parms->taskfile.disk = head; + parms->taskfile.sectnum = sect; + parms->taskfile.cyllsb = cylinder & 0xFF; + parms->taskfile.cylmsb = cylinder >> 8; break; } @@ -559,26 +205,31 @@ grub_ata_setaddress (struct grub_ata_device *dev, case GRUB_ATA_LBA: if (size == 256) size = 0; - grub_ata_regset (dev, GRUB_ATA_REG_DISK, - 0xE0 | (dev->device << 4) | ((sector >> 24) & 0x0F)); - if (grub_ata_check_ready (dev)) - return grub_errno; + parms->taskfile.disk = ((sector >> 24) & 0x0F); - grub_ata_setlba (dev, sector, size); + parms->taskfile.sectors = size; + parms->taskfile.lba_low = sector & 0xFF; + parms->taskfile.lba_mid = (sector >> 8) & 0xFF; + parms->taskfile.lba_high = (sector >> 16) & 0xFF; break; case GRUB_ATA_LBA48: if (size == 65536) size = 0; - grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4)); - if (grub_ata_check_ready (dev)) - return grub_errno; + parms->taskfile.disk = 0; /* Set "Previous". */ - grub_ata_setlba (dev, sector >> 24, size >> 8); + parms->taskfile.sectors = size & 0xFF; + parms->taskfile.lba_low = sector & 0xFF; + parms->taskfile.lba_mid = (sector >> 8) & 0xFF; + parms->taskfile.lba_high = (sector >> 16) & 0xFF; + /* Set "Current". */ - grub_ata_setlba (dev, sector, size); + parms->taskfile.sectors48 = (size >> 8) & 0xFF; + parms->taskfile.lba48_low = (sector >> 24) & 0xFF; + parms->taskfile.lba48_mid = (sector >> 32) & 0xFF; + parms->taskfile.lba48_high = (sector >> 40) & 0xFF; break; } @@ -590,14 +241,15 @@ static grub_err_t grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf, int rw) { - struct grub_ata_device *dev = (struct grub_ata_device *) disk->data; + struct grub_ata *ata = disk->data; - grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n", (unsigned long long) size, rw); - - grub_ata_addressing_t addressing = dev->addr; + grub_ata_addressing_t addressing = ata->addr; grub_size_t batch; int cmd, cmd_write; + grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n", + (unsigned long long) size, rw); + if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0) { batch = 65536; @@ -608,7 +260,10 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, { if (addressing == GRUB_ATA_LBA48) addressing = GRUB_ATA_LBA; - batch = 256; + if (addressing != GRUB_ATA_CHS) + batch = 256; + else + batch = 1; cmd = GRUB_ATA_CMD_READ_SECTORS; cmd_write = GRUB_ATA_CMD_WRITE_SECTORS; } @@ -616,44 +271,25 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t nsectors = 0; while (nsectors < size) { + struct grub_disk_ata_pass_through_parms parms; + grub_err_t err; + if (size - nsectors < batch) batch = size - nsectors; grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch); - - /* Send read/write command. */ - if (grub_ata_setaddress (dev, addressing, sector, batch)) - return grub_errno; - - grub_ata_regset (dev, GRUB_ATA_REG_CMD, (! rw ? cmd : cmd_write)); - - unsigned sect; - for (sect = 0; sect < batch; sect++) - { - /* Wait for !BSY, DRQ. */ - if (grub_ata_wait_drq (dev, rw, GRUB_ATA_TOUT_DATA)) - return grub_errno; - - /* Transfer data. */ - if (! rw) - grub_ata_pio_read (dev, buf, GRUB_DISK_SECTOR_SIZE); - else - grub_ata_pio_write (dev, buf, GRUB_DISK_SECTOR_SIZE); - - buf += GRUB_DISK_SECTOR_SIZE; - } - - if (rw) - { - /* Check for write error. */ - if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA)) - return grub_errno; - - if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) - & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) - return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error"); - } - + grub_memset (&parms, 0, sizeof (parms)); + grub_ata_setaddress (ata, &parms, sector, batch); + parms.taskfile.cmd = (! rw ? cmd : cmd_write); + parms.buffer = buf; + parms.size = batch * GRUB_DISK_SECTOR_SIZE; + + err = ata->dev->readwrite (ata, &parms); + if (err) + return err; + if (parms.size != batch * GRUB_DISK_SECTOR_SIZE) + return grub_error (GRUB_ERR_READ_ERROR, "incomplete read"); + buf += GRUB_DISK_SECTOR_SIZE * batch; sector += batch; nsectors += batch; } @@ -663,76 +299,118 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, -static int -grub_ata_iterate (int (*hook) (const char *name)) +static inline void +grub_ata_real_close (struct grub_ata *ata) { - struct grub_ata_device *dev; + if (ata->dev->close) + ata->dev->close (ata); +} - for (dev = grub_ata_devices; dev; dev = dev->next) +static struct grub_ata * +grub_ata_real_open (int id, int bus) +{ + struct grub_ata *ata; + grub_ata_dev_t p; + + ata = grub_malloc (sizeof (*ata)); + if (!ata) + return NULL; + for (p = grub_ata_dev_list; p; p = p->next) { - char devname[10]; grub_err_t err; - - err = check_device (dev); - if (err) + if (p->open (id, bus, ata)) { grub_errno = GRUB_ERR_NONE; continue; } - - if (dev->atapi) - continue; - - grub_snprintf (devname, sizeof (devname), - "ata%d", dev->port * 2 + dev->device); - - if (hook (devname)) - return 1; + ata->dev = p; + /* Use the IDENTIFY DEVICE command to query the device. */ + err = grub_ata_identify (ata); + if (err) + { + grub_free (ata); + return NULL; + } + return ata; } + grub_free (ata); + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device"); + return NULL; +} +static int +grub_ata_iterate (int (*hook_in) (const char *name)) +{ + auto int hook (int id, int bus); + int hook (int id, int bus) + { + struct grub_ata *ata; + int ret; + char devname[40]; + + ata = grub_ata_real_open (id, bus); + + if (!ata) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (ata->atapi) + { + grub_ata_real_close (ata); + return 0; + } + grub_snprintf (devname, sizeof (devname), + "%s%d", grub_scsi_names[id], bus); + ret = hook_in (devname); + grub_ata_real_close (ata); + return ret; + } + + grub_ata_dev_t p; + + for (p = grub_ata_dev_list; p; p = p->next) + if (p->iterate && p->iterate (hook)) + return 1; return 0; } static grub_err_t grub_ata_open (const char *name, grub_disk_t disk) { - struct grub_ata_device *dev; - grub_err_t err; + unsigned id, bus; + struct grub_ata *ata; - for (dev = grub_ata_devices; dev; dev = dev->next) - { - char devname[10]; - grub_snprintf (devname, sizeof (devname), - "ata%d", dev->port * 2 + dev->device); - if (grub_strcmp (name, devname) == 0) - break; - } + for (id = 0; id < GRUB_SCSI_NUM_SUBSYSTEMS; id++) + if (grub_strncmp (grub_scsi_names[id], name, + grub_strlen (grub_scsi_names[id])) == 0 + && grub_isdigit (name[grub_strlen (grub_scsi_names[id])])) + break; + if (id == GRUB_SCSI_NUM_SUBSYSTEMS) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk"); + bus = grub_strtoul (name + grub_strlen (grub_scsi_names[id]), 0, 0); + ata = grub_ata_real_open (id, bus); + if (!ata) + return grub_errno; - if (! dev) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); - - if (dev->atapi) + if (ata->atapi) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk"); - err = check_device (dev); + disk->total_sectors = ata->size; - if (err) - return err; - - disk->total_sectors = dev->size; - - disk->id = (unsigned long) dev; + disk->id = grub_make_scsi_id (id, bus, 0); disk->has_partitions = 1; - disk->data = dev; + disk->data = ata; return 0; } static void -grub_ata_close (grub_disk_t disk __attribute__((unused))) +grub_ata_close (grub_disk_t disk) { - + struct grub_ata *ata = disk->data; + grub_ata_real_close (ata); } static grub_err_t @@ -767,70 +445,35 @@ static struct grub_disk_dev grub_atadisk_dev = /* ATAPI code. */ -static int -grub_atapi_iterate (int (*hook) (int bus, int luns)) -{ - struct grub_ata_device *dev; - - for (dev = grub_ata_devices; dev; dev = dev->next) - { - grub_err_t err; - - err = check_device (dev); - if (err) - { - grub_errno = GRUB_ERR_NONE; - continue; - } - - if (! dev->atapi) - continue; - - if (hook (dev->port * 2 + dev->device, 1)) - return 1; - } - - return 0; - -} - static grub_err_t -grub_atapi_read (struct grub_scsi *scsi, - grub_size_t cmdsize __attribute__((unused)), - char *cmd, grub_size_t size, char *buf) +grub_atapi_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf) { - struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data; + struct grub_ata *dev = scsi->data; + struct grub_disk_ata_pass_through_parms parms; + grub_err_t err; grub_dprintf("ata", "grub_atapi_read (size=%llu)\n", (unsigned long long) size); + grub_memset (&parms, 0, sizeof (parms)); - if (grub_atapi_packet (dev, cmd, size)) - return grub_errno; + parms.taskfile.disk = 0; + parms.taskfile.features = 0; + parms.taskfile.atapi_ireason = 0; + parms.taskfile.atapi_cnthigh = size >> 8; + parms.taskfile.atapi_cntlow = size & 0xff; + parms.taskfile.cmd = GRUB_ATA_CMD_PACKET; + parms.cmd = cmd; + parms.cmdsize = cmdsize; - grub_size_t nread = 0; - while (nread < size) - { - /* Wait for !BSY, DRQ, I/O, !C/D. */ - if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_DATA_IN, GRUB_ATA_TOUT_DATA)) - return grub_errno; - - /* Get byte count for this DRQ assertion. */ - unsigned cnt = grub_ata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8 - | grub_ata_regget (dev, GRUB_ATAPI_REG_CNTLOW); - grub_dprintf("ata", "DRQ count=%u\n", cnt); - - /* Count of last transfer may be uneven. */ - if (! (0 < cnt && cnt <= size - nread && (! (cnt & 1) || cnt == size - nread))) - return grub_error (GRUB_ERR_READ_ERROR, "invalid ATAPI transfer count"); - - /* Read the data. */ - grub_ata_pio_read (dev, buf + nread, cnt); - - if (cnt & 1) - buf[nread + cnt - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress + GRUB_ATA_REG_DATA)); - - nread += cnt; - } + parms.size = size; + parms.buffer = buf; + + err = dev->dev->readwrite (dev, &parms); + if (err) + return err; + if (parms.size != size) + return grub_error (GRUB_ERR_READ_ERROR, "incomplete ATAPI read"); return GRUB_ERR_NONE; } @@ -846,64 +489,98 @@ grub_atapi_write (struct grub_scsi *scsi __attribute__((unused)), } static grub_err_t -grub_atapi_open (int devnum, struct grub_scsi *scsi) +grub_atapi_open (int id, int bus, struct grub_scsi *scsi) { - struct grub_ata_device *dev; - struct grub_ata_device *devfnd = 0; - grub_err_t err; + struct grub_ata *ata; - for (dev = grub_ata_devices; dev; dev = dev->next) - { - if (dev->port * 2 + dev->device == devnum) - { - devfnd = dev; - break; - } - } - - grub_dprintf ("ata", "opening ATAPI dev `ata%d'\n", devnum); - - if (! devfnd) + ata = grub_ata_real_open (id, bus); + if (!ata) + return grub_errno; + + if (! ata->atapi) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device"); - err = check_device (devfnd); - if (err) - return err; - - if (! devfnd->atapi) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device"); - - scsi->data = devfnd; + scsi->data = ata; return GRUB_ERR_NONE; } +static int +grub_atapi_iterate (int (*hook_in) (int id, int bus, int luns)) +{ + auto int hook (int id, int bus); + int hook (int id, int bus) + { + struct grub_ata *ata; + int ret; + + ata = grub_ata_real_open (id, bus); + + if (!ata) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (!ata->atapi) + { + grub_ata_real_close (ata); + return 0; + } + ret = hook_in (id, bus, 1); + grub_ata_real_close (ata); + return ret; + } + + grub_ata_dev_t p; + + for (p = grub_ata_dev_list; p; p = p->next) + if (p->iterate && p->iterate (hook)) + return 1; + return 0; +} + +static void +grub_atapi_close (grub_scsi_t disk) +{ + struct grub_ata *ata = disk->data; + grub_ata_real_close (ata); +} + + +void +grub_ata_dev_register (grub_ata_dev_t dev) +{ + dev->next = grub_ata_dev_list; + grub_ata_dev_list = dev; +} + +void +grub_ata_dev_unregister (grub_ata_dev_t dev) +{ + grub_ata_dev_t *p, q; + + for (p = &grub_ata_dev_list, q = *p; q; p = &(q->next), q = q->next) + if (q == dev) + { + *p = q->next; + break; + } +} static struct grub_scsi_dev grub_atapi_dev = { - .name = "ata", - .id = GRUB_SCSI_SUBSYSTEM_ATAPI, .iterate = grub_atapi_iterate, .open = grub_atapi_open, + .close = grub_atapi_close, .read = grub_atapi_read, - .write = grub_atapi_write + .write = grub_atapi_write, + .next = 0 }; GRUB_MOD_INIT(ata) { - /* 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_ata_initialize (); - grub_disk_dev_register (&grub_atadisk_dev); /* ATAPI devices are handled by scsi.mod. */ diff --git a/disk/ata_pthru.c b/disk/ata_pthru.c index f52725a49..b6edf78d6 100644 --- a/disk/ata_pthru.c +++ b/disk/ata_pthru.c @@ -18,90 +18,496 @@ */ #include +#include #include #include #include +#include +#include +#include +/* 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 }; +static const grub_port_t grub_pata_ioaddress2[] = { GRUB_ATA_CH0_PORT2, + GRUB_ATA_CH1_PORT2 }; -/* 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) +struct grub_pata_device { - if (disk->dev->id != GRUB_DISK_DEVICE_ATA_ID) - return grub_error (GRUB_ERR_BAD_DEVICE, - "device not accessed via ata.mod"); + /* IDE port to use. */ + int port; - struct grub_ata_device *dev = (struct grub_ata_device *) disk->data; + /* IO addresses on which the registers for this device can be + found. */ + grub_port_t ioaddress; + grub_port_t ioaddress2; - 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"); + /* 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; - 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); + struct grub_pata_device *next; +}; - /* 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; +static struct grub_pata_device *grub_pata_devices; - int i; - for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++) - grub_ata_regset (dev, i, parms->taskfile[i]); +static inline void +grub_pata_regset (struct grub_pata_device *dev, int reg, int val) +{ + grub_outb (val, dev->ioaddress + reg); +} - /* Start command. */ - grub_ata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile[GRUB_ATA_REG_CMD]); +static inline grub_uint8_t +grub_pata_regget (struct grub_pata_device *dev, int reg) +{ + return grub_inb (dev->ioaddress + reg); +} - /* Wait for !BSY. */ - if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA)) - return grub_errno; +static inline void +grub_pata_regset2 (struct grub_pata_device *dev, int reg, int val) +{ + grub_outb (val, dev->ioaddress2 + reg); +} - /* Check status. */ - grub_int8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - grub_dprintf ("ata", "status=0x%x\n", sts); +static inline grub_uint8_t +grub_pata_regget2 (struct grub_pata_device *dev, int reg) +{ + return grub_inb (dev->ioaddress2 + reg); +} - /* Transfer data. */ - if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_DRQ) +/* 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 (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); + 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 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; } +static inline grub_err_t +grub_pata_check_ready (struct grub_pata_device *dev) +{ + if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY) + return grub_pata_wait_not_busy (dev, 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) +{ + grub_uint16_t *buf16 = (grub_uint16_t *) buf; + unsigned int i; + + /* Read in the data, word by word. */ + for (i = 0; i < size / 2; i++) + buf16[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) +{ + grub_uint16_t *buf16 = (grub_uint16_t *) buf; + unsigned int i; + + /* Write the data, word by word. */ + for (i = 0; i < size / 2; i++) + grub_outw(grub_cpu_to_le16 (buf16[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) +{ + struct grub_pata_device *dev = (struct grub_pata_device *) disk->data; + grub_size_t nread = 0; + + 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=%d\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, (parms->cmdsize ? 0 : 0xE0) + | dev->device << 4 + | (parms->taskfile.disk & 0xf)); + if (grub_pata_check_ready (dev)) + return grub_errno; + + int i; + 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 = 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; + 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; + + /* 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, int addr2) +{ + struct grub_pata_device *dev; + struct grub_pata_device **devp; + grub_err_t err; + + grub_dprintf ("pata", "detecting device %d,%d (0x%x, 0x%x)\n", + port, device, addr, addr2); + + 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->ioaddress2 = addr2 + GRUB_MACHINE_PCI_IO_BASE; + 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; +} + +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 regb; + 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; + regb = 0; + + /* If the channel is in compatibility mode, just assign the + default registers. */ + if (compat == 0 && !compat_use[i]) + { + rega = grub_pata_ioaddress[i]; + regb = grub_pata_ioaddress2[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; + regb = bar2 & ~3; + } + } + + grub_dprintf ("pata", + "PCI dev (%d,%d,%d) compat=%d rega=0x%x regb=0x%x\n", + grub_pci_get_bus (dev), grub_pci_get_device (dev), + grub_pci_get_function (dev), compat, rega, regb); + + if (rega && regb) + { + grub_errno = GRUB_ERR_NONE; + grub_pata_device_initialize (controller * 2 + i, 0, rega, regb); + + /* 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, regb); + + /* 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; +} + +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; + + return GRUB_ERR_NONE; +} + +static int +grub_pata_iterate (int (*hook) (int id, int bus)) +{ + struct grub_pata_device *dev; + + 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) { - /* Register ATA pass through function. */ - grub_disk_ata_pass_through = grub_ata_pass_through; + /* 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) { - if (grub_disk_ata_pass_through == grub_ata_pass_through) - grub_disk_ata_pass_through = NULL; + grub_ata_dev_unregister (&grub_pata_dev); } diff --git a/disk/scsi.c b/disk/scsi.c index 60192bef5..f0594e7c7 100644 --- a/disk/scsi.c +++ b/disk/scsi.c @@ -30,6 +30,12 @@ static grub_scsi_dev_t grub_scsi_dev_list; +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) { @@ -318,9 +324,9 @@ grub_scsi_iterate (int (*hook) (const char *name)) { grub_scsi_dev_t p; - auto int scsi_iterate (int bus, int luns); + auto int scsi_iterate (int id, int bus, int luns); - int scsi_iterate (int bus, int luns) + int scsi_iterate (int id, int bus, int luns) { int i; @@ -329,7 +335,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); @@ -343,7 +349,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); @@ -370,6 +376,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 @@ -394,15 +401,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; @@ -484,7 +501,6 @@ grub_scsi_open (const char *name, grub_disk_t disk) } grub_free (scsi); - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk"); } diff --git a/disk/usbms.c b/disk/usbms.c index 225761e0f..0ff2e6235 100644 --- a/disk/usbms.c +++ b/disk/usbms.c @@ -205,7 +205,7 @@ 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 (*hook) (int id, int bus, int luns)) { unsigned i; @@ -214,7 +214,7 @@ grub_usbms_iterate (int (*hook) (int bus, int luns)) 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; } @@ -384,8 +384,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]) @@ -400,8 +404,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, diff --git a/include/grub/ata.h b/include/grub/ata.h index 9e3aaf0e6..b1c6ead26 100644 --- a/include/grub/ata.h +++ b/include/grub/ata.h @@ -97,21 +97,64 @@ enum grub_ata_timeout_milliseconds GRUB_ATA_TOUT_DATA = 10000 /* 10s DATA I/O timeout. */ }; -struct grub_ata_device +typedef union { - /* IDE port to use. */ - int port; + grub_uint8_t raw[11]; + struct + { + union + { + grub_uint8_t features; + grub_uint8_t error; + }; + union + { + grub_uint8_t sectors; + grub_uint8_t atapi_ireason; + }; + union + { + grub_uint8_t lba_low; + grub_uint8_t sectnum; + }; + union + { + grub_uint8_t lba_mid; + grub_uint8_t cyllsb; + grub_uint8_t atapi_cntlow; + }; + union + { + grub_uint8_t lba_high; + grub_uint8_t cylmsb; + grub_uint8_t atapi_cnthigh; + }; + grub_uint8_t disk; + union + { + grub_uint8_t cmd; + grub_uint8_t status; + }; + grub_uint8_t sectors48; + grub_uint8_t lba48_low; + grub_uint8_t lba48_mid; + grub_uint8_t lba48_high; + }; +} grub_ata_regs_t; - /* IO addresses on which the registers for this device can be - found. */ - grub_port_t ioaddress; - grub_port_t ioaddress2; - - /* 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; +/* ATA pass through parameters and function. */ +struct grub_disk_ata_pass_through_parms +{ + grub_ata_regs_t taskfile; + void * buffer; + grub_size_t size; + int write; + void *cmd; + int cmdsize; +}; +struct grub_ata +{ /* Addressing methods available for accessing this device. If CHS is only available, use that. Otherwise use LBA, except for the high sectors. In that case use LBA48. */ @@ -128,47 +171,36 @@ struct grub_ata_device /* Set to 0 for ATA, set to 1 for ATAPI. */ int atapi; - struct grub_ata_device *next; + void *data; + + struct grub_ata_dev *dev; }; -grub_err_t EXPORT_FUNC(grub_ata_wait_not_busy) (struct grub_ata_device *dev, - int milliseconds); -grub_err_t EXPORT_FUNC(grub_ata_wait_drq) (struct grub_ata_device *dev, - int rw, int milliseconds); -void EXPORT_FUNC(grub_ata_pio_read) (struct grub_ata_device *dev, - char *buf, grub_size_t size); +typedef struct grub_ata *grub_ata_t; -static inline void -grub_ata_regset (struct grub_ata_device *dev, int reg, int val) +struct grub_ata_dev { - grub_outb (val, dev->ioaddress + reg); -} + /* Call HOOK with each device name, until HOOK returns non-zero. */ + int (*iterate) (int (*hook) (int id, int bus)); -static inline grub_uint8_t -grub_ata_regget (struct grub_ata_device *dev, int reg) -{ - return grub_inb (dev->ioaddress + reg); -} + /* Open the device named NAME, and set up SCSI. */ + grub_err_t (*open) (int id, int bus, struct grub_ata *scsi); -static inline void -grub_ata_regset2 (struct grub_ata_device *dev, int reg, int val) -{ - grub_outb (val, dev->ioaddress2 + reg); -} + /* Close the scsi device SCSI. */ + void (*close) (struct grub_ata *ata); -static inline grub_uint8_t -grub_ata_regget2 (struct grub_ata_device *dev, int reg) -{ - return grub_inb (dev->ioaddress2 + reg); -} + /* Read SIZE bytes from the device SCSI into BUF after sending the + command CMD of size CMDSIZE. */ + grub_err_t (*readwrite) (struct grub_ata *ata, + struct grub_disk_ata_pass_through_parms *parms); -static inline grub_err_t -grub_ata_check_ready (struct grub_ata_device *dev) -{ - if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY) - return grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_STD); + /* The next scsi device. */ + struct grub_ata_dev *next; +}; - return GRUB_ERR_NONE; -} +typedef struct grub_ata_dev *grub_ata_dev_t; + +void grub_ata_dev_register (grub_ata_dev_t dev); +void grub_ata_dev_unregister (grub_ata_dev_t dev); #endif /* ! GRUB_ATA_HEADER */ diff --git a/include/grub/disk.h b/include/grub/disk.h index e7f807e0e..ac3fb6497 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -165,16 +165,5 @@ grub_uint64_t EXPORT_FUNC(grub_disk_get_size) (grub_disk_t disk); extern void (* EXPORT_VAR(grub_disk_firmware_fini)) (void); extern int EXPORT_VAR(grub_disk_firmware_is_tainted); - -/* ATA pass through parameters and function. */ -struct grub_disk_ata_pass_through_parms -{ - grub_uint8_t taskfile[8]; - void * buffer; - int size; -}; - -extern grub_err_t (* EXPORT_VAR(grub_disk_ata_pass_through)) (grub_disk_t, - struct grub_disk_ata_pass_through_parms *); - + #endif /* ! GRUB_DISK_HEADER */ diff --git a/include/grub/scsi.h b/include/grub/scsi.h index 289cd8e4a..081189fb0 100644 --- a/include/grub/scsi.h +++ b/include/grub/scsi.h @@ -29,10 +29,13 @@ struct grub_scsi; enum { GRUB_SCSI_SUBSYSTEM_USBMS, - GRUB_SCSI_SUBSYSTEM_ATAPI, - GRUB_SCSI_SUBSYSTEM_AHCI + GRUB_SCSI_SUBSYSTEM_PATA, + GRUB_SCSI_SUBSYSTEM_AHCI, + GRUB_SCSI_NUM_SUBSYSTEMS }; +extern char grub_scsi_names[GRUB_SCSI_NUM_SUBSYSTEMS][5]; + #define GRUB_SCSI_ID_SUBSYSTEM_SHIFT 24 #define GRUB_SCSI_ID_BUS_SHIFT 8 #define GRUB_SCSI_ID_LUN_SHIFT 0 @@ -46,16 +49,11 @@ grub_make_scsi_id (int subsystem, int bus, int lun) struct grub_scsi_dev { - /* The device name. */ - const char *name; - - grub_uint8_t id; - /* Call HOOK with each device name, until HOOK returns non-zero. */ - int (*iterate) (int (*hook) (int bus, int luns)); + int (*iterate) (int (*hook) (int id, int bus, int luns)); /* Open the device named NAME, and set up SCSI. */ - grub_err_t (*open) (int bus, struct grub_scsi *scsi); + grub_err_t (*open) (int id, int bus, struct grub_scsi *scsi); /* Close the scsi device SCSI. */ void (*close) (struct grub_scsi *scsi); diff --git a/kern/disk.c b/kern/disk.c index ccd5f200f..84c48e65c 100644 --- a/kern/disk.c +++ b/kern/disk.c @@ -46,10 +46,6 @@ static struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM]; void (*grub_disk_firmware_fini) (void); int grub_disk_firmware_is_tainted; -grub_err_t (* grub_disk_ata_pass_through) (grub_disk_t, - struct grub_disk_ata_pass_through_parms *); - - #if 0 static unsigned long grub_disk_cache_hits; static unsigned long grub_disk_cache_misses; From faec81d97dd6b3cb58b631f4fcf426786f1db54f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Dec 2010 16:54:33 +0100 Subject: [PATCH 03/49] Add missing buffer specifications --- grub-core/disk/ata.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c index 04b81cd27..9f62546e1 100644 --- a/grub-core/disk/ata.c +++ b/grub-core/disk/ata.c @@ -72,6 +72,8 @@ grub_atapi_identify (struct grub_ata *dev) grub_memset (&parms, 0, sizeof (parms)); parms.taskfile.disk = 0; parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE; + parms.size = GRUB_DISK_SECTOR_SIZE; + parms.buffer = info; err = dev->dev->readwrite (dev, &parms); if (err) @@ -105,6 +107,7 @@ grub_ata_identify (struct grub_ata *dev) info16 = (grub_uint16_t *) info; grub_memset (&parms, 0, sizeof (parms)); parms.buffer = info; + parms.size = GRUB_DISK_SECTOR_SIZE; parms.taskfile.disk = 0; parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE; From 22a7cc9a6f5e61de79b34f09c97a71a12dd370a2 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Dec 2010 17:09:56 +0100 Subject: [PATCH 04/49] fix 3rd argument to scsi_iterate --- grub-core/disk/ata.c | 2 +- grub-core/disk/scsi.c | 4 ++-- grub-core/disk/usbms.c | 2 +- include/grub/scsi.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c index 9f62546e1..6f7f44227 100644 --- a/grub-core/disk/ata.c +++ b/grub-core/disk/ata.c @@ -508,7 +508,7 @@ grub_atapi_open (int id, int bus, struct grub_scsi *scsi) } static int -grub_atapi_iterate (int (*hook_in) (int id, int bus, int luns)) +grub_atapi_iterate (int NESTED_FUNC_ATTR (*hook_in) (int id, int bus, int luns)) { auto int hook (int id, int bus); int hook (int id, int bus) diff --git a/grub-core/disk/scsi.c b/grub-core/disk/scsi.c index b989a98ad..015e5dce6 100644 --- a/grub-core/disk/scsi.c +++ b/grub-core/disk/scsi.c @@ -324,9 +324,9 @@ grub_scsi_iterate (int (*hook) (const char *name)) { grub_scsi_dev_t p; - auto int scsi_iterate (int id, int bus, int luns); + auto int NESTED_FUNC_ATTR scsi_iterate (int id, int bus, int luns); - int scsi_iterate (int id, int bus, int luns) + int NESTED_FUNC_ATTR scsi_iterate (int id, int bus, int luns) { int i; diff --git a/grub-core/disk/usbms.c b/grub-core/disk/usbms.c index 4e1af7be3..8041c6c95 100644 --- a/grub-core/disk/usbms.c +++ b/grub-core/disk/usbms.c @@ -205,7 +205,7 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno) static int -grub_usbms_iterate (int (*hook) (int id, int bus, int luns)) +grub_usbms_iterate (int NESTED_FUNC_ATTR (*hook) (int id, int bus, int luns)) { unsigned i; diff --git a/include/grub/scsi.h b/include/grub/scsi.h index 3deb18f1b..5b6ccc9f4 100644 --- a/include/grub/scsi.h +++ b/include/grub/scsi.h @@ -50,7 +50,7 @@ grub_make_scsi_id (int subsystem, int bus, int lun) struct grub_scsi_dev { /* Call HOOK with each device name, until HOOK returns non-zero. */ - int (*iterate) (int (*hook) (int id, int bus, int luns)); + int (*iterate) (int NESTED_FUNC_ATTR (*hook) (int id, int bus, int luns)); /* Open the device named NAME, and set up SCSI. */ grub_err_t (*open) (int id, int bus, struct grub_scsi *scsi); From 8f5ac9e57050652378a9d54d9082ab331a775946 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Dec 2010 17:10:41 +0100 Subject: [PATCH 05/49] fix ATAPI support --- grub-core/disk/ata.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c index 6f7f44227..5015f8507 100644 --- a/grub-core/disk/ata.c +++ b/grub-core/disk/ata.c @@ -113,13 +113,12 @@ grub_ata_identify (struct grub_ata *dev) parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE; err = dev->dev->readwrite (dev, &parms); - if (err) - return err; - if (parms.size != GRUB_DISK_SECTOR_SIZE) + if (err || parms.size != GRUB_DISK_SECTOR_SIZE) { grub_uint8_t sts = parms.taskfile.status; grub_free (info); + grub_errno = GRUB_ERR_NONE; if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR && (parms.taskfile.error & 0x04 /* ABRT */)) @@ -503,6 +502,7 @@ grub_atapi_open (int id, int bus, struct grub_scsi *scsi) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device"); scsi->data = ata; + scsi->luns = 1; return GRUB_ERR_NONE; } From ee2b985ef68f5522796057ef6d3055ad7d6dc5a1 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Dec 2010 17:11:21 +0100 Subject: [PATCH 06/49] rearrange wait_not_busy --- grub-core/disk/pata.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/grub-core/disk/pata.c b/grub-core/disk/pata.c index b6edf78d6..a24237a83 100644 --- a/grub-core/disk/pata.c +++ b/grub-core/disk/pata.c @@ -182,17 +182,18 @@ grub_pata_readwrite (struct grub_ata *disk, /* 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 = grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON); + 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)) @@ -210,6 +211,11 @@ grub_pata_readwrite (struct grub_ata *disk, & 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 @@ -246,6 +252,10 @@ grub_pata_readwrite (struct grub_ata *disk, } 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); From 908a8fc37a51aea0dd22d53494772456a210b900 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Dec 2010 19:48:55 +0100 Subject: [PATCH 07/49] Fix several AHCI problems --- grub-core/disk/ahci.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index 946db82e1..1e28b3dc5 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -56,10 +56,12 @@ struct grub_ahci_hba_port grub_uint32_t intstatus; grub_uint32_t inten; grub_uint32_t command; - grub_uint32_t unused1[6]; + grub_uint32_t unused1[3]; + grub_uint32_t status; + grub_uint32_t unused2[2]; grub_uint32_t sata_active; grub_uint32_t command_issue; - grub_uint32_t unused2[17]; + grub_uint32_t unused3[17]; }; struct grub_ahci_hba @@ -195,6 +197,10 @@ grub_ahci_pciinit (grub_pci_device_t dev, if (!(hba->ports_implemented & (1 << i))) continue; + /* FIXME: support hotplugging. */ + if (!hba->ports[i].status) + continue; + command_list = grub_memalign_dma32 (1024, sizeof (struct grub_ahci_cmd_head)); if (!command_list) @@ -333,8 +339,11 @@ grub_ahci_readwrite (grub_ata_t disk, 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]; + dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i]; + + dev->command_table[0].cfis[7] |= (parms->cmdsize ? 0 : 0xE0); dev->command_table[0].prdt[0].data_base = grub_dma_get_phys (bufc); dev->command_table[0].prdt[0].unused = 0; @@ -345,12 +354,12 @@ grub_ahci_readwrite (grub_ata_t disk, grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size); grub_dprintf ("ahci", "AHCI command schedulded\n"); - dev->hba->ports[dev->port].inten = (1 << 5); - dev->hba->ports[dev->port].intstatus = (1 << 5); + dev->hba->ports[dev->port].inten = (1 << 2) | (1 << 5); + dev->hba->ports[dev->port].intstatus = (1 << 2) | (1 << 5); dev->hba->ports[dev->port].command_issue |= 1; dev->hba->ports[dev->port].command |= 1; - endtime = grub_get_time_ms () + 1000; + endtime = grub_get_time_ms () + 1000; while (!(dev->hba->ports[dev->port].intstatus & (1 << 5))) if (grub_get_time_ms () > endtime) { @@ -362,6 +371,7 @@ grub_ahci_readwrite (grub_ata_t disk, grub_dprintf ("ahci", "AHCI command completed succesfully\n"); dev->hba->ports[dev->port].command &= ~1; + dev->hba->ports[dev->port].command_issue &= ~1; if (!parms->write) grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size); @@ -379,10 +389,8 @@ grub_ahci_open (int id, int devnum, struct grub_ata *ata) 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->num == devnum) + break; if (! dev) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device"); From 51f7e1acb7355d15d83ff3f916cc80cc1827b96b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 25 Dec 2010 02:53:20 +0100 Subject: [PATCH 08/49] DMA ATA commands support --- grub-core/disk/ahci.c | 1 + grub-core/disk/ata.c | 28 +++++++++++++++++++++++----- grub-core/disk/pata.c | 1 + include/grub/ata.h | 8 ++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index 1e28b3dc5..4b5d195f2 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -398,6 +398,7 @@ grub_ahci_open (int id, int devnum, struct grub_ata *ata) grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num); ata->data = dev; + ata->dma = 0; return GRUB_ERR_NONE; } diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c index 5015f8507..b33abf90d 100644 --- a/grub-core/disk/ata.c +++ b/grub-core/disk/ata.c @@ -255,8 +255,16 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0) { batch = 65536; - cmd = GRUB_ATA_CMD_READ_SECTORS_EXT; - cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_EXT; + if (ata->dma) + { + cmd = GRUB_ATA_CMD_READ_SECTORS_DMA_EXT; + cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA_EXT; + } + else + { + cmd = GRUB_ATA_CMD_READ_SECTORS_EXT; + cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_EXT; + } } else { @@ -266,9 +274,17 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, batch = 256; else batch = 1; - cmd = GRUB_ATA_CMD_READ_SECTORS; - cmd_write = GRUB_ATA_CMD_WRITE_SECTORS; - } + if (ata->dma) + { + cmd = GRUB_ATA_CMD_READ_SECTORS_DMA; + cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA; + } + else + { + cmd = GRUB_ATA_CMD_READ_SECTORS; + cmd_write = GRUB_ATA_CMD_WRITE_SECTORS; + } + } grub_size_t nsectors = 0; while (nsectors < size) @@ -285,6 +301,8 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, parms.taskfile.cmd = (! rw ? cmd : cmd_write); parms.buffer = buf; parms.size = batch * GRUB_DISK_SECTOR_SIZE; + if (ata->dma) + parms.dma = 1; err = ata->dev->readwrite (ata, &parms); if (err) diff --git a/grub-core/disk/pata.c b/grub-core/disk/pata.c index a24237a83..186caf6a2 100644 --- a/grub-core/disk/pata.c +++ b/grub-core/disk/pata.c @@ -474,6 +474,7 @@ grub_pata_open (int id, int devnum, struct grub_ata *ata) return err; ata->data = devfnd; + ata->dma = 0; return GRUB_ERR_NONE; } diff --git a/include/grub/ata.h b/include/grub/ata.h index b1c6ead26..bea74c180 100644 --- a/include/grub/ata.h +++ b/include/grub/ata.h @@ -82,6 +82,9 @@ enum grub_ata_commands GRUB_ATA_CMD_PACKET = 0xa0, GRUB_ATA_CMD_READ_SECTORS = 0x20, GRUB_ATA_CMD_READ_SECTORS_EXT = 0x24, + GRUB_ATA_CMD_READ_SECTORS_DMA = 0xc8, + GRUB_ATA_CMD_READ_SECTORS_DMA_EXT = 0x25, + GRUB_ATA_CMD_SECURITY_FREEZE_LOCK = 0xf5, GRUB_ATA_CMD_SET_FEATURES = 0xef, GRUB_ATA_CMD_SLEEP = 0xe6, @@ -89,6 +92,8 @@ enum grub_ata_commands GRUB_ATA_CMD_STANDBY_IMMEDIATE = 0xe0, GRUB_ATA_CMD_WRITE_SECTORS = 0x30, GRUB_ATA_CMD_WRITE_SECTORS_EXT = 0x34, + GRUB_ATA_CMD_WRITE_SECTORS_DMA_EXT = 0x35, + GRUB_ATA_CMD_WRITE_SECTORS_DMA = 0xca, }; enum grub_ata_timeout_milliseconds @@ -151,6 +156,7 @@ struct grub_disk_ata_pass_through_parms int write; void *cmd; int cmdsize; + int dma; }; struct grub_ata @@ -171,6 +177,8 @@ struct grub_ata /* Set to 0 for ATA, set to 1 for ATAPI. */ int atapi; + int dma; + void *data; struct grub_ata_dev *dev; From 060d0c7ac2fbf763cc07efcb498c04c03b4cf197 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 25 Dec 2010 03:06:07 +0100 Subject: [PATCH 09/49] working AHCI (at last) --- grub-core/disk/ahci.c | 182 +++++++++++++++++++++++++++++++++++------- 1 file changed, 152 insertions(+), 30 deletions(-) diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index 4b5d195f2..4d5ee692c 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -44,8 +44,8 @@ struct grub_ahci_prdt_entry struct grub_ahci_cmd_table { grub_uint8_t cfis[0x40]; - grub_uint8_t command[16]; - grub_uint32_t reserved[0xc]; + grub_uint8_t command[0x10]; + grub_uint8_t reserved[0x30]; struct grub_ahci_prdt_entry prdt[1]; }; @@ -56,14 +56,27 @@ struct grub_ahci_hba_port grub_uint32_t intstatus; grub_uint32_t inten; grub_uint32_t command; - grub_uint32_t unused1[3]; + grub_uint32_t unused1; + grub_uint32_t task_file_data; + grub_uint32_t sig; grub_uint32_t status; - grub_uint32_t unused2[2]; + grub_uint32_t unused2; + grub_uint32_t sata_error; grub_uint32_t sata_active; grub_uint32_t command_issue; - grub_uint32_t unused3[17]; + 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; @@ -76,6 +89,11 @@ struct grub_ahci_hba struct grub_ahci_hba_port ports[32]; }; +struct grub_ahci_received_fis +{ + char raw[4096]; +}; + enum { GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f @@ -83,6 +101,7 @@ enum enum { + GRUB_AHCI_HBA_GLOBAL_CONTROL_RESET = 0x00000001, GRUB_AHCI_HBA_GLOBAL_CONTROL_INTR_EN = 0x00000002, GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN = 0x80000000, }; @@ -106,8 +125,14 @@ struct grub_ahci_device 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; }; +static grub_err_t +grub_ahci_readwrite_real (struct grub_ahci_device *dev, + struct grub_disk_ata_pass_through_parms *parms); + + enum { GRUB_AHCI_CONFIG_READ = 0, @@ -160,10 +185,6 @@ grub_ahci_pciinit (grub_pci_device_t dev, hba = grub_pci_device_map_range (dev, bar & GRUB_PCI_ADDR_MEM_MASK, sizeof (hba)); - hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN; - - nports = (hba->cap & GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1; - if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED)) { grub_uint64_t endtime; @@ -186,6 +207,11 @@ grub_ahci_pciinit (grub_pci_device_t dev, } else grub_dprintf ("ahci", "AHCI is already in OS mode\n"); + + hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN; + + nports = (hba->cap & GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1; + grub_dprintf ("ahci", "%d AHCI ports\n", nports); for (i = 0; i < nports; i++) @@ -197,8 +223,9 @@ grub_ahci_pciinit (grub_pci_device_t dev, if (!(hba->ports_implemented & (1 << i))) continue; + grub_dprintf ("ahci", "status %d:%x\n", i, hba->ports[i].status); /* FIXME: support hotplugging. */ - if (!hba->ports[i].status) + if ((hba->ports[i].status & 0xf) != 0x3) continue; command_list = grub_memalign_dma32 (1024, @@ -224,6 +251,26 @@ grub_ahci_pciinit (grub_pci_device_t dev, grub_dprintf ("ahci", "found device ahci%d (port %d)\n", numdevs, i); + hba->ports[i].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; + while ((hba->ports[i].command & GRUB_AHCI_HBA_PORT_CMD_FR)); + hba->ports[i].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST; + while ((hba->ports[i].command & GRUB_AHCI_HBA_PORT_CMD_CR)); + + hba->ports[i].fbs = 2; + + adev->rfis = grub_memalign_dma32 (4096, + sizeof (struct grub_ahci_received_fis)); + grub_memset ((char *) grub_dma_get_virt (adev->rfis), 0, + sizeof (struct grub_ahci_received_fis)); + hba->ports[i].fis_base = grub_dma_get_phys (adev->rfis); + hba->ports[i].command |= GRUB_AHCI_HBA_PORT_CMD_FRE; + while (!(hba->ports[i].command & GRUB_AHCI_HBA_PORT_CMD_FR)); + hba->ports[i].command |= GRUB_AHCI_HBA_PORT_CMD_ST; + while (!(hba->ports[i].command & GRUB_AHCI_HBA_PORT_CMD_CR)); + + hba->ports[i].command = (hba->ports[i].command & 0x0fffffff) + | (1 << 28) | 2 | 4; + adev->hba = hba; adev->port = i; adev->num = numdevs++; @@ -299,13 +346,26 @@ static const int register_map[11] = { 3 /* Features */, 10 /* LBA48 high */ }; static grub_err_t -grub_ahci_readwrite (grub_ata_t disk, - struct grub_disk_ata_pass_through_parms *parms) +grub_ahci_readwrite_real (struct grub_ahci_device *dev, + struct grub_disk_ata_pass_through_parms *parms) { - struct grub_ahci_device *dev = (struct grub_ahci_device *) disk->data; 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; + while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)); + 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)); + } + + 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, @@ -319,14 +379,19 @@ grub_ahci_readwrite (grub_ata_t disk, 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 - = (4 << GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT) - | GRUB_AHCI_CONFIG_CLEAR_R_OK + = (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->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); @@ -344,40 +409,97 @@ grub_ahci_readwrite (grub_ata_t disk, dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i]; dev->command_table[0].cfis[7] |= (parms->cmdsize ? 0 : 0xE0); + 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 (%x)\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"); - dev->hba->ports[dev->port].inten = (1 << 2) | (1 << 5); - dev->hba->ports[dev->port].intstatus = (1 << 2) | (1 << 5); + 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; - dev->hba->ports[dev->port].command |= 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 () + 1000; - while (!(dev->hba->ports[dev->port].intstatus & (1 << 5))) + while ((dev->hba->ports[dev->port].command_issue & 1)) if (grub_get_time_ms () > endtime) { - grub_dprintf ("ahci", "AHCI timeout\n"); - dev->hba->ports[dev->port].command &= ~1; - /* FIXME: free resources. */ - return grub_error (GRUB_ERR_IO, "AHCI transfer timeouted"); + 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 succesfully\n"); - dev->hba->ports[dev->port].command &= ~1; - dev->hba->ports[dev->port].command_issue &= ~1; + 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 GRUB_ERR_NONE; + return err; +} + +static grub_err_t +grub_ahci_readwrite (grub_ata_t disk, + struct grub_disk_ata_pass_through_parms *parms) +{ + return grub_ahci_readwrite_real (disk->data, parms); } static grub_err_t @@ -415,8 +537,8 @@ static struct grub_ata_dev grub_ahci_dev = GRUB_MOD_INIT(ahci) { /* To prevent two drivers operating on the same disks. */ - // grub_disk_firmware_is_tainted = 1; - if (0 && grub_disk_firmware_fini) + grub_disk_firmware_is_tainted = 1; + if (grub_disk_firmware_fini) { grub_disk_firmware_fini (); grub_disk_firmware_fini = NULL; From 188ac234bd37cd6c0fbf649d1f09cb1a0f114f5e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 25 Dec 2010 03:12:49 +0100 Subject: [PATCH 10/49] use DMA for AHCI commands --- grub-core/disk/ahci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index 4d5ee692c..d9b95df37 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -520,7 +520,7 @@ grub_ahci_open (int id, int devnum, struct grub_ata *ata) grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num); ata->data = dev; - ata->dma = 0; + ata->dma = 1; return GRUB_ERR_NONE; } From a934071d76d66b4b1a36ec73240249363392351f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 25 Dec 2010 03:22:12 +0100 Subject: [PATCH 11/49] Remove unused pata ioaddress2 --- grub-core/disk/pata.c | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/grub-core/disk/pata.c b/grub-core/disk/pata.c index 186caf6a2..940a9a5f0 100644 --- a/grub-core/disk/pata.c +++ b/grub-core/disk/pata.c @@ -29,8 +29,6 @@ /* 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 }; -static const grub_port_t grub_pata_ioaddress2[] = { GRUB_ATA_CH0_PORT2, - GRUB_ATA_CH1_PORT2 }; struct grub_pata_device { @@ -40,7 +38,6 @@ struct grub_pata_device /* IO addresses on which the registers for this device can be found. */ grub_port_t ioaddress; - grub_port_t ioaddress2; /* Two devices can be connected to a single cable. Use this field to select device 0 (commonly known as "master") or device 1 @@ -64,18 +61,6 @@ grub_pata_regget (struct grub_pata_device *dev, int reg) return grub_inb (dev->ioaddress + reg); } -static inline void -grub_pata_regset2 (struct grub_pata_device *dev, int reg, int val) -{ - grub_outb (val, dev->ioaddress2 + reg); -} - -static inline grub_uint8_t -grub_pata_regget2 (struct grub_pata_device *dev, int reg) -{ - return grub_inb (dev->ioaddress2 + reg); -} - /* Wait for !BSY. */ static grub_err_t grub_pata_wait_not_busy (struct grub_pata_device *dev, int milliseconds) @@ -298,14 +283,14 @@ check_device (struct grub_pata_device *dev) } static grub_err_t -grub_pata_device_initialize (int port, int device, int addr, int addr2) +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, 0x%x)\n", - port, device, addr, addr2); + grub_dprintf ("pata", "detecting device %d,%d (0x%x)\n", + port, device, addr); dev = grub_malloc (sizeof(*dev)); if (! dev) @@ -315,7 +300,6 @@ grub_pata_device_initialize (int port, int device, int addr, int addr2) dev->port = port; dev->device = device; dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE; - dev->ioaddress2 = addr2 + GRUB_MACHINE_PCI_IO_BASE; dev->next = NULL; /* Register the device. */ @@ -339,7 +323,6 @@ grub_pata_pciinit (grub_pci_device_t dev, grub_uint32_t bar1; grub_uint32_t bar2; int rega; - int regb; int i; static int controller = 0; int cs5536 = 0; @@ -372,14 +355,12 @@ grub_pata_pciinit (grub_pci_device_t dev, compat = (class >> (8 + 2 * i)) & 1; rega = 0; - regb = 0; /* If the channel is in compatibility mode, just assign the default registers. */ if (compat == 0 && !compat_use[i]) { rega = grub_pata_ioaddress[i]; - regb = grub_pata_ioaddress2[i]; compat_use[i] = 1; } else if (compat) @@ -398,19 +379,18 @@ grub_pata_pciinit (grub_pci_device_t dev, if ((bar1 & 1) && (bar2 & 1)) { rega = bar1 & ~3; - regb = bar2 & ~3; } } grub_dprintf ("pata", - "PCI dev (%d,%d,%d) compat=%d rega=0x%x regb=0x%x\n", + "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, regb); + grub_pci_get_function (dev), compat, rega); - if (rega && regb) + if (rega) { grub_errno = GRUB_ERR_NONE; - grub_pata_device_initialize (controller * 2 + i, 0, rega, regb); + 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 @@ -422,7 +402,7 @@ grub_pata_pciinit (grub_pci_device_t dev, grub_errno = GRUB_ERR_NONE; } - grub_pata_device_initialize (controller * 2 + i, 1, rega, regb); + grub_pata_device_initialize (controller * 2 + i, 1, rega); /* Likewise. */ if (grub_errno) From d9675dbee3525def248571a7937e69fdec7afab0 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 25 Dec 2010 03:47:51 +0100 Subject: [PATCH 12/49] Move 0xE0 to callers rather than to have it in drivers --- grub-core/commands/hdparm.c | 4 ++++ grub-core/disk/ahci.c | 1 - grub-core/disk/ata.c | 10 +++++----- grub-core/disk/pata.c | 5 ++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/grub-core/commands/hdparm.c b/grub-core/commands/hdparm.c index 0154ece57..800bfa06d 100644 --- a/grub-core/commands/hdparm.c +++ b/grub-core/commands/hdparm.c @@ -72,6 +72,8 @@ grub_hdparm_do_ata_cmd (grub_ata_t ata, grub_uint8_t cmd, apt.taskfile.cmd = cmd; apt.taskfile.features = features; apt.taskfile.sectors = sectors; + apt.taskfile.disk = 0xE0; + apt.buffer = buffer; apt.size = size; @@ -88,6 +90,7 @@ grub_hdparm_do_check_powermode_cmd (grub_ata_t ata) grub_memset (&apt, 0, sizeof (apt)); apt.taskfile.cmd = GRUB_ATA_CMD_CHECK_POWER_MODE; + apt.taskfile.disk = 0xE0; if (ata->dev->readwrite (ata, &apt)) return -1; @@ -105,6 +108,7 @@ grub_hdparm_do_smart_cmd (grub_ata_t ata, grub_uint8_t features) apt.taskfile.features = features; apt.taskfile.lba_mid = 0x4f; apt.taskfile.lba_high = 0xc2; + apt.taskfile.disk = 0xE0; if (ata->dev->readwrite (ata, &apt)) return -1; diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index d9b95df37..3160523ce 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -408,7 +408,6 @@ grub_ahci_readwrite_real (struct grub_ahci_device *dev, for (i = 0; i < sizeof (parms->taskfile.raw); i++) dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i]; - dev->command_table[0].cfis[7] |= (parms->cmdsize ? 0 : 0xE0); 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], diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c index b33abf90d..494325aa5 100644 --- a/grub-core/disk/ata.c +++ b/grub-core/disk/ata.c @@ -70,7 +70,7 @@ grub_atapi_identify (struct grub_ata *dev) return grub_errno; grub_memset (&parms, 0, sizeof (parms)); - parms.taskfile.disk = 0; + parms.taskfile.disk = 0xE0; parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE; parms.size = GRUB_DISK_SECTOR_SIZE; parms.buffer = info; @@ -108,7 +108,7 @@ grub_ata_identify (struct grub_ata *dev) grub_memset (&parms, 0, sizeof (parms)); parms.buffer = info; parms.size = GRUB_DISK_SECTOR_SIZE; - parms.taskfile.disk = 0; + parms.taskfile.disk = 0xE0; parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE; @@ -196,7 +196,7 @@ grub_ata_setaddress (struct grub_ata *dev, "sector %d cannot be addressed " "using CHS addressing", sector); - parms->taskfile.disk = head; + parms->taskfile.disk = 0xE0 | head; parms->taskfile.sectnum = sect; parms->taskfile.cyllsb = cylinder & 0xFF; parms->taskfile.cylmsb = cylinder >> 8; @@ -207,7 +207,7 @@ grub_ata_setaddress (struct grub_ata *dev, case GRUB_ATA_LBA: if (size == 256) size = 0; - parms->taskfile.disk = ((sector >> 24) & 0x0F); + parms->taskfile.disk = 0xE0 | ((sector >> 24) & 0x0F); parms->taskfile.sectors = size; parms->taskfile.lba_low = sector & 0xFF; @@ -219,7 +219,7 @@ grub_ata_setaddress (struct grub_ata *dev, if (size == 65536) size = 0; - parms->taskfile.disk = 0; + parms->taskfile.disk = 0xE0; /* Set "Previous". */ parms->taskfile.sectors = size & 0xFF; diff --git a/grub-core/disk/pata.c b/grub-core/disk/pata.c index 940a9a5f0..f9111bc5f 100644 --- a/grub-core/disk/pata.c +++ b/grub-core/disk/pata.c @@ -151,9 +151,8 @@ grub_pata_readwrite (struct grub_ata *disk, parms->taskfile.lba_low, parms->size); /* Set registers. */ - grub_pata_regset (dev, GRUB_ATA_REG_DISK, (parms->cmdsize ? 0 : 0xE0) - | dev->device << 4 - | (parms->taskfile.disk & 0xf)); + grub_pata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4) + | (parms->taskfile.disk & 0xef)); if (grub_pata_check_ready (dev)) return grub_errno; From d1e517eb20c2e5e14bdd8eea7168538f9f15a67c Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 25 Dec 2010 12:11:54 +0100 Subject: [PATCH 13/49] extend --disk-module to AHCI and USB --- grub-core/bus/usb/ohci.c | 8 ++++++-- grub-core/disk/ahci.c | 38 ++++++++++++++++++++++++++++++++++++++ util/grub-install.in | 14 +++++++++++--- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/grub-core/bus/usb/ohci.c b/grub-core/bus/usb/ohci.c index 8adaee6e0..577f35259 100644 --- a/grub-core/bus/usb/ohci.c +++ b/grub-core/bus/usb/ohci.c @@ -1422,18 +1422,22 @@ static struct grub_usb_controller_dev usb_controller = .detect_dev = grub_ohci_detect_dev }; +static void *fini_hnd; + GRUB_MOD_INIT(ohci) { COMPILE_TIME_ASSERT (sizeof (struct grub_ohci_td) == 32); COMPILE_TIME_ASSERT (sizeof (struct grub_ohci_ed) == 16); grub_ohci_inithw (); grub_usb_controller_dev_register (&usb_controller); - grub_loader_register_preboot_hook (grub_ohci_fini_hw, grub_ohci_restore_hw, - GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); + fini_hnd = grub_loader_register_preboot_hook (grub_ohci_fini_hw, + grub_ohci_restore_hw, + GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); } GRUB_MOD_FINI(ohci) { grub_ohci_fini_hw (0); + grub_loader_unregister_preboot_hook (fini_hnd); grub_usb_controller_dev_unregister (&usb_controller); } diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index 3160523ce..61ca7e4d6 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -25,6 +25,7 @@ #include #include #include +#include struct grub_ahci_cmd_head { @@ -296,6 +297,34 @@ grub_ahci_initialize (void) return grub_errno; } +static grub_err_t +grub_ahci_fini_hw (int noreturn __attribute__ ((unused))) +{ + struct grub_ahci_device *dev, *next; + + for (dev = grub_ahci_devices; dev; dev = next) + { + next = dev->next; + dev->hba->ports[dev->num].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; + while ((dev->hba->ports[dev->num].command & GRUB_AHCI_HBA_PORT_CMD_FR)); + dev->hba->ports[dev->num].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST; + while ((dev->hba->ports[dev->num].command & GRUB_AHCI_HBA_PORT_CMD_CR)); + grub_dma_free (dev->command_list_chunk); + grub_dma_free (dev->command_table_chunk); + grub_dma_free (dev->rfis); + + grub_free (dev); + } + grub_ahci_devices = NULL; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_ahci_restore_hw (void) +{ + return grub_ahci_initialize (); +} + @@ -533,6 +562,8 @@ static struct grub_ata_dev grub_ahci_dev = +static void *fini_hnd; + GRUB_MOD_INIT(ahci) { /* To prevent two drivers operating on the same disks. */ @@ -548,9 +579,16 @@ GRUB_MOD_INIT(ahci) /* 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); } diff --git a/util/grub-install.in b/util/grub-install.in index b9e833360..6259b1c4a 100644 --- a/util/grub-install.in +++ b/util/grub-install.in @@ -77,7 +77,7 @@ if [ "${target_cpu}-${platform}" = "i386-pc" ] ; then elif [ "${platform}" = "ieee1275" ] || [ "${platform}" = "efi" ] ; then disk_module= else - disk_module=ata + disk_module=native fi # Usage: usage @@ -114,7 +114,7 @@ Install GRUB on your drive. EOF if [ "${target_cpu}-${platform}" = "i386-pc" ] ; then cat < Date: Sat, 25 Dec 2010 15:47:23 +0100 Subject: [PATCH 14/49] Fix adressing mode mismatch --- grub-core/disk/ata.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c index 494325aa5..3fecb98dd 100644 --- a/grub-core/disk/ata.c +++ b/grub-core/disk/ata.c @@ -173,9 +173,10 @@ static grub_err_t grub_ata_setaddress (struct grub_ata *dev, struct grub_disk_ata_pass_through_parms *parms, grub_disk_addr_t sector, - grub_size_t size) + grub_size_t size, + grub_ata_addressing_t addressing) { - switch (dev->addr) + switch (addressing) { case GRUB_ATA_CHS: { @@ -297,7 +298,7 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch); grub_memset (&parms, 0, sizeof (parms)); - grub_ata_setaddress (ata, &parms, sector, batch); + grub_ata_setaddress (ata, &parms, sector, batch, addressing); parms.taskfile.cmd = (! rw ? cmd : cmd_write); parms.buffer = buf; parms.size = batch * GRUB_DISK_SECTOR_SIZE; From c76ae9b4da785c43dba8478768dd346f5c0b4d59 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 25 Dec 2010 17:40:00 +0100 Subject: [PATCH 15/49] set parms.write on disk write --- grub-core/disk/ata.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c index 3fecb98dd..c7cde3166 100644 --- a/grub-core/disk/ata.c +++ b/grub-core/disk/ata.c @@ -302,6 +302,7 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, parms.taskfile.cmd = (! rw ? cmd : cmd_write); parms.buffer = buf; parms.size = batch * GRUB_DISK_SECTOR_SIZE; + parms.write = rw; if (ata->dma) parms.dma = 1; From 9797178671d1044cf9fc13648152ea2ea5e76e41 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 25 Dec 2010 18:02:43 +0100 Subject: [PATCH 16/49] Accept ports in state 1 --- grub-core/disk/ahci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index 61ca7e4d6..b348ce3d0 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -220,13 +220,15 @@ grub_ahci_pciinit (grub_pci_device_t dev, struct grub_ahci_device *adev; struct grub_pci_dma_chunk *command_list; struct grub_pci_dma_chunk *command_table; + 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. */ - if ((hba->ports[i].status & 0xf) != 0x3) + st = hba->ports[i].status; + if ((st & 0xf) != 0x3 && (st & 0xf) != 0x1) continue; command_list = grub_memalign_dma32 (1024, From 7a1d4deec9267fc56ed50507b5a7fa9d96e3b610 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 3 Jan 2011 15:59:20 +0100 Subject: [PATCH 17/49] Fix finalisation/restore --- grub-core/disk/ahci.c | 255 +++++++++++++++++++++++++++++++----------- 1 file changed, 192 insertions(+), 63 deletions(-) diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index b348ce3d0..c67dbbe7e 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -157,6 +157,98 @@ enum 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))) @@ -209,7 +301,50 @@ grub_ahci_pciinit (grub_pci_device_t dev, else grub_dprintf ("ahci", "AHCI is already in OS mode\n"); - hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN; + 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; @@ -218,8 +353,6 @@ grub_ahci_pciinit (grub_pci_device_t dev, for (i = 0; i < nports; i++) { struct grub_ahci_device *adev; - struct grub_pci_dma_chunk *command_list; - struct grub_pci_dma_chunk *command_table; grub_uint32_t st; if (!(hba->ports_implemented & (1 << i))) @@ -231,60 +364,20 @@ grub_ahci_pciinit (grub_pci_device_t dev, if ((st & 0xf) != 0x3 && (st & 0xf) != 0x1) continue; - 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; - } - adev = grub_malloc (sizeof (*adev)); if (!adev) - { - grub_dma_free (command_list); - grub_dma_free (command_table); - return 1; - } - - grub_dprintf ("ahci", "found device ahci%d (port %d)\n", numdevs, i); - - hba->ports[i].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; - while ((hba->ports[i].command & GRUB_AHCI_HBA_PORT_CMD_FR)); - hba->ports[i].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST; - while ((hba->ports[i].command & GRUB_AHCI_HBA_PORT_CMD_CR)); - - hba->ports[i].fbs = 2; - - adev->rfis = grub_memalign_dma32 (4096, - sizeof (struct grub_ahci_received_fis)); - grub_memset ((char *) grub_dma_get_virt (adev->rfis), 0, - sizeof (struct grub_ahci_received_fis)); - hba->ports[i].fis_base = grub_dma_get_phys (adev->rfis); - hba->ports[i].command |= GRUB_AHCI_HBA_PORT_CMD_FRE; - while (!(hba->ports[i].command & GRUB_AHCI_HBA_PORT_CMD_FR)); - hba->ports[i].command |= GRUB_AHCI_HBA_PORT_CMD_ST; - while (!(hba->ports[i].command & GRUB_AHCI_HBA_PORT_CMD_CR)); - - hba->ports[i].command = (hba->ports[i].command & 0x0fffffff) - | (1 << 28) | 2 | 4; + return 1; adev->hba = hba; adev->port = i; adev->num = numdevs++; - adev->command_list_chunk = command_list; - adev->command_list = grub_dma_get_virt (command_list); - adev->command_table_chunk = command_table; - adev->command_table = grub_dma_get_virt (command_table); - adev->command_list->command_table_base - = grub_dma_get_phys (command_table); - adev->hba->ports[i].command_list_base = grub_dma_get_phys (command_list); + if (init_port (adev)) + { + grub_free (adev); + return 1; + } + grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices), GRUB_AS_LIST (adev)); } @@ -302,29 +395,53 @@ grub_ahci_initialize (void) static grub_err_t grub_ahci_fini_hw (int noreturn __attribute__ ((unused))) { - struct grub_ahci_device *dev, *next; + struct grub_ahci_device *dev; - for (dev = grub_ahci_devices; dev; dev = next) + for (dev = grub_ahci_devices; dev; dev = dev->next) { - next = dev->next; - dev->hba->ports[dev->num].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; - while ((dev->hba->ports[dev->num].command & GRUB_AHCI_HBA_PORT_CMD_FR)); - dev->hba->ports[dev->num].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST; - while ((dev->hba->ports[dev->num].command & GRUB_AHCI_HBA_PORT_CMD_CR)); + 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); - - grub_free (dev); + dev->command_list_chunk = NULL; + dev->command_table_chunk = NULL; + dev->rfis = NULL; } - grub_ahci_devices = NULL; return GRUB_ERR_NONE; } static grub_err_t grub_ahci_restore_hw (void) { - return grub_ahci_initialize (); + 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; } @@ -391,9 +508,21 @@ grub_ahci_readwrite_real (struct grub_ahci_device *dev, if ((dev->hba->ports[dev->port].task_file_data & 0x80)) { 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)); + 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; - while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)); + 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 start CR\n"); + break; + } } dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error; From 09573499ff62ebbeabfedc45dd8106ba715cbdf3 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Tue, 29 Mar 2011 02:02:55 +0200 Subject: [PATCH 18/49] Initial variable sector size support --- grub-core/disk/efi/efidisk.c | 17 ++++--- grub-core/disk/i386/pc/biosdisk.c | 52 +++++++++++---------- grub-core/disk/scsi.c | 36 +++++---------- grub-core/kern/disk.c | 77 +++++++++++++++++++++++-------- grub-core/kern/emu/hostdisk.c | 37 ++++++++++----- grub-core/partmap/msdos.c | 22 ++++++--- include/grub/disk.h | 10 ++-- 7 files changed, 155 insertions(+), 96 deletions(-) diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c index 08094fa5c..20fea956f 100644 --- a/grub-core/disk/efi/efidisk.c +++ b/grub-core/disk/efi/efidisk.c @@ -536,8 +536,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; + if (m->blocksize & (m->blocksize - 1) || !m->blocksize) + return grub_error (GRUB_ERR_IO, "invalid sector size %d", + m->blocksize); + for (disk->log_sector_size = 0; + (1 << disk->log_sector_size) < m->blocksize; + disk->log_sector_size++); disk->data = d; grub_dprintf ("efidisk", "opening %s succeeded\n", name); @@ -571,8 +576,8 @@ grub_efidisk_read (struct grub_disk *disk, grub_disk_addr_t sector, (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, + (grub_efi_uint64_t) sector << disk->log_sector_size, + (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"); @@ -599,8 +604,8 @@ grub_efidisk_write (struct grub_disk *disk, grub_disk_addr_t sector, (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, + (grub_efi_uint64_t) sector << disk->log_sector_size, + (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"); diff --git a/grub-core/disk/i386/pc/biosdisk.c b/grub-core/disk/i386/pc/biosdisk.c index 069bb0b59..6e48fff43 100644 --- a/grub-core/disk/i386/pc/biosdisk.c +++ b/grub-core/disk/i386/pc/biosdisk.c @@ -338,7 +338,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; } @@ -347,6 +348,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) { @@ -367,6 +370,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++); + } } } } @@ -429,7 +441,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; @@ -443,9 +455,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; @@ -501,10 +510,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_uint32_t offset; + struct grub_biosdisk_data *data = disk->data; + grub_uint32_t sectors = data->sectors; /* OFFSET = SECTOR % SECTORS */ grub_divmod64 (sector, sectors, &offset); @@ -512,8 +523,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; } @@ -522,21 +533,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; @@ -545,9 +546,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; } @@ -568,18 +570,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; } diff --git a/grub-core/disk/scsi.c b/grub-core/disk/scsi.c index a40de278f..0d4734dc0 100644 --- a/grub-core/disk/scsi.c +++ b/grub-core/disk/scsi.c @@ -463,15 +463,20 @@ 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; @@ -501,25 +506,6 @@ 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) - { - 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"); - - grub_uint32_t sector_mod = 0; - sector = grub_divmod64 (sector, spb, §or_mod); - - if (! (sector_mod == 0 && size % spb == 0)) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unaligned SCSI read not supported"); - - size /= spb; - } - /* Depending on the type, select a read function. */ switch (scsi->devtype) { diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index 807ee4277..af56527ad 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -247,6 +247,7 @@ grub_disk_open (const char *name) disk = (grub_disk_t) grub_zalloc (sizeof (*disk)); if (! disk) return 0; + disk->log_sector_size = GRUB_DISK_SECTOR_BITS; p = find_part_sep (name); if (p) @@ -266,7 +267,6 @@ grub_disk_open (const char *name) if (! disk->name) goto fail; - for (dev = grub_disk_dev_list; dev; dev = dev->next) { if ((dev->open) (raw, disk) == GRUB_ERR_NONE) @@ -282,6 +282,14 @@ grub_disk_open (const char *name) grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such disk"); goto fail; } + if (disk->log_sector_size > GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS + || disk->log_sector_size < GRUB_DISK_SECTOR_BITS) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "sector sizes of %d bytes aren't supported yet", + (1 << disk->log_sector_size)); + goto fail; + } disk->dev = dev; @@ -373,14 +381,23 @@ grub_disk_adjust_range (grub_disk_t disk, grub_disk_addr_t *sector, *sector += start; } - if (disk->total_sectors <= *sector - || ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) - >> GRUB_DISK_SECTOR_BITS) > disk->total_sectors - *sector) + if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN + && ((disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)) <= *sector + || ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) + >> GRUB_DISK_SECTOR_BITS) > (disk->total_sectors + << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)) - *sector)) return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of disk"); return GRUB_ERR_NONE; } +static inline grub_disk_addr_t +transform_sector (grub_disk_t disk, grub_disk_addr_t sector) +{ + return sector >> (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); +} + /* Read data from the disk. */ grub_err_t grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, @@ -433,27 +450,39 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, else { /* Otherwise read data from the disk actually. */ - if (start_sector + GRUB_DISK_CACHE_SIZE > disk->total_sectors - || (disk->dev->read) (disk, start_sector, - GRUB_DISK_CACHE_SIZE, tmp_buf) + if ((disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN + && start_sector + GRUB_DISK_CACHE_SIZE + > (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS))) + || (disk->dev->read) (disk, transform_sector (disk, start_sector), + 1 << (GRUB_DISK_CACHE_BITS + + GRUB_DISK_SECTOR_BITS + - disk->log_sector_size), tmp_buf) != GRUB_ERR_NONE) { /* Uggh... Failed. Instead, just read necessary data. */ unsigned num; char *p; + grub_disk_addr_t aligned_sector; grub_errno = GRUB_ERR_NONE; - num = ((size + real_offset + GRUB_DISK_SECTOR_SIZE - 1) - >> GRUB_DISK_SECTOR_BITS); + aligned_sector = (sector & ~((1 << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)) + - 1)); + real_offset += ((sector - aligned_sector) + << GRUB_DISK_SECTOR_BITS); + num = ((size + real_offset + (1 << (disk->log_sector_size)) + - 1) >> (disk->log_sector_size)); - p = grub_realloc (tmp_buf, num << GRUB_DISK_SECTOR_BITS); + p = grub_realloc (tmp_buf, num << disk->log_sector_size); if (!p) goto finish; tmp_buf = p; - if ((disk->dev->read) (disk, sector, num, tmp_buf)) + if ((disk->dev->read) (disk, transform_sector (disk, + aligned_sector), + num, tmp_buf)) { grub_error_push (); grub_dprintf ("disk", "%s read failed\n", disk->name); @@ -528,25 +557,31 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector, grub_off_t offset, grub_size_t size, const void *buf) { unsigned real_offset; + grub_disk_addr_t aligned_sector; grub_dprintf ("disk", "Writing `%s'...\n", disk->name); if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE) return -1; - real_offset = offset; + aligned_sector = (sector & ~((1 << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)) - 1)); + real_offset = offset + ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS); + sector = aligned_sector; while (size) { - if (real_offset != 0 || (size < GRUB_DISK_SECTOR_SIZE && size != 0)) + if (real_offset != 0 || (size < (1U << disk->log_sector_size) + && size != 0)) { - char tmp_buf[GRUB_DISK_SECTOR_SIZE]; + char tmp_buf[1 << disk->log_sector_size]; grub_size_t len; grub_partition_t part; part = disk->partition; disk->partition = 0; - if (grub_disk_read (disk, sector, 0, GRUB_DISK_SECTOR_SIZE, tmp_buf) + if (grub_disk_read (disk, sector, + 0, (1 << disk->log_sector_size), tmp_buf) != GRUB_ERR_NONE) { disk->partition = part; @@ -554,7 +589,7 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector, } disk->partition = part; - len = GRUB_DISK_SECTOR_SIZE - real_offset; + len = (1 << disk->log_sector_size) - real_offset; if (len > size) len = size; @@ -565,7 +600,7 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector, if ((disk->dev->write) (disk, sector, 1, tmp_buf) != GRUB_ERR_NONE) goto finish; - sector++; + sector += (1 << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); buf = (char *) buf + len; size -= len; real_offset = 0; @@ -575,8 +610,8 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t len; grub_size_t n; - len = size & ~(GRUB_DISK_SECTOR_SIZE - 1); - n = size >> GRUB_DISK_SECTOR_BITS; + len = size & ~((1 << disk->log_sector_size) - 1); + n = size >> disk->log_sector_size; if ((disk->dev->write) (disk, sector, n, buf) != GRUB_ERR_NONE) goto finish; @@ -599,6 +634,8 @@ grub_disk_get_size (grub_disk_t disk) { if (disk->partition) return grub_partition_get_len (disk->partition); + else if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN) + return disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); else - return disk->total_sectors; + return GRUB_DISK_SIZE_UNKNOWN; } diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c index 73d023ce9..f082ada89 100644 --- a/grub-core/kern/emu/hostdisk.c +++ b/grub-core/kern/emu/hostdisk.c @@ -42,6 +42,7 @@ #ifdef __linux__ # include /* ioctl */ +# include # if !defined(__GLIBC__) || \ ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) /* Maybe libc doesn't have large file support. */ @@ -264,6 +265,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) # else unsigned long long nr; # endif + int sector_size; int fd; fd = open (map[drive].device, O_RDONLY); @@ -295,16 +297,28 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) goto fail; } + if (ioctl (fd, BLKSSZGET, §or_size)) + { + close (fd); + goto fail; + } + close (fd); + if (sector_size & (sector_size - 1) || !sector_size) + goto fail; + for (disk->log_sector_size = 0; + (1 << disk->log_sector_size) < sector_size; + disk->log_sector_size++); + # if defined (__APPLE__) disk->total_sectors = nr; # elif defined(__NetBSD__) disk->total_sectors = label.d_secperunit; # else - disk->total_sectors = nr / 512; + disk->total_sectors = nr >> disk->log_sector_size; - if (nr % 512) + if (nr & ((1 << disk->log_sector_size) - 1)) grub_util_error ("unaligned device size"); # endif @@ -321,7 +335,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) if (stat (map[drive].device, &st) < 0) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot stat `%s'", map[drive].device); - disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS; + disk->total_sectors = st.st_size >> disk->log_sector_size; grub_util_info ("the size of %s is %lu", name, disk->total_sectors); @@ -760,7 +774,7 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, loff_t *, res, uint, wh); - offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS; + offset = (loff_t) sector << disk->log_sector_size; if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) { grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device); @@ -770,7 +784,7 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) } #else { - off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS; + off_t offset = (off_t) sector << disk->log_sector_size; if (lseek (fd, offset, SEEK_SET) != offset) { @@ -870,20 +884,21 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, sectors that are read together with the MBR in one read. It should only remap the MBR, so we split the read in two parts. -jochen */ - if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE) + if (nread (fd, buf, (1 << disk->log_sector_size)) + != (1 << disk->log_sector_size)) { grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device); close (fd); return grub_errno; } - buf += GRUB_DISK_SECTOR_SIZE; + buf += (1 << disk->log_sector_size); size--; } #endif /* __linux__ */ - if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS) - != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) + if (nread (fd, buf, size << disk->log_sector_size) + != (ssize_t) (size << disk->log_sector_size)) grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device); return grub_errno; @@ -916,8 +931,8 @@ grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, if (fd < 0) return grub_errno; - if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS) - != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) + if (nwrite (fd, buf, size << disk->log_sector_size) + != (ssize_t) (size << disk->log_sector_size)) grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device); return grub_errno; diff --git a/grub-core/partmap/msdos.c b/grub-core/partmap/msdos.c index 31a0a0707..c2e726bd7 100644 --- a/grub-core/partmap/msdos.c +++ b/grub-core/partmap/msdos.c @@ -90,8 +90,11 @@ grub_partition_msdos_iterate (grub_disk_t disk, { e = mbr.entries + p.index; - p.start = p.offset + grub_le_to_cpu32 (e->start) - delta; - p.len = grub_le_to_cpu32 (e->length); + p.start = p.offset + + (grub_le_to_cpu32 (e->start) + << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)) - delta; + p.len = grub_le_to_cpu32 (e->length) + << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); p.msdostype = e->type; grub_dprintf ("partition", @@ -126,7 +129,9 @@ grub_partition_msdos_iterate (grub_disk_t disk, if (grub_msdos_partition_is_extended (e->type)) { - p.offset = ext_offset + grub_le_to_cpu32 (e->start); + p.offset = ext_offset + + (grub_le_to_cpu32 (e->start) + << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); if (! ext_offset) ext_offset = p.offset; @@ -204,8 +209,11 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, e = mbr.entries + i; if (!grub_msdos_partition_is_empty (e->type) - && end > offset + grub_le_to_cpu32 (e->start)) - end = offset + grub_le_to_cpu32 (e->start); + && end > offset + + (grub_le_to_cpu32 (e->start) + << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS))) + end = offset + (grub_le_to_cpu32 (e->start) + << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); /* If this is a GPT partition, this MBR is just a dummy. */ if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0) @@ -219,7 +227,9 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, if (grub_msdos_partition_is_extended (e->type)) { - offset = ext_offset + grub_le_to_cpu32 (e->start); + offset = ext_offset + + (grub_le_to_cpu32 (e->start) + << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); if (! ext_offset) ext_offset = offset; diff --git a/include/grub/disk.h b/include/grub/disk.h index 66db1149a..b46d6670a 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -100,6 +100,9 @@ struct grub_disk /* The total number of sectors. */ grub_uint64_t total_sectors; + /* Logarithm of sector size. */ + unsigned int log_sector_size; + /* The id used by the disk cache manager. */ unsigned long id; @@ -132,9 +135,10 @@ typedef struct grub_disk_memberlist *grub_disk_memberlist_t; /* The maximum number of disk caches. */ #define GRUB_DISK_CACHE_NUM 1021 -/* The size of a disk cache in sector units. */ -#define GRUB_DISK_CACHE_SIZE 8 -#define GRUB_DISK_CACHE_BITS 3 +/* The size of a disk cache in 512B units. Must be at least as big as the + largest supported sector size, currently 16K. */ +#define GRUB_DISK_CACHE_BITS 6 +#define GRUB_DISK_CACHE_SIZE (1 << GRUB_DISK_CACHE_BITS) /* Return value of grub_disk_get_size() in case disk size is unknown. */ #define GRUB_DISK_SIZE_UNKNOWN 0xffffffffffffffffULL From 6ac14e6cef8b3dc675fc09239b2101dcc4393a18 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Tue, 5 Apr 2011 09:34:58 +0200 Subject: [PATCH 19/49] Fix EFI compilation --- grub-core/disk/efi/efidisk.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c index 20fea956f..1bf764f10 100644 --- a/grub-core/disk/efi/efidisk.c +++ b/grub-core/disk/efi/efidisk.c @@ -537,11 +537,11 @@ grub_efidisk_open (const char *name, struct grub_disk *disk) 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; - if (m->blocksize & (m->blocksize - 1) || !m->blocksize) + if (m->block_size & (m->block_size - 1) || !m->block_size) return grub_error (GRUB_ERR_IO, "invalid sector size %d", - m->blocksize); + m->block_size); for (disk->log_sector_size = 0; - (1 << disk->log_sector_size) < m->blocksize; + (1U << disk->log_sector_size) < m->block_size; disk->log_sector_size++); disk->data = d; From bd671cc4fe27784df6bacb57d83f691a84e3f5f0 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 15 Apr 2011 18:15:06 +0200 Subject: [PATCH 20/49] Use Block IO on EFI --- grub-core/disk/efi/efidisk.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c index 1bf764f10..35602513e 100644 --- a/grub-core/disk/efi/efidisk.c +++ b/grub-core/disk/efi/efidisk.c @@ -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; @@ -143,7 +141,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 +153,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 +165,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 +181,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; } @@ -563,22 +557,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 << disk->log_sector_size, - (grub_efi_uintn_t) size << disk->log_sector_size, - 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"); @@ -591,20 +583,18 @@ 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 << disk->log_sector_size, + 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) From d8a2bcf564728cc38b138fed18cd89b097493e75 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 15 Apr 2011 21:42:29 +0200 Subject: [PATCH 21/49] contiguous read --- grub-core/kern/disk.c | 296 +++++++++++++++++++++++++----------------- 1 file changed, 178 insertions(+), 118 deletions(-) diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index af56527ad..5439a02f6 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -398,13 +398,93 @@ transform_sector (grub_disk_t disk, grub_disk_addr_t sector) return sector >> (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); } +/* Small read (less than cache size and not pass across cache unit boundaries). + sector is already adjusted and is divisible by cache unit size. + */ +static grub_err_t +grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector, + grub_off_t offset, grub_size_t size, void *buf) +{ + char *data; + char *tmp_buf; + + /* Fetch the cache. */ + data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector); + if (data) + { + /* Just copy it! */ + grub_memcpy (buf, data + offset, size); + grub_disk_cache_unlock (disk->dev->id, disk->id, sector); + return GRUB_ERR_NONE; + } + + /* Allocate a temporary buffer. */ + tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); + if (! tmp_buf) + return grub_errno; + + /* Otherwise read data from the disk actually. */ + if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN + || sector + GRUB_DISK_CACHE_SIZE + < (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS))) + { + grub_err_t err; + err = (disk->dev->read) (disk, transform_sector (disk, sector), + 1 << (GRUB_DISK_CACHE_BITS + + GRUB_DISK_SECTOR_BITS + - disk->log_sector_size), tmp_buf); + if (!err) + { + /* Copy it and store it in the disk cache. */ + grub_memcpy (buf, tmp_buf + offset, size); + grub_disk_cache_store (disk->dev->id, disk->id, + sector, tmp_buf); + grub_free (tmp_buf); + return GRUB_ERR_NONE; + } + } + + grub_errno = GRUB_ERR_NONE; + + { + /* Uggh... Failed. Instead, just read necessary data. */ + unsigned num; + grub_disk_addr_t aligned_sector; + + sector += (offset >> GRUB_DISK_SECTOR_BITS); + offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1); + aligned_sector = (sector & ~((1 << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)) + - 1)); + offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS); + num = ((size + offset + (1 << (disk->log_sector_size)) + - 1) >> (disk->log_sector_size)); + + tmp_buf = grub_malloc (num << disk->log_sector_size); + if (!tmp_buf) + return grub_errno; + + if ((disk->dev->read) (disk, transform_sector (disk, aligned_sector), + num, tmp_buf)) + { + grub_error_push (); + grub_dprintf ("disk", "%s read failed\n", disk->name); + grub_error_pop (); + return grub_errno; + } + grub_memcpy (buf, tmp_buf + offset, size); + return GRUB_ERR_NONE; + } +} + /* Read data from the disk. */ grub_err_t grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, grub_off_t offset, grub_size_t size, void *buf) { - char *tmp_buf; - unsigned real_offset; + grub_off_t real_offset; + grub_disk_addr_t real_sector; + grub_size_t real_size; /* First of all, check if the region is within the disk. */ if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE) @@ -416,138 +496,118 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, return grub_errno; } + real_sector = sector; real_offset = offset; + real_size = size; - /* Allocate a temporary buffer. */ - tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); - if (! tmp_buf) - return grub_errno; - - /* Until SIZE is zero... */ - while (size) + /* First read until first cache boundary. */ + if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1))) { - char *data; grub_disk_addr_t start_sector; - grub_size_t len; grub_size_t pos; + grub_err_t err; + grub_size_t len; - /* For reading bulk data. */ start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1); pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS; len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS) - - pos - real_offset); + - pos - offset); if (len > size) len = size; - - /* Fetch the cache. */ - data = grub_disk_cache_fetch (disk->dev->id, disk->id, start_sector); - if (data) - { - /* Just copy it! */ - grub_memcpy (buf, data + pos + real_offset, len); - grub_disk_cache_unlock (disk->dev->id, disk->id, start_sector); - } - else - { - /* Otherwise read data from the disk actually. */ - if ((disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN - && start_sector + GRUB_DISK_CACHE_SIZE - > (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS))) - || (disk->dev->read) (disk, transform_sector (disk, start_sector), - 1 << (GRUB_DISK_CACHE_BITS - + GRUB_DISK_SECTOR_BITS - - disk->log_sector_size), tmp_buf) - != GRUB_ERR_NONE) - { - /* Uggh... Failed. Instead, just read necessary data. */ - unsigned num; - char *p; - grub_disk_addr_t aligned_sector; - - grub_errno = GRUB_ERR_NONE; - - aligned_sector = (sector & ~((1 << (disk->log_sector_size - - GRUB_DISK_SECTOR_BITS)) - - 1)); - real_offset += ((sector - aligned_sector) - << GRUB_DISK_SECTOR_BITS); - num = ((size + real_offset + (1 << (disk->log_sector_size)) - - 1) >> (disk->log_sector_size)); - - p = grub_realloc (tmp_buf, num << disk->log_sector_size); - if (!p) - goto finish; - - tmp_buf = p; - - if ((disk->dev->read) (disk, transform_sector (disk, - aligned_sector), - num, tmp_buf)) - { - grub_error_push (); - grub_dprintf ("disk", "%s read failed\n", disk->name); - grub_error_pop (); - goto finish; - } - - grub_memcpy (buf, tmp_buf + real_offset, size); - - /* Call the read hook, if any. */ - if (disk->read_hook) - while (size) - { - grub_size_t to_read = (size > GRUB_DISK_SECTOR_SIZE) ? GRUB_DISK_SECTOR_SIZE : size; - (disk->read_hook) (sector, real_offset, - to_read); - if (grub_errno != GRUB_ERR_NONE) - goto finish; - - sector++; - size -= to_read - real_offset; - real_offset = 0; - } - - /* This must be the end. */ - goto finish; - } - - /* Copy it and store it in the disk cache. */ - grub_memcpy (buf, tmp_buf + pos + real_offset, len); - grub_disk_cache_store (disk->dev->id, disk->id, - start_sector, tmp_buf); - } - - /* Call the read hook, if any. */ - if (disk->read_hook) - { - grub_disk_addr_t s = sector; - grub_size_t l = len; - - while (l) - { - (disk->read_hook) (s, real_offset, - ((l > GRUB_DISK_SECTOR_SIZE) - ? GRUB_DISK_SECTOR_SIZE - : l)); - - if (l < GRUB_DISK_SECTOR_SIZE - real_offset) - break; - - s++; - l -= GRUB_DISK_SECTOR_SIZE - real_offset; - real_offset = 0; - } - } - - sector = start_sector + GRUB_DISK_CACHE_SIZE; + err = grub_disk_read_small (disk, start_sector, + offset + pos, len, buf); + if (err) + return err; buf = (char *) buf + len; size -= len; - real_offset = 0; + offset += len; + sector += (offset >> GRUB_DISK_SECTOR_BITS); + offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1); } - finish: + /* Until SIZE is zero... */ + while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS)) + { + char *data = NULL; + grub_disk_addr_t agglomerate; + grub_err_t err; - grub_free (tmp_buf); + /* agglomerate read until we find a first cached entry. */ + for (agglomerate = 0; agglomerate + < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS)); + agglomerate++) + { + data = grub_disk_cache_fetch (disk->dev->id, disk->id, + sector + (agglomerate + << GRUB_DISK_CACHE_BITS)); + if (data) + break; + } + + if (agglomerate) + { + grub_disk_addr_t i; + + err = (disk->dev->read) (disk, transform_sector (disk, sector), + agglomerate << (GRUB_DISK_CACHE_BITS + + GRUB_DISK_SECTOR_BITS + - disk->log_sector_size), + buf); + if (err) + return err; + + for (i = 0; i < agglomerate; i ++) + grub_disk_cache_store (disk->dev->id, disk->id, + sector + (i << GRUB_DISK_CACHE_BITS), + (char *) buf + + (i << (GRUB_DISK_CACHE_BITS + + GRUB_DISK_SECTOR_BITS))); + + sector += agglomerate << GRUB_DISK_CACHE_BITS; + size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS); + buf = (char *) buf + + (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS)); + } + + if (data) + { + grub_memcpy (buf, data, GRUB_DISK_CACHE_SIZE); + sector += GRUB_DISK_CACHE_SIZE; + buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); + size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); + grub_disk_cache_unlock (disk->dev->id, disk->id, + sector + (agglomerate + << GRUB_DISK_CACHE_BITS)); + } + } + + /* And now read the last part. */ + if (size) + { + grub_err_t err; + err = grub_disk_read_small (disk, sector, 0, size, buf); + if (err) + return err; + } + + /* Call the read hook, if any. */ + if (disk->read_hook) + { + grub_disk_addr_t s = real_sector; + grub_size_t l = real_size; + grub_off_t o = real_offset; + + while (l) + { + (disk->read_hook) (s, o, + ((l > GRUB_DISK_SECTOR_SIZE) + ? GRUB_DISK_SECTOR_SIZE + : l)); + s++; + l -= GRUB_DISK_SECTOR_SIZE - o; + o = 0; + } + } return grub_errno; } From 0c512b092c885733eb31aa1a12c6e7289c5e5ea1 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 14:52:00 +0200 Subject: [PATCH 22/49] Fix a cache retrieving bug --- grub-core/kern/disk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index 5439a02f6..30bc604f0 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -571,7 +571,8 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, if (data) { - grub_memcpy (buf, data, GRUB_DISK_CACHE_SIZE); + grub_memcpy (buf, data, + GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); sector += GRUB_DISK_CACHE_SIZE; buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); From c64db050f7f8669fa0b7e27811c869febbeaa848 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 27 May 2011 13:52:21 +0100 Subject: [PATCH 23/49] * grub-core/kern/emu/hostdisk.c (linux_find_partition): Give up after ten consecutive open failures. Scanning all the way up to 10000 is excessive and can cause serious performance problems in some configurations. Fixes Ubuntu bug #787461. --- ChangeLog | 8 ++++++++ grub-core/kern/emu/hostdisk.c | 9 ++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 198750f6a..351700803 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2011-05-27 Colin Watson + + * grub-core/kern/emu/hostdisk.c (linux_find_partition): Give up + after ten consecutive open failures. Scanning all the way up to + 10000 is excessive and can cause serious performance problems in + some configurations. + Fixes Ubuntu bug #787461. + 2011-05-21 Vladimir Serbinenko * grub-core/disk/arc/arcdisk.c (reopen): Close old handle before diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c index d633059c3..e404c4fea 100644 --- a/grub-core/kern/emu/hostdisk.c +++ b/grub-core/kern/emu/hostdisk.c @@ -564,6 +564,7 @@ linux_find_partition (char *dev, grub_disk_addr_t sector) int i; char real_dev[PATH_MAX]; struct linux_partition_cache *cache; + int missing = 0; strcpy(real_dev, dev); @@ -602,7 +603,13 @@ linux_find_partition (char *dev, grub_disk_addr_t sector) fd = open (real_dev, O_RDONLY); if (fd == -1) - continue; + { + if (missing++ < 10) + continue; + else + return 0; + } + missing = 0; close (fd); start = find_partition_start (real_dev); From 6b4e643081999c9fe388ad5bd8ad96812777fc76 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 27 May 2011 13:57:22 +0100 Subject: [PATCH 24/49] * grub-core/kern/emu/hostdisk.c (linux_find_partition): Handle partitions under /dev/disk/by-id/. --- ChangeLog | 5 +++++ grub-core/kern/emu/hostdisk.c | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/ChangeLog b/ChangeLog index 351700803..b9abcd07f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2011-05-27 Colin Watson + + * grub-core/kern/emu/hostdisk.c (linux_find_partition): Handle + partitions under /dev/disk/by-id/. + 2011-05-27 Colin Watson * grub-core/kern/emu/hostdisk.c (linux_find_partition): Give up diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c index e404c4fea..bb528d9bc 100644 --- a/grub-core/kern/emu/hostdisk.c +++ b/grub-core/kern/emu/hostdisk.c @@ -573,6 +573,12 @@ linux_find_partition (char *dev, grub_disk_addr_t sector) p = real_dev + len - 4; format = "part%d"; } + else if (strncmp (real_dev, "/dev/disk/by-id/", + sizeof ("/dev/disk/by-id/") - 1) == 0) + { + p = real_dev + len; + format = "-part%d"; + } else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9') { p = real_dev + len; From 351c7c8a155d1c3ace2cbefb9682c5b862082d98 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Sun, 29 May 2011 22:15:08 +0100 Subject: [PATCH 25/49] * docs/grub.texi (Obtaining and Building GRUB): Substitute `ftp.gnu.org' for `alpha.gnu.org'. --- ChangeLog | 5 +++++ docs/grub.texi | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index b9abcd07f..fca980c1b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2011-05-29 Colin Watson + + * docs/grub.texi (Obtaining and Building GRUB): Substitute + `ftp.gnu.org' for `alpha.gnu.org'. + 2011-05-27 Colin Watson * grub-core/kern/emu/hostdisk.c (linux_find_partition): Handle diff --git a/docs/grub.texi b/docs/grub.texi index 818417d41..45c93d69c 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4330,11 +4330,11 @@ how to get the latest version. @end quotation GRUB is available from the GNU alpha archive site -@uref{ftp://alpha.gnu.org/gnu/grub} or any of its mirrors. The file +@uref{ftp://ftp.gnu.org/gnu/grub} or any of its mirrors. The file will be named grub-version.tar.gz. The current version is @value{VERSION}, so the file you should grab is: -@uref{ftp://alpha.gnu.org/gnu/grub/grub-@value{VERSION}.tar.gz} +@uref{ftp://ftp.gnu.org/gnu/grub/grub-@value{VERSION}.tar.gz} To unbundle GRUB use the instruction: From 1e9aef7d963e86016cbb0568c5f9d01eee31fd99 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jun 2011 17:35:50 +0100 Subject: [PATCH 26/49] * docs/man/grub-mklayout.h2m (DESCRIPTION): Add a reference to the input format. --- ChangeLog | 5 +++++ docs/man/grub-mklayout.h2m | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/ChangeLog b/ChangeLog index fca980c1b..5ac0ffb5c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2011-06-13 Colin Watson + + * docs/man/grub-mklayout.h2m (DESCRIPTION): Add a reference to the + input format. + 2011-05-29 Colin Watson * docs/grub.texi (Obtaining and Building GRUB): Substitute diff --git a/docs/man/grub-mklayout.h2m b/docs/man/grub-mklayout.h2m index cda6f3676..1e43409c0 100644 --- a/docs/man/grub-mklayout.h2m +++ b/docs/man/grub-mklayout.h2m @@ -1,4 +1,10 @@ [NAME] grub-mklayout \- generate a GRUB keyboard layout file +[DESCRIPTION] +grub-mklayout processes a keyboard layout description in +.BR keymaps (5) +format into a format that can be used by GRUB's +.B keymap +command. [SEE ALSO] .BR grub-mkconfig (8) From 881ac815d00ecf1aa9093b015edf8c2da334d191 Mon Sep 17 00:00:00 2001 From: Robert Millan Date: Thu, 16 Jun 2011 14:33:11 +0200 Subject: [PATCH 27/49] 2011-06-16 Robert Millan Detect `ataraid' devices on GNU/kFreeBSD. Fix for ATA devices using `ata' driver on kernel of FreeBSD 9. * util/deviceiter.c [__FreeBSD_kernel__] (get_ada_disk_name) (get_ataraid_disk_name): New functions. [__FreeBSD_kernel__] (grub_util_iterate_devices): Scan for ataraid (/dev/ar[0-9]+) and ada (/dev/ada[0-9]+) devices using get_ataraid_disk_name() and get_ada_disk_name(). --- ChangeLog | 11 +++++++++++ util/deviceiter.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 5ac0ffb5c..043b858a5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2011-06-16 Robert Millan + + Detect `ataraid' devices on GNU/kFreeBSD. Fix for ATA devices using + `ata' driver on kernel of FreeBSD 9. + + * util/deviceiter.c [__FreeBSD_kernel__] (get_ada_disk_name) + (get_ataraid_disk_name): New functions. + [__FreeBSD_kernel__] (grub_util_iterate_devices): Scan for ataraid + (/dev/ar[0-9]+) and ada (/dev/ada[0-9]+) devices using + get_ataraid_disk_name() and get_ada_disk_name(). + 2011-06-13 Colin Watson * docs/man/grub-mklayout.h2m (DESCRIPTION): Add a reference to the diff --git a/util/deviceiter.c b/util/deviceiter.c index 30c18beea..2a8acec0e 100644 --- a/util/deviceiter.c +++ b/util/deviceiter.c @@ -1,7 +1,7 @@ /* deviceiter.c - iterate over system devices */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,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 @@ -286,6 +286,20 @@ get_scsi_disk_name (char *name, int unit) #endif } +#ifdef __FreeBSD_kernel__ +static void +get_ada_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/ada%d", unit); +} + +static void +get_ataraid_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/ar%d", unit); +} +#endif + #ifdef __linux__ static void get_virtio_disk_name (char *name, int unit) @@ -620,6 +634,35 @@ grub_util_iterate_devices (int NESTED_FUNC_ATTR (*hook) (const char *, int), } } +#ifdef __FreeBSD_kernel__ + /* IDE disks using ATA Direct Access driver. */ + if (get_kfreebsd_version () >= 800000) + for (i = 0; i < 96; i++) + { + char name[16]; + + get_ada_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0)) + goto out; + } + } + + /* ATARAID disks. */ + for (i = 0; i < 8; i++) + { + char name[20]; + + get_ataraid_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0)) + goto out; + } + } +#endif + #ifdef __linux__ /* Virtio disks. */ for (i = 0; i < 26; i++) From 77ba5392d0e482de3618c00129c7bc2a35691cce Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 08:45:12 +0200 Subject: [PATCH 28/49] New testload grub-fstest command --- Makefile.util.def | 1 + util/grub-fstest.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile.util.def b/Makefile.util.def index 829b16fac..1fcd9465e 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -34,6 +34,7 @@ library = { common_nodist = grub_script.tab.h; common = grub-core/commands/blocklist.c; + common = grub-core/commands/testload.c; common = grub-core/commands/extcmd.c; common = grub-core/commands/ls.c; common = grub-core/disk/dmraid_nvidia.c; diff --git a/util/grub-fstest.c b/util/grub-fstest.c index f253a96f6..c2c8b1148 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -61,7 +61,8 @@ enum { CMD_CMP, CMD_HEX, CMD_CRC, - CMD_BLOCKLIST + CMD_BLOCKLIST, + CMD_TESTLOAD }; #define BUF_SIZE 32256 @@ -359,6 +360,9 @@ fstest (int n, char **args) case CMD_BLOCKLIST: execute_command ("blocklist", n, args); grub_printf ("\n"); + case CMD_TESTLOAD: + execute_command ("testload", n, args); + grub_printf ("\n"); } for (i = 0; i < num_disks; i++) @@ -520,6 +524,11 @@ argp_parser (int key, char *arg, struct argp_state *state) cmd = CMD_BLOCKLIST; nparm = 1; } + else if (!grub_strcmp (arg, "testload")) + { + cmd = CMD_TESTLOAD; + nparm = 1; + } else { fprintf (stderr, _("Invalid command %s.\n"), arg); From 2df2e89fee627a098b0192a526a2b6999699211c Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 09:39:12 +0200 Subject: [PATCH 29/49] Fixed a cache collision bug. Thanks guufy1 for bugreport and test --- grub-core/kern/disk.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index 30bc604f0..f1456defd 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -544,6 +544,17 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, break; } + if (data) + { + grub_memcpy ((char *) buf + + (agglomerate << (GRUB_DISK_CACHE_BITS + + GRUB_DISK_SECTOR_BITS)), + data, GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); + grub_disk_cache_unlock (disk->dev->id, disk->id, + sector + (agglomerate + << GRUB_DISK_CACHE_BITS)); + } + if (agglomerate) { grub_disk_addr_t i; @@ -568,17 +579,12 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, buf = (char *) buf + (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS)); } - + if (data) { - grub_memcpy (buf, data, - GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); sector += GRUB_DISK_CACHE_SIZE; buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); - grub_disk_cache_unlock (disk->dev->id, disk->id, - sector + (agglomerate - << GRUB_DISK_CACHE_BITS)); } } From 8a5d6919e169d5839c474a5c0e8d8ed6b492fef1 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 23 Jun 2011 18:08:53 +0200 Subject: [PATCH 30/49] Avoid NULL deref in grub_device_open. * grub-core/kern/device.c (grub_device_open): Don't dereference a NULL pointer upon failed grub_env_get. --- ChangeLog | 6 ++++++ grub-core/kern/device.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index e3ed5d13a..a7f6ce738 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2011-06-23 Jim Meyering + + avoid NULL deref in grub_device_open + * grub-core/kern/device.c (grub_device_open): Don't dereference + a NULL pointer upon failed grub_env_get. + 2011-06-23 Vladimir Serbinenko Support non-512B sectors and agglomerate reads. diff --git a/grub-core/kern/device.c b/grub-core/kern/device.c index 3db14f50e..50f49ae6b 100644 --- a/grub-core/kern/device.c +++ b/grub-core/kern/device.c @@ -35,7 +35,7 @@ grub_device_open (const char *name) if (! name) { name = grub_env_get ("root"); - if (*name == '\0') + if (name == NULL || *name == '\0') { grub_error (GRUB_ERR_BAD_DEVICE, "no device is set"); goto fail; From e2d1dba0ae8d87c9f9fc8ecbb4b10243507b2615 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 19:50:41 +0200 Subject: [PATCH 31/49] * grub-core/kern/emu/getroot.c (grub_guess_root_device): Don't accept /dev/root as a valid device. --- ChangeLog | 8 +++++++- grub-core/kern/emu/getroot.c | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index a7f6ce738..0ea09a0ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ +2011-06-23 Vladimir Serbinenko + + * grub-core/kern/emu/getroot.c (grub_guess_root_device): Don't accept + /dev/root as a valid device. + 2011-06-23 Jim Meyering - avoid NULL deref in grub_device_open + Avoid NULL deref in grub_device_open. + * grub-core/kern/device.c (grub_device_open): Don't dereference a NULL pointer upon failed grub_env_get. diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index 6e49cc31b..db27abf74 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -616,13 +616,15 @@ grub_guess_root_device (const char *dir) if (os_dev) { - if (strncmp (os_dev, "/dev/dm-", sizeof ("/dev/dm-") - 1) != 0) + int dm = (strncmp (os_dev, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0); + int root = (strcmp (os_dev, "/dev/root") == 0); + if (!dm && !root) return os_dev; if (stat (os_dev, &st) < 0) grub_util_error ("cannot stat `%s'", os_dev); free (os_dev); dev = st.st_rdev; - return grub_find_device ("/dev/mapper", dev); + return grub_find_device (dm ? "/dev/mapper" : "/dev", dev); } if (stat (dir, &st) < 0) From a199a8cd1a5d01847e21bb40f21e044f4d728244 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 20:05:39 +0200 Subject: [PATCH 32/49] Fix spurious warning. * grub-core/partmap/acorn.c (grub_acorn_boot_block): Make a union. (acorn_partition_map_find): Use .bin member. --- ChangeLog | 7 +++++++ grub-core/partmap/acorn.c | 19 +++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0ea09a0ab..a455d9b6b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2011-06-23 Vladimir Serbinenko + + Fix spurious warning. + + * grub-core/partmap/acorn.c (grub_acorn_boot_block): Make a union. + (acorn_partition_map_find): Use .bin member. + 2011-06-23 Vladimir Serbinenko * grub-core/kern/emu/getroot.c (grub_guess_root_device): Don't accept diff --git a/grub-core/partmap/acorn.c b/grub-core/partmap/acorn.c index 9a68ddd92..341b8ac5f 100644 --- a/grub-core/partmap/acorn.c +++ b/grub-core/partmap/acorn.c @@ -34,11 +34,18 @@ GRUB_MOD_LICENSE ("GPLv3+"); struct grub_acorn_boot_block { - grub_uint8_t misc[0x1C0]; - struct grub_filecore_disc_record disc_record; - grub_uint8_t flags; - grub_uint16_t start_cylinder; - grub_uint8_t checksum; + union + { + struct + { + grub_uint8_t misc[0x1C0]; + struct grub_filecore_disc_record disc_record; + grub_uint8_t flags; + grub_uint16_t start_cylinder; + grub_uint8_t checksum; + } __attribute__ ((packed, aligned)); + grub_uint8_t bin[0x200]; + }; } __attribute__ ((packed, aligned)); struct linux_part @@ -71,7 +78,7 @@ acorn_partition_map_find (grub_disk_t disk, struct linux_part *m, goto fail; for (i = 0; i != 0x1ff; ++i) - checksum = (checksum & 0xff) + (checksum >> 8) + boot.misc[i]; + checksum = ((checksum & 0xff) + (checksum >> 8) + boot.bin[i]); if ((grub_uint8_t) checksum != boot.checksum) goto fail; From e98c83e910e7fd3e39dd021213d111526e80775c Mon Sep 17 00:00:00 2001 From: David Volgyes Date: Thu, 23 Jun 2011 20:28:04 +0200 Subject: [PATCH 33/49] * grub-core/bus/usb/ohci.c (grub_ohci_pci_iter): Avoid NULL-pointer dereference. --- ChangeLog | 5 +++++ grub-core/bus/usb/ohci.c | 10 ++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index a455d9b6b..ad72c8da2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2011-06-23 David Volgyes + + * grub-core/bus/usb/ohci.c (grub_ohci_pci_iter): Avoid NULL-pointer + dereference. + 2011-06-23 Vladimir Serbinenko Fix spurious warning. diff --git a/grub-core/bus/usb/ohci.c b/grub-core/bus/usb/ohci.c index df0d0f4af..7e8eaaac2 100644 --- a/grub-core/bus/usb/ohci.c +++ b/grub-core/bus/usb/ohci.c @@ -454,10 +454,12 @@ grub_ohci_pci_iter (grub_pci_device_t dev, fail: if (o) - grub_dma_free (o->td_chunk); - grub_dma_free (o->ed_bulk_chunk); - grub_dma_free (o->ed_ctrl_chunk); - grub_dma_free (o->hcca_chunk); + { + grub_dma_free (o->td_chunk); + grub_dma_free (o->ed_bulk_chunk); + grub_dma_free (o->ed_ctrl_chunk); + grub_dma_free (o->hcca_chunk); + } grub_free (o); return 0; From fbc626665f458d60cc3d19e1e8eedb68638cb4dc Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 20:38:19 +0200 Subject: [PATCH 34/49] * grub-core/loader/i386/xnu.c (grub_cpu_xnu_fill_devprop): Don't attempt to continue if allocation is failed. Reported by: David Volgyes . --- ChangeLog | 7 +++++++ grub-core/loader/i386/xnu.c | 10 +++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index ad72c8da2..66c55d101 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2011-06-23 Vladimir Serbinenko + + * grub-core/loader/i386/xnu.c (grub_cpu_xnu_fill_devprop): Don't attempt + to continue if allocation is failed. + + Reported by: David Volgyes . + 2011-06-23 David Volgyes * grub-core/bus/usb/ohci.c (grub_ohci_pci_iter): Avoid NULL-pointer diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c index b877b0ea5..6128ec384 100644 --- a/grub-core/loader/i386/xnu.c +++ b/grub-core/loader/i386/xnu.c @@ -452,11 +452,11 @@ grub_cpu_xnu_fill_devprop (void) } devprop = grub_xnu_create_value (&(efikey->first_child), "device-properties"); - if (devprop) - { - devprop->data = grub_malloc (total_length); - devprop->datasize = total_length; - } + if (!devprop) + return grub_errno; + + devprop->data = grub_malloc (total_length); + devprop->datasize = total_length; ptr = devprop->data; head = ptr; From 1abe47dc9935106c0079d0e50388d2c6168864a5 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 20:55:36 +0200 Subject: [PATCH 35/49] * grub-core/lib/reed_solomon.c (rs_recover): Prevent memory leak. (main): Close file. --- ChangeLog | 5 +++++ grub-core/lib/reed_solomon.c | 25 +++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 66c55d101..c267a8a0f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2011-06-23 Vladimir Serbinenko + + * grub-core/lib/reed_solomon.c (rs_recover): Prevent memory leak. + (main): Close file. + 2011-06-23 Vladimir Serbinenko * grub-core/loader/i386/xnu.c (grub_cpu_xnu_fill_devprop): Don't attempt diff --git a/grub-core/lib/reed_solomon.c b/grub-core/lib/reed_solomon.c index e500ba32d..8b5e8235a 100644 --- a/grub-core/lib/reed_solomon.c +++ b/grub-core/lib/reed_solomon.c @@ -265,6 +265,22 @@ rs_recover (gf_single_t *m, grub_size_t s, grub_size_t rs) syndroms (m, s, rs, sy); + for (i = 0; i < (int) rs; i++) + if (sy[i] != 0) + break; + + /* No error detected. */ + if (i == (int) rs) + { +#ifndef STANDALONE + free (sigma); + free (errpot); + free (errpos); + free (sy); +#endif + return; + } + { gf_single_t *eq; @@ -275,14 +291,6 @@ rs_recover (gf_single_t *m, grub_size_t s, grub_size_t rs) scratch += rs2 * (rs2 + 1) * sizeof (gf_single_t); #endif - for (i = 0; i < (int) rs; i++) - if (sy[i] != 0) - break; - - /* No error detected. */ - if (i == (int) rs) - return; - for (i = 0; i < (int) rs2; i++) for (j = 0; j < (int) rs2 + 1; j++) eq[i * (rs2 + 1) + j] = sy[i+j]; @@ -504,6 +512,7 @@ main (int argc, char **argv) rs = s / 3; buf = xmalloc (s + rs + SECTOR_SIZE); fread (buf, 1, s, in); + fclose (in); grub_reed_solomon_add_redundancy (buf, s, rs); From e061a1b537b1f8b719bfa69c49bdf39877fdf4d7 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 20:58:35 +0200 Subject: [PATCH 36/49] * grub-core/lib/reed_solomon.c (rs_recover) [STANDALONE]: Prevent memory leak. --- ChangeLog | 6 ++++++ grub-core/lib/reed_solomon.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/ChangeLog b/ChangeLog index c267a8a0f..327b52afc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,13 @@ +2011-06-23 Vladimir Serbinenko + + * grub-core/lib/reed_solomon.c (rs_recover) [STANDALONE]: + Prevent memory leak. + 2011-06-23 Vladimir Serbinenko * grub-core/lib/reed_solomon.c (rs_recover): Prevent memory leak. (main): Close file. + Reported by: David Volgyes . 2011-06-23 Vladimir Serbinenko diff --git a/grub-core/lib/reed_solomon.c b/grub-core/lib/reed_solomon.c index 8b5e8235a..365b76003 100644 --- a/grub-core/lib/reed_solomon.c +++ b/grub-core/lib/reed_solomon.c @@ -277,6 +277,11 @@ rs_recover (gf_single_t *m, grub_size_t s, grub_size_t rs) free (errpot); free (errpos); free (sy); +#else + scratch -= rs2 * sizeof (gf_single_t); + scratch -= rs2 * sizeof (gf_single_t); + scratch -= rs2 * sizeof (int); + scratch -= rs * sizeof (gf_single_t); #endif return; } From 13548d26e9492450da911aa5b7c5d9e6850cb327 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 21:06:50 +0200 Subject: [PATCH 37/49] * util/raid.c (grub_util_raid_getmembers): Close fd before returning. Reported by: David Volgyes . --- ChangeLog | 6 ++++++ util/raid.c | 2 ++ 2 files changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 327b52afc..058116fbb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2011-06-23 Vladimir Serbinenko + + * util/raid.c (grub_util_raid_getmembers): Close fd before returning. + + Reported by: David Volgyes . + 2011-06-23 Vladimir Serbinenko * grub-core/lib/reed_solomon.c (rs_recover) [STANDALONE]: diff --git a/util/raid.c b/util/raid.c index a6aa5f95e..c1c32b959 100644 --- a/util/raid.c +++ b/util/raid.c @@ -80,6 +80,8 @@ grub_util_raid_getmembers (const char *name) devicelist[j] = NULL; + close (fd); + return devicelist; } From cad3237fb5ecf1a21f9e1bad957942a0cac8d6cc Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 22:02:05 +0200 Subject: [PATCH 38/49] * grub-core/kern/emu/hostdisk.c (grub_util_biosdisk_is_floppy): Close file after stat. Reported by: David Volgyes . --- ChangeLog | 6 ++++++ grub-core/kern/emu/hostdisk.c | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 058116fbb..abe285ee9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2011-06-23 Vladimir Serbinenko + + * grub-core/kern/emu/hostdisk.c (grub_util_biosdisk_is_floppy): Close + file after stat. + Reported by: David Volgyes . + 2011-06-23 Vladimir Serbinenko * util/raid.c (grub_util_raid_getmembers): Close fd before returning. diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c index e53a39c39..f4af73858 100644 --- a/grub-core/kern/emu/hostdisk.c +++ b/grub-core/kern/emu/hostdisk.c @@ -1866,7 +1866,12 @@ grub_util_biosdisk_is_floppy (grub_disk_t disk) /* Shouldn't happen either. */ if (fstat (fd, &st) < 0) - return 0; + { + close (fd); + return 0; + } + + close (fd); #if defined(__NetBSD__) if (major(st.st_rdev) == RAW_FLOPPY_MAJOR) From 03147f46671e2270ef4849a710b20572462844ac Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 22:11:42 +0200 Subject: [PATCH 39/49] * util/grub-mkpasswd-pbkdf2.c (main): Don't double-close. Reported by: David Volgyes . --- ChangeLog | 5 +++++ util/grub-mkpasswd-pbkdf2.c | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index abe285ee9..59cb3ec57 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2011-06-23 Vladimir Serbinenko + + * util/grub-mkpasswd-pbkdf2.c (main): Don't double-close. + Reported by: David Volgyes . + 2011-06-23 Vladimir Serbinenko * grub-core/kern/emu/hostdisk.c (grub_util_biosdisk_is_floppy): Close diff --git a/util/grub-mkpasswd-pbkdf2.c b/util/grub-mkpasswd-pbkdf2.c index dc2afdb6e..709ba7da7 100644 --- a/util/grub-mkpasswd-pbkdf2.c +++ b/util/grub-mkpasswd-pbkdf2.c @@ -270,7 +270,6 @@ main (int argc, char *argv[]) free (bufhex); free (salthex); free (salt); - fclose (f); grub_util_error ("couldn't retrieve random data for salt"); } fclose (f); From fe12fd5b439956ab5354dde8699870cb0fd7c8bd Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 23 Jun 2011 22:18:31 +0200 Subject: [PATCH 40/49] * util/ieee1275/ofpath.c (check_sas): Close fd. (main): Free of_path. Reported by: David Volgyes . --- ChangeLog | 6 ++++++ util/ieee1275/ofpath.c | 2 ++ 2 files changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 59cb3ec57..062f66c00 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2011-06-23 Vladimir Serbinenko + + * util/ieee1275/ofpath.c (check_sas): Close fd. + (main): Free of_path. + Reported by: David Volgyes . + 2011-06-23 Vladimir Serbinenko * util/grub-mkpasswd-pbkdf2.c (main): Don't double-close. diff --git a/util/ieee1275/ofpath.c b/util/ieee1275/ofpath.c index 1a433345d..f72bea8a5 100644 --- a/util/ieee1275/ofpath.c +++ b/util/ieee1275/ofpath.c @@ -297,6 +297,7 @@ check_sas (char *sysfs_path, int *tgt) free (path); free (p); + close (fd); } static void @@ -419,6 +420,7 @@ int main(int argc, char **argv) of_path = grub_util_devname_to_ofpath (argv[1]); printf("%s\n", of_path); + free (of_path); return 0; } From c31dc5f5e4291e2aa7fb9fbf5ed88640fb05cc83 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Thu, 23 Jun 2011 23:13:20 +0200 Subject: [PATCH 41/49] Add support for DRI and RSTn markers in JPEG files. * grub-core/video/readers/jpeg.c (JPEG_MARKER_DRI): New define. (JPEG_MARKER_RST0): Likewise. (JPEG_MARKER_RST1): Likewise. (JPEG_MARKER_RST2): Likewise. (JPEG_MARKER_RST3): Likewise. (JPEG_MARKER_RST4): Likewise. (JPEG_MARKER_RST5): Likewise. (JPEG_MARKER_RST6): Likewise. (JPEG_MARKER_RST7): Likewise. (grub_jpeg_data): New fields dri, r1, bitmap_ptr. (grub_jpeg_decode_dri): New function. (grub_jpeg_decode_sos): Move image data related part into grub_jpeg_decode_data function. (grub_jpeg_decode_data): New function. (grub_jpeg_reset): New function. (grub_jpeg_decode_jpeg): Handle new markers. --- ChangeLog | 21 +++++++++ grub-core/video/readers/jpeg.c | 78 ++++++++++++++++++++++++++++------ 2 files changed, 87 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 062f66c00..655cbe25e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2011-06-23 Szymon Janc + + Add support for DRI and RSTn markers in JPEG files. + + * grub-core/video/readers/jpeg.c (JPEG_MARKER_DRI): New define. + (JPEG_MARKER_RST0): Likewise. + (JPEG_MARKER_RST1): Likewise. + (JPEG_MARKER_RST2): Likewise. + (JPEG_MARKER_RST3): Likewise. + (JPEG_MARKER_RST4): Likewise. + (JPEG_MARKER_RST5): Likewise. + (JPEG_MARKER_RST6): Likewise. + (JPEG_MARKER_RST7): Likewise. + (grub_jpeg_data): New fields dri, r1, bitmap_ptr. + (grub_jpeg_decode_dri): New function. + (grub_jpeg_decode_sos): Move image data related part into + grub_jpeg_decode_data function. + (grub_jpeg_decode_data): New function. + (grub_jpeg_reset): New function. + (grub_jpeg_decode_jpeg): Handle new markers. + 2011-06-23 Vladimir Serbinenko * util/ieee1275/ofpath.c (check_sas): Close fd. diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index 8cdb2f61d..d61c90ec9 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -39,6 +39,15 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define JPEG_MARKER_DQT 0xdb #define JPEG_MARKER_SOF0 0xc0 #define JPEG_MARKER_SOS 0xda +#define JPEG_MARKER_DRI 0xdd +#define JPEG_MARKER_RST0 0xd0 +#define JPEG_MARKER_RST1 0xd1 +#define JPEG_MARKER_RST2 0xd2 +#define JPEG_MARKER_RST3 0xd3 +#define JPEG_MARKER_RST4 0xd4 +#define JPEG_MARKER_RST5 0xd5 +#define JPEG_MARKER_RST6 0xd6 +#define JPEG_MARKER_RST7 0xd7 #define SHIFT_BITS 8 #define CONST(x) ((int) ((x) * (1L << SHIFT_BITS) + 0.5)) @@ -66,6 +75,7 @@ struct grub_jpeg_data { grub_file_t file; struct grub_video_bitmap **bitmap; + grub_uint8_t *bitmap_ptr; int image_width; int image_height; @@ -82,6 +92,8 @@ struct grub_jpeg_data jpeg_data_unit_t cbdu; int vs, hs; + int dri; + int r1; int dc_value[3]; @@ -315,6 +327,18 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data) return grub_errno; } +static grub_err_t +grub_jpeg_decode_dri (struct grub_jpeg_data *data) +{ + if (grub_jpeg_get_word (data) != 4) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: DRI marker length must be 4"); + + data->dri = grub_jpeg_get_word (data); + + return grub_errno; +} + static void grub_jpeg_idct_transform (jpeg_data_unit_t du) { @@ -526,8 +550,7 @@ grub_jpeg_ycrcb_to_rgb (int yy, int cr, int cb, grub_uint8_t * rgb) static grub_err_t grub_jpeg_decode_sos (struct grub_jpeg_data *data) { - int i, cc, r1, c1, nr1, nc1, vb, hb; - grub_uint8_t *ptr1; + int i, cc; grub_uint32_t data_offset; data_offset = data->file->offset; @@ -563,17 +586,25 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) GRUB_VIDEO_BLIT_FORMAT_RGB_888)) return grub_errno; - data->bit_mask = 0x0; + data->bitmap_ptr = (*data->bitmap)->data; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_jpeg_decode_data (struct grub_jpeg_data *data) +{ + int c1, vb, hb, nr1, nc1; + int rst = data->dri; vb = data->vs * 8; hb = data->hs * 8; nr1 = (data->image_height + vb - 1) / vb; nc1 = (data->image_width + hb - 1) / hb; - ptr1 = (*data->bitmap)->data; - for (r1 = 0; r1 < nr1; - r1++, ptr1 += (vb * data->image_width - hb * nc1) * 3) - for (c1 = 0; c1 < nc1; c1++, ptr1 += hb * 3) + for (; data->r1 < nr1 && (!data->dri || rst); + data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3) + for (c1 = 0; c1 < nc1 && (!data->dri || rst); + c1++, rst--, data->bitmap_ptr += hb * 3) { int r2, c2, nr2, nc2; grub_uint8_t *ptr2; @@ -588,10 +619,10 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) if (grub_errno) return grub_errno; - nr2 = (r1 == nr1 - 1) ? (data->image_height - r1 * vb) : vb; + nr2 = (data->r1 == nr1 - 1) ? (data->image_height - data->r1 * vb) : vb; nc2 = (c1 == nc1 - 1) ? (data->image_width - c1 * hb) : hb; - ptr2 = ptr1; + ptr2 = data->bitmap_ptr; for (r2 = 0; r2 < nr2; r2++, ptr2 += (data->image_width - nc2) * 3) for (c2 = 0; c2 < nc2; c2++, ptr2 += 3) { @@ -600,8 +631,7 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) i0 = (r2 / data->vs) * 8 + (c2 / data->hs); cr = data->crdu[i0]; cb = data->cbdu[i0]; - yy = - data->ydu[(r2 / 8) * 2 + (c2 / 8)][(r2 % 8) * 8 + (c2 % 8)]; + yy = data->ydu[(r2 / 8) * 2 + (c2 / 8)][(r2 % 8) * 8 + (c2 % 8)]; grub_jpeg_ycrcb_to_rgb (yy, cr, cb, ptr2); } @@ -610,6 +640,16 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) return grub_errno; } +static void +grub_jpeg_reset (struct grub_jpeg_data *data) +{ + data->bit_mask = 0x0; + + data->dc_value[0] = 0; + data->dc_value[1] = 0; + data->dc_value[2] = 0; +} + static grub_uint8_t grub_jpeg_get_marker (struct grub_jpeg_data *data) { @@ -655,8 +695,22 @@ grub_jpeg_decode_jpeg (struct grub_jpeg_data *data) case JPEG_MARKER_SOF0: /* Start Of Frame 0. */ grub_jpeg_decode_sof (data); break; + case JPEG_MARKER_DRI: /* Define Restart Interval. */ + grub_jpeg_decode_dri (data); + break; case JPEG_MARKER_SOS: /* Start Of Scan. */ - grub_jpeg_decode_sos (data); + if (grub_jpeg_decode_sos (data)) + break; + case JPEG_MARKER_RST0: /* Restart. */ + case JPEG_MARKER_RST1: + case JPEG_MARKER_RST2: + case JPEG_MARKER_RST3: + case JPEG_MARKER_RST4: + case JPEG_MARKER_RST5: + case JPEG_MARKER_RST6: + case JPEG_MARKER_RST7: + grub_jpeg_decode_data (data); + grub_jpeg_reset (data); break; case JPEG_MARKER_EOI: /* End Of Image. */ return grub_errno; From 005dd67cb68ece8ea1af43c5c11096d0d32614ea Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Jun 2011 00:29:21 +0200 Subject: [PATCH 42/49] * grub-core/disk/ahci.c: Add missing license statements. * grub-core/fs/romfs.c: Likewise. * grub-core/lib/ia64/setjmp.S: Likewise. * grub-core/loader/i386/pc/freedos.c: Likewise. * grub-core/loader/ia64/efi/linux.c: Likewise. * grub-core/video/colors.c: Likewise. * include/grub/dl.h (GRUB_MOD_DEP): New macro. --- grub-core/disk/ahci.c | 2 ++ grub-core/fs/romfs.c | 2 ++ grub-core/lib/ia64/setjmp.S | 6 ++++++ grub-core/loader/i386/pc/freedos.c | 2 ++ grub-core/loader/ia64/efi/linux.c | 2 ++ grub-core/video/colors.c | 3 +++ include/grub/dl.h | 3 +++ 7 files changed, 20 insertions(+) diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index 699df767c..0f355aa53 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -27,6 +27,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + struct grub_ahci_cmd_head { grub_uint32_t config; diff --git a/grub-core/fs/romfs.c b/grub-core/fs/romfs.c index 07632e635..58dc98f34 100644 --- a/grub-core/fs/romfs.c +++ b/grub-core/fs/romfs.c @@ -24,6 +24,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + struct grub_romfs_superblock { char magic[8]; diff --git a/grub-core/lib/ia64/setjmp.S b/grub-core/lib/ia64/setjmp.S index 0851885c5..190623d35 100644 --- a/grub-core/lib/ia64/setjmp.S +++ b/grub-core/lib/ia64/setjmp.S @@ -64,6 +64,12 @@ 0x1b0 f30 0x1c0 f31 */ +#include +#include + + .file "setjmp.S" + +GRUB_MOD_LICENSE ("GPLv2+") /* The following two entry points are the traditional entry points: */ diff --git a/grub-core/loader/i386/pc/freedos.c b/grub-core/loader/i386/pc/freedos.c index 0ae815490..f796e08f4 100644 --- a/grub-core/loader/i386/pc/freedos.c +++ b/grub-core/loader/i386/pc/freedos.c @@ -33,6 +33,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static grub_dl_t my_mod; static struct grub_relocator *rel; static grub_uint32_t ebx = 0xffffffff; diff --git a/grub-core/loader/ia64/efi/linux.c b/grub-core/loader/ia64/efi/linux.c index b018e4549..247eebae5 100644 --- a/grub-core/loader/ia64/efi/linux.c +++ b/grub-core/loader/ia64/efi/linux.c @@ -31,6 +31,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + #define ALIGN_MIN (256*1024*1024) #define GRUB_ELF_SEARCH 1024 diff --git a/grub-core/video/colors.c b/grub-core/video/colors.c index 0637c5508..3119c0249 100644 --- a/grub-core/video/colors.c +++ b/grub-core/video/colors.c @@ -21,6 +21,9 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); struct named_color { diff --git a/include/grub/dl.h b/include/grub/dl.h index 5ce01199c..75cd71758 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -90,6 +90,9 @@ static const char grub_module_name_##name[] \ #ifndef ASM_FILE #define GRUB_MOD_LICENSE(license) \ static char grub_module_license[] __attribute__ ((section (GRUB_MOD_SECTION (module_license)), used)) = "LICENSE=" license; +#define GRUB_MOD_DEP(name) \ +static const char grub_module_depend_##name[] \ + __attribute__((section(GRUB_MOD_SECTION(moddeps)), __used__)) = #name #else #define GRUB_MOD_LICENSE(license) \ .section GRUB_MOD_SECTION(module_license), "a"; \ From 290766fb770aa9d3ebd3ada989ae3b24d21a093a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Jun 2011 00:31:29 +0200 Subject: [PATCH 43/49] ZFS zlib support * grub-core/fs/zfs/zfs.c (zlib_decompress): New function. (decomp_table): Add zlib entries. (zio_read): USe 8 bits for compression function rather than 3. * include/grub/zfs/zio.h (zio_compress): Add zlib values. --- ChangeLog | 19 +++++++++++++++++++ grub-core/fs/zfs/zfs.c | 22 ++++++++++++++++++++-- include/grub/zfs/zio.h | 10 +++++++++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6535b6e17..954166148 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2011-06-24 Vladimir Serbinenko + + ZFS zlib support + + * grub-core/fs/zfs/zfs.c (zlib_decompress): New function. + (decomp_table): Add zlib entries. + (zio_read): USe 8 bits for compression function rather than 3. + * include/grub/zfs/zio.h (zio_compress): Add zlib values. + +2011-06-24 Vladimir Serbinenko + + * grub-core/disk/ahci.c: Add missing license statements. + * grub-core/fs/romfs.c: Likewise. + * grub-core/lib/ia64/setjmp.S: Likewise. + * grub-core/loader/i386/pc/freedos.c: Likewise. + * grub-core/loader/ia64/efi/linux.c: Likewise. + * grub-core/video/colors.c: Likewise. + * include/grub/dl.h (GRUB_MOD_DEP): New macro. + 2011-06-23 Vladimir Serbinenko AHCI support. diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 8d86cf9e5..1eea13b26 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -51,6 +51,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -163,13 +164,30 @@ struct grub_zfs_data grub_disk_addr_t vdev_phys_sector; }; +static grub_err_t +zlib_decompress (void *s, void *d, + grub_size_t slen, grub_size_t dlen) +{ + if (grub_zlib_decompress (s, slen, 0, d, dlen) < 0) + return grub_errno; + return GRUB_ERR_NONE; +} + static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = { {"inherit", NULL}, /* ZIO_COMPRESS_INHERIT */ {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */ {"off", NULL}, /* ZIO_COMPRESS_OFF */ {"lzjb", lzjb_decompress}, /* ZIO_COMPRESS_LZJB */ {"empty", NULL}, /* ZIO_COMPRESS_EMPTY */ - {"gzip", NULL}, /* ZIO_COMPRESS_GZIP */ + {"gzip-1", zlib_decompress}, /* ZIO_COMPRESS_GZIP1 */ + {"gzip-2", zlib_decompress}, /* ZIO_COMPRESS_GZIP2 */ + {"gzip-3", zlib_decompress}, /* ZIO_COMPRESS_GZIP3 */ + {"gzip-4", zlib_decompress}, /* ZIO_COMPRESS_GZIP4 */ + {"gzip-5", zlib_decompress}, /* ZIO_COMPRESS_GZIP5 */ + {"gzip-6", zlib_decompress}, /* ZIO_COMPRESS_GZIP6 */ + {"gzip-7", zlib_decompress}, /* ZIO_COMPRESS_GZIP7 */ + {"gzip-8", zlib_decompress}, /* ZIO_COMPRESS_GZIP8 */ + {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */ }; static grub_err_t zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, @@ -527,7 +545,7 @@ zio_read (blkptr_t * bp, grub_zfs_endian_t endian, void **buf, *buf = NULL; checksum = (grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff; - comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0x7; + comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0xff; lsize = (BP_IS_HOLE(bp) ? 0 : (((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) & 0xffff) + 1) << SPA_MINBLOCKSHIFT)); diff --git a/include/grub/zfs/zio.h b/include/grub/zfs/zio.h index 797d4f9b3..3dafb4028 100644 --- a/include/grub/zfs/zio.h +++ b/include/grub/zfs/zio.h @@ -77,7 +77,15 @@ enum zio_compress { ZIO_COMPRESS_OFF, ZIO_COMPRESS_LZJB, ZIO_COMPRESS_EMPTY, - ZIO_COMPRESS_GZIP, + ZIO_COMPRESS_GZIP1, + ZIO_COMPRESS_GZIP2, + ZIO_COMPRESS_GZIP3, + ZIO_COMPRESS_GZIP4, + ZIO_COMPRESS_GZIP5, + ZIO_COMPRESS_GZIP6, + ZIO_COMPRESS_GZIP7, + ZIO_COMPRESS_GZIP8, + ZIO_COMPRESS_GZIP9, ZIO_COMPRESS_FUNCTIONS }; From bc09e1a23804bd4132a51bca01851135d7307bff Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Jun 2011 04:32:54 +0200 Subject: [PATCH 44/49] * grub-core/kern/emu/getroot.c (grub_guess_root_device): Revert to old method if mountinfo would return /dev/root and /dev/root doesn't exist. --- ChangeLog | 6 ++++++ grub-core/kern/emu/getroot.c | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 954166148..1b63af871 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2011-06-24 Vladimir Serbinenko + + * grub-core/kern/emu/getroot.c (grub_guess_root_device): Revert to + old method if mountinfo would return /dev/root and /dev/root doesn't + exist. + 2011-06-24 Vladimir Serbinenko ZFS zlib support diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index db27abf74..6d6c8ef8b 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -620,11 +620,13 @@ grub_guess_root_device (const char *dir) int root = (strcmp (os_dev, "/dev/root") == 0); if (!dm && !root) return os_dev; - if (stat (os_dev, &st) < 0) - grub_util_error ("cannot stat `%s'", os_dev); + if (stat (os_dev, &st) >= 0) + { + free (os_dev); + dev = st.st_rdev; + return grub_find_device (dm ? "/dev/mapper" : "/dev", dev); + } free (os_dev); - dev = st.st_rdev; - return grub_find_device (dm ? "/dev/mapper" : "/dev", dev); } if (stat (dir, &st) < 0) From 535c191059459b9212991f59aea494db1c1da767 Mon Sep 17 00:00:00 2001 From: Alexander Kurtz Date: Fri, 24 Jun 2011 12:38:24 +0200 Subject: [PATCH 45/49] * util/grub-mkconfig_lib.in: Add missing quotes. --- ChangeLog | 4 ++++ util/grub-mkconfig_lib.in | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 1b63af871..d5c97b6e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2011-06-24 Alexander Kurtz + + * util/grub-mkconfig_lib.in: Add missing quotes. + 2011-06-24 Vladimir Serbinenko * grub-core/kern/emu/getroot.c (grub_guess_root_device): Revert to diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 2c5fd8c6f..759bcba1b 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -63,7 +63,7 @@ is_path_readable_by_grub () # ... or if we can't figure out the abstraction module, for example if # memberlist fails on an LVM volume group. - if ${grub_probe} -t abstraction $path > /dev/null 2>&1 ; then : ; else + if "${grub_probe}" -t abstraction "$path" > /dev/null 2>&1 ; then : ; else return 1 fi From 2a5e94d84abd9efad39e7eafe68fb766cea87314 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Jun 2011 12:41:05 +0200 Subject: [PATCH 46/49] * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Load partmap before abstraction. --- ChangeLog | 5 +++++ util/grub-mkconfig_lib.in | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index d5c97b6e0..1e7a468ec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2011-06-24 Vladimir Serbinenko + + * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Load + partmap before abstraction. + 2011-06-24 Alexander Kurtz * util/grub-mkconfig_lib.in: Add missing quotes. diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 759bcba1b..090fcf314 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -105,12 +105,6 @@ prepare_grub_to_access_device () { device="$1" - # Abstraction modules aren't auto-loaded. - abstraction="`"${grub_probe}" --device "${device}" --target=abstraction`" - for module in ${abstraction} ; do - echo "insmod ${module}" - done - partmap="`"${grub_probe}" --device "${device}" --target=partmap`" for module in ${partmap} ; do case "${module}" in @@ -121,6 +115,12 @@ prepare_grub_to_access_device () esac done + # Abstraction modules aren't auto-loaded. + abstraction="`"${grub_probe}" --device "${device}" --target=abstraction`" + for module in ${abstraction} ; do + echo "insmod ${module}" + done + fs="`"${grub_probe}" --device "${device}" --target=fs`" for module in ${fs} ; do echo "insmod ${module}" From 98e2f50661b63494a7d94d3ad04d31669468f9b4 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Jun 2011 13:02:49 +0200 Subject: [PATCH 47/49] * grub-core/fs/iso9660.c (grub_iso9660_iterate_dir): Skip . and .. --- ChangeLog | 4 ++++ grub-core/fs/iso9660.c | 11 ++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1e7a468ec..d21fa1026 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2011-06-24 Vladimir Serbinenko + + * grub-core/fs/iso9660.c (grub_iso9660_iterate_dir): Skip . and .. + 2011-06-24 Vladimir Serbinenko * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Load diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c index 508cc5b71..5b53ca597 100644 --- a/grub-core/fs/iso9660.c +++ b/grub-core/fs/iso9660.c @@ -598,7 +598,7 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir, return 0; } - while (offset < dir->size) + for (; offset < dir->size; offset += dirent.len) { if (grub_disk_read (dir->data->disk, (dir->blk << GRUB_ISO9660_LOG2_BLKSZ) @@ -676,10 +676,9 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir, if (filename) *filename = '\0'; - if (dirent.namelen == 1 && name[0] == 0) - filename = "."; - else if (dirent.namelen == 1 && name[0] == 1) - filename = ".."; + /* . and .. */ + if (dirent.namelen == 1 && (name[0] == 0 || name[0] == 1)) + continue; else filename = name; } @@ -712,8 +711,6 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir, if (filename_alloc) grub_free (filename); } - - offset += dirent.len; } return 0; From 4388ca7224904dd35cafb7aa4a48fe60af1cdd4d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Jun 2011 13:43:10 +0200 Subject: [PATCH 48/49] * grub-core/commands/wildcard.c (match_files): Add a useful dprintf. (wildcard_expand): Don't stop on nonregexp parts after regexp ones since it truncates the output. Reported by: Ximin Luo. --- ChangeLog | 7 +++++++ grub-core/commands/wildcard.c | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index d21fa1026..eaee6266a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2011-06-24 Vladimir Serbinenko + + * grub-core/commands/wildcard.c (match_files): Add a useful dprintf. + (wildcard_expand): Don't stop on nonregexp parts after regexp ones since + it truncates the output. + Reported by: Ximin Luo. + 2011-06-24 Vladimir Serbinenko * grub-core/fs/iso9660.c (grub_iso9660_iterate_dir): Skip . and .. diff --git a/grub-core/commands/wildcard.c b/grub-core/commands/wildcard.c index 32561abe6..45d819a3e 100644 --- a/grub-core/commands/wildcard.c +++ b/grub-core/commands/wildcard.c @@ -288,6 +288,8 @@ match_files (const char *prefix, const char *suffix, const char *end, if (regexec (regexp, name, 0, 0, 0)) return 0; + grub_dprintf ("expand", "matched\n"); + buffer = grub_xasprintf ("%s%s", dir, name); if (! buffer) return 1; @@ -423,8 +425,6 @@ wildcard_expand (const char *s, char ***strs) while (*start) { split_path (start, &noregexop, ®exop); - if (noregexop >= regexop) /* no more wildcards */ - break; if (make_regex (noregexop, regexop, ®exp)) goto fail; From 031f22a01f81f3b65bdc8bc8fde473a5f847752e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 24 Jun 2011 18:26:20 +0200 Subject: [PATCH 49/49] * grub-core/disk/raid.c (insert_array): Ensure uniqueness of readable names. --- ChangeLog | 5 +++++ grub-core/disk/raid.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/ChangeLog b/ChangeLog index eaee6266a..d70c7c6c0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2011-06-24 Vladimir Serbinenko + + * grub-core/disk/raid.c (insert_array): Ensure uniqueness of readable + names. + 2011-06-24 Vladimir Serbinenko * grub-core/commands/wildcard.c (match_files): Add a useful dprintf. diff --git a/grub-core/disk/raid.c b/grub-core/disk/raid.c index c6be3efde..7c7dbc3dc 100644 --- a/grub-core/disk/raid.c +++ b/grub-core/disk/raid.c @@ -679,6 +679,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;