From 7d8c0213538e9954d3b466d91c6c7c3c0f10b300 Mon Sep 17 00:00:00 2001 From: BVK Chaitanya Date: Tue, 25 May 2010 18:24:46 +0530 Subject: [PATCH 01/12] * INSTALL: Added flex >= 2.5.35 requirement. --- ChangeLog | 4 ++++ INSTALL | 1 + 2 files changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 01d2dbf7c..b8e66c7a2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2010-05-25 BVK Chaitanya + + * INSTALL: Added flex >= 2.5.35 requirement. + 2010-05-23 Vladimir Serbinenko * commands/usbtest.c (grub_usb_get_string): Properly support UTF-16. diff --git a/INSTALL b/INSTALL index 0dd408bcc..7186d20db 100644 --- a/INSTALL +++ b/INSTALL @@ -16,6 +16,7 @@ configuring the GRUB. * GNU Bison 2.3 or later * GNU gettext 0.17 or later * GNU binutils 2.9.1.0.23 or later +* Flex 2.5.35 or later * Other standard GNU/Unix tools If you use a development snapshot or want to hack on GRUB you may From 95ac3c730c58a60ec8a64bf046d235eec95eb790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Sutre?= Date: Wed, 26 May 2010 23:49:52 +0200 Subject: [PATCH 02/12] Fix -Wshadow warning with emu build on NetBSD. --- ChangeLog | 5 +++++ kern/emu/hostdisk.c | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index b8e66c7a2..ff7d02b44 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-05-26 Grégoire Sutre + + * kern/emu/hostdisk.c (find_partition_start) [__NetBSD__]: Renamed + variable index into p_index to suppress a warning with -Wshadow. + 2010-05-25 BVK Chaitanya * INSTALL: Added flex >= 2.5.35 requirement. diff --git a/kern/emu/hostdisk.c b/kern/emu/hostdisk.c index 9c4697552..ae2f7d9ad 100644 --- a/kern/emu/hostdisk.c +++ b/kern/emu/hostdisk.c @@ -338,7 +338,7 @@ find_partition_start (const char *dev) struct hd_geometry hdg; # else /* defined(__NetBSD__) */ struct disklabel label; - int index; + int p_index; # endif /* !defined(__NetBSD__) */ # ifdef HAVE_DEVICE_MAPPER @@ -435,15 +435,15 @@ devmapper_fail: # if !defined(__NetBSD__) return hdg.start; # else /* defined(__NetBSD__) */ - index = dev[strlen(dev) - 1] - 'a'; + p_index = dev[strlen(dev) - 1] - 'a'; - if (index >= label.d_npartitions) + if (p_index >= label.d_npartitions) { grub_error (GRUB_ERR_BAD_DEVICE, "no disk label entry for `%s'", dev); return 0; } - return (grub_disk_addr_t) label.d_partitions[index].p_offset; + return (grub_disk_addr_t) label.d_partitions[p_index].p_offset; # endif /* !defined(__NetBSD__) */ } #endif /* __linux__ || __CYGWIN__ */ From d1d368e426d3a0554ec28f52dafbd644fd21816e Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Thu, 27 May 2010 12:43:22 +0100 Subject: [PATCH 03/12] * util/grub-mkrescue.in: Initialise override_dir rather than assuming that it's unset or empty in the environment. --- ChangeLog | 5 +++++ util/grub-mkrescue.in | 1 + 2 files changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index ff7d02b44..22005fbb9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-05-27 Colin Watson + + * util/grub-mkrescue.in: Initialise override_dir rather than + assuming that it's unset or empty in the environment. + 2010-05-26 Grégoire Sutre * kern/emu/hostdisk.c (find_partition_start) [__NetBSD__]: Renamed diff --git a/util/grub-mkrescue.in b/util/grub-mkrescue.in index 7e7253006..99b10de7b 100644 --- a/util/grub-mkrescue.in +++ b/util/grub-mkrescue.in @@ -37,6 +37,7 @@ pc_dir=${libdir}/$(echo ${PACKAGE_TARNAME} | sed ${transform})/i386-pc efi32_dir=${libdir}/$(echo ${PACKAGE_TARNAME} | sed ${transform})/i386-efi efi64_dir=${libdir}/$(echo ${PACKAGE_TARNAME} | sed ${transform})/x86_64-efi rom_directory= +override_dir= grub_mkimage=${bindir}/`echo grub-mkimage | sed ${transform}` # Usage: usage From 5fdba519bb35c64a2c90221f069863cc36e00387 Mon Sep 17 00:00:00 2001 From: Robert Millan Date: Thu, 27 May 2010 16:38:16 +0200 Subject: [PATCH 04/12] 2010-05-27 Robert Millan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Insert partmap module to handle cross-partmap setups. Reported by Orestes Mas. Gràcies! --- ChangeLog | 6 ++++++ util/grub-mkconfig_lib.in | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/ChangeLog b/ChangeLog index 22005fbb9..18c2abeac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2010-05-27 Robert Millan + + * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Insert + partmap module to handle cross-partmap setups. + Reported by Orestes Mas. Gràcies! + 2010-05-27 Colin Watson * util/grub-mkrescue.in: Initialise override_dir rather than diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index f4674b257..7ec8cb5be 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -105,6 +105,11 @@ prepare_grub_to_access_device () echo "insmod ${module}" done + partmap="`${grub_probe} --device ${device} --target=partmap`" + for module in ${partmap} ; do + echo "insmod ${module}" + done + fs="`${grub_probe} --device ${device} --target=fs`" for module in ${fs} ; do echo "insmod ${module}" From db2102a0468e15c76bb369eef728622a9e51683e Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Thu, 27 May 2010 15:45:41 +0100 Subject: [PATCH 05/12] * kern/emu/misc.c (canonicalize_file_name): realpath can still return NULL for various reasons even if it has a maximum-length buffer: for example, there might be a symlink loop, or the path might exceed PATH_MAX. If this happens, return NULL. --- ChangeLog | 7 +++++++ kern/emu/misc.c | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 18c2abeac..34bc31bf9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2010-05-27 Colin Watson + + * kern/emu/misc.c (canonicalize_file_name): realpath can still + return NULL for various reasons even if it has a maximum-length + buffer: for example, there might be a symlink loop, or the path + might exceed PATH_MAX. If this happens, return NULL. + 2010-05-27 Robert Millan * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Insert diff --git a/kern/emu/misc.c b/kern/emu/misc.c index a3ccb3076..9437169e0 100644 --- a/kern/emu/misc.c +++ b/kern/emu/misc.c @@ -185,7 +185,8 @@ canonicalize_file_name (const char *path) char *ret; #ifdef PATH_MAX ret = xmalloc (PATH_MAX); - (void) realpath (path, ret); + if (!realpath (path, ret)) + return NULL; #else ret = realpath (path, NULL); #endif From d870813427f6e58ddc6877a578ae5b882f6f0898 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Thu, 27 May 2010 15:54:18 +0100 Subject: [PATCH 06/12] * util/grub-script-check.c (main): Ensure defined behaviour on empty input files (in which case exit zero). --- ChangeLog | 5 +++++ util/grub-script-check.c | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 34bc31bf9..a8e3e5284 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-05-27 Colin Watson + + * util/grub-script-check.c (main): Ensure defined behaviour on empty + input files (in which case exit zero). + 2010-05-27 Colin Watson * kern/emu/misc.c (canonicalize_file_name): realpath can still diff --git a/util/grub-script-check.c b/util/grub-script-check.c index dc732aa01..e69f787f5 100644 --- a/util/grub-script-check.c +++ b/util/grub-script-check.c @@ -145,7 +145,8 @@ main (int argc, char *argv[]) char *input; FILE *file = 0; int verbose = 0; - struct grub_script *script; + int found_input = 0; + struct grub_script *script = NULL; auto grub_err_t get_config_line (char **line, int cont); grub_err_t get_config_line (char **line, int cont __attribute__ ((unused))) @@ -247,6 +248,7 @@ main (int argc, char *argv[]) get_config_line(&input, 0); if (! input) break; + found_input = 1; script = grub_script_parse (input, get_config_line); if (script) @@ -262,5 +264,5 @@ main (int argc, char *argv[]) grub_fini_all (); if (file) fclose (file); - return (script == 0); + return (found_input && script == 0); } From 15fb2ae896e6aad4ba947a5b4828f938575c9877 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 28 May 2010 11:51:50 +0100 Subject: [PATCH 07/12] Make grub-probe work with symbolic links under /dev/mapper as well as with real block devices. The Linux world seems to be (at best) in transition here, and GRUB shouldn't get caught in the middle. * kern/emu/getroot.c (find_root_device): Follow symbolic links under /dev/mapper. --- ChangeLog.mapper-symlink | 8 ++++++++ kern/emu/getroot.c | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 ChangeLog.mapper-symlink diff --git a/ChangeLog.mapper-symlink b/ChangeLog.mapper-symlink new file mode 100644 index 000000000..afcfc6479 --- /dev/null +++ b/ChangeLog.mapper-symlink @@ -0,0 +1,8 @@ +2010-05-28 Colin Watson + + Make grub-probe work with symbolic links under /dev/mapper as well + as with real block devices. The Linux world seems to be (at best) + in transition here, and GRUB shouldn't get caught in the middle. + + * kern/emu/getroot.c (find_root_device): Follow symbolic links under + /dev/mapper. diff --git a/kern/emu/getroot.c b/kern/emu/getroot.c index 6875044da..e62abe089 100644 --- a/kern/emu/getroot.c +++ b/kern/emu/getroot.c @@ -126,9 +126,20 @@ find_root_device (const char *dir, dev_t dev) /* Ignore any error. */ continue; - if (S_ISLNK (st.st_mode)) - /* Don't follow symbolic links. */ + if (S_ISLNK (st.st_mode)) { +#ifdef __linux__ + if (strcmp (dir, "mapper") == 0) { + /* Follow symbolic links under /dev/mapper/; the canonical name + may be something like /dev/dm-0, but the names under + /dev/mapper/ are more human-readable and so we prefer them if + we can get them. */ + if (stat (ent->d_name, &st) < 0) + continue; + } else +#endif /* __linux__ */ + /* Don't follow other symbolic links. */ continue; + } if (S_ISDIR (st.st_mode)) { From fa4b84900afa71ad9fcf498d509e8f0e8abbb6dc Mon Sep 17 00:00:00 2001 From: BVK Chaitanya Date: Fri, 28 May 2010 19:18:45 +0530 Subject: [PATCH 08/12] Fix grub-emu issues on NetBSD, with gcc 4.1.3. * conf/any-emu.rmk: Remove unnecessary COMMON_CFLAGS. * include/grub/emu/misc.h (canonicalize_file_name): New Prototype. * kern/misc.c (__enable_execute_stack): Disable on GRUB_MACHINE_EMU. --- ChangeLog | 9 +++++++++ conf/any-emu.rmk | 2 -- include/grub/emu/misc.h | 1 + kern/misc.c | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 965848669..e3fb344aa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2010-05-28 BVK Chaitanya + + Fix grub-emu issues on NetBSD, with gcc 4.1.3. + + * conf/any-emu.rmk: Remove unnecessary COMMON_CFLAGS. + * include/grub/emu/misc.h (canonicalize_file_name): New Prototype. + * kern/misc.c (__enable_execute_stack): Disable on + GRUB_MACHINE_EMU. + 2010-05-28 Colin Watson Make grub-probe work with symbolic links under /dev/mapper as well diff --git a/conf/any-emu.rmk b/conf/any-emu.rmk index 3f0df06aa..000996172 100644 --- a/conf/any-emu.rmk +++ b/conf/any-emu.rmk @@ -1,7 +1,5 @@ # -*- makefile -*- -COMMON_CFLAGS += -nostdinc -isystem $(shell $(TARGET_CC) -print-file-name=include) - kernel_img_RELOCATABLE = yes pkglib_PROGRAMS = kernel.img kernel_img_SOURCES = kern/device.c kern/disk.c kern/dl.c kern/env.c \ diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h index e037e6be7..0e6ace0e8 100644 --- a/include/grub/emu/misc.h +++ b/include/grub/emu/misc.h @@ -46,5 +46,6 @@ int EXPORT_FUNC(asprintf) (char **buf, const char *fmt, ...); #endif char * EXPORT_FUNC(xasprintf) (const char *fmt, ...); +extern char * canonicalize_file_name (const char *path); #endif /* GRUB_EMU_MISC_H */ diff --git a/kern/misc.c b/kern/misc.c index ccc01d43f..2ba34f235 100644 --- a/kern/misc.c +++ b/kern/misc.c @@ -1058,7 +1058,7 @@ grub_abort (void) void abort (void) __attribute__ ((alias ("grub_abort"))); #endif -#if defined(NEED_ENABLE_EXECUTE_STACK) && !defined(GRUB_UTIL) +#if defined(NEED_ENABLE_EXECUTE_STACK) && !defined(GRUB_UTIL) && !defined(GRUB_MACHINE_EMU) /* Some gcc versions generate a call to this function in trampolines for nested functions. */ void __enable_execute_stack (void *addr __attribute__ ((unused))) @@ -1075,3 +1075,4 @@ void __deregister_frame_info (void) { } #endif + From c7c75cf4cb568889432527f94efc0d971e2cc3b0 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 28 May 2010 18:13:48 +0100 Subject: [PATCH 09/12] * configure.ac: AC_PROG_LEX sets LEX to ":" if lex is missing, not to the empty string. --- ChangeLog | 5 +++++ configure.ac | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index e3fb344aa..5072a6026 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-05-28 Colin Watson + + * configure.ac: AC_PROG_LEX sets LEX to ":" if lex is missing, not + to the empty string. + 2010-05-28 BVK Chaitanya Fix grub-emu issues on NetBSD, with gcc 4.1.3. diff --git a/configure.ac b/configure.ac index ace6bd81d..31b179d46 100644 --- a/configure.ac +++ b/configure.ac @@ -192,7 +192,7 @@ AC_PROG_LEX AC_PROG_MAKE_SET AC_PROG_MKDIR_P -if test "x$LEX" = x; then +if test "x$LEX" = "x:"; then AC_MSG_ERROR([flex is not found]) else version=`$LEX --version | $AWK '{ split($NF,x,"."); print x[[1]]*10000+x[[2]]*100+x[[3]]; }'` From 8b1cf5e87f603a30dbc0e0d13c44c3628d67b2bf Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 31 May 2010 13:58:18 +0200 Subject: [PATCH 10/12] Dedicated DMA allocation functions. CS5536 OHCI support. --- bus/cs5536.c | 215 +++++++++++++++++++++++++++++++ bus/pci.c | 53 ++++++++ bus/usb/ohci.c | 175 +++++++++++++++++-------- bus/usb/uhci.c | 6 +- bus/usb/usbtrans.c | 91 ++++++++++--- conf/i386.rmk | 6 + conf/mips-yeeloong.rmk | 34 ++++- include/grub/cs5536.h | 190 +++++++++++++++++++++++++++ include/grub/i386/pci.h | 2 +- include/grub/mips/yeeloong/pci.h | 32 +++-- include/grub/pci.h | 26 ++++ include/grub/smbus.h | 70 ++++++++++ include/grub/usbtrans.h | 2 +- 13 files changed, 817 insertions(+), 85 deletions(-) create mode 100644 bus/cs5536.c create mode 100644 include/grub/cs5536.h create mode 100644 include/grub/smbus.h diff --git a/bus/cs5536.c b/bus/cs5536.c new file mode 100644 index 000000000..61b0646b4 --- /dev/null +++ b/bus/cs5536.c @@ -0,0 +1,215 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 + +int +grub_cs5536_find (grub_pci_device_t *devp) +{ + int found = 0; + auto int NESTED_FUNC_ATTR hook (grub_pci_device_t dev, + grub_pci_id_t pciid); + + int NESTED_FUNC_ATTR hook (grub_pci_device_t dev, + grub_pci_id_t pciid) + { + if (pciid == GRUB_CS5536_PCIID) + { + *devp = dev; + found = 1; + return 1; + } + return 0; + } + + grub_pci_iterate (hook); + + return found; +} + +grub_uint64_t +grub_cs5536_read_msr (grub_pci_device_t dev, grub_uint32_t addr) +{ + grub_uint64_t ret = 0; + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_ADDR), + addr); + ret = (grub_uint64_t) + grub_pci_read (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA0)); + ret |= (((grub_uint64_t) + grub_pci_read (grub_pci_make_address (dev, + GRUB_CS5536_MSR_MAILBOX_DATA1))) + << 32); + return ret; +} + +void +grub_cs5536_write_msr (grub_pci_device_t dev, grub_uint32_t addr, + grub_uint64_t val) +{ + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_ADDR), + addr); + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA0), + val & 0xffffffff); + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA1), + val >> 32); +} + +grub_err_t +grub_cs5536_smbus_wait (grub_port_t smbbase) +{ + grub_uint64_t start = grub_get_time_ms (); + while (1) + { + grub_uint8_t status; + status = grub_inb (smbbase + GRUB_CS5536_SMB_REG_STATUS); + if (status & GRUB_CS5536_SMB_REG_STATUS_SDAST) + return GRUB_ERR_NONE; + if (status & GRUB_CS5536_SMB_REG_STATUS_BER) + return grub_error (GRUB_ERR_IO, "SM bus error"); + if (status & GRUB_CS5536_SMB_REG_STATUS_NACK) + return grub_error (GRUB_ERR_IO, "NACK received"); + if (grub_get_time_ms () > start + 40) + return grub_error (GRUB_ERR_IO, "SM stalled"); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_cs5536_read_spd_byte (grub_port_t smbbase, grub_uint8_t dev, + grub_uint8_t addr, grub_uint8_t *res) +{ + grub_err_t err; + + /* Send START. */ + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_START, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Send device address. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (dev << 1, smbbase + GRUB_CS5536_SMB_REG_DATA); + + /* Send ACK. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_ACK, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Send byte address. */ + grub_outb (addr, smbbase + GRUB_CS5536_SMB_REG_DATA); + + /* Send START. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_START, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Send device address. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb ((dev << 1) | 1, smbbase + GRUB_CS5536_SMB_REG_DATA); + + /* Send STOP. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_STOP, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + *res = grub_inb (smbbase + GRUB_CS5536_SMB_REG_DATA); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_cs5536_init_smbus (grub_pci_device_t dev, grub_uint16_t divisor, + grub_port_t *smbbase) +{ + grub_uint64_t smbbar; + + smbbar = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_SMB_BAR); + + /* FIXME */ + if (!(smbbar & GRUB_CS5536_LBAR_ENABLE)) + return grub_error(GRUB_ERR_IO, "SMB controller not enabled\n"); + *smbbase = (smbbar & GRUB_CS5536_LBAR_ADDR_MASK) + GRUB_MACHINE_PCI_IO_BASE; + + if (divisor < 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid divisor"); + + /* Disable SMB. */ + grub_outb (0, *smbbase + GRUB_CS5536_SMB_REG_CTRL2); + + /* Disable interrupts. */ + grub_outb (0, *smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Set as master. */ + grub_outb (GRUB_CS5536_SMB_REG_ADDR_MASTER, + *smbbase + GRUB_CS5536_SMB_REG_ADDR); + + /* Launch. */ + grub_outb (((divisor >> 7) & 0xff), *smbbase + GRUB_CS5536_SMB_REG_CTRL3); + grub_outb (((divisor << 1) & 0xfe) | GRUB_CS5536_SMB_REG_CTRL2_ENABLE, + *smbbase + GRUB_CS5536_SMB_REG_CTRL2); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_cs5536_read_spd (grub_port_t smbbase, grub_uint8_t dev, + struct grub_smbus_spd *res) +{ + grub_err_t err; + grub_size_t size; + grub_uint8_t b; + grub_size_t ptr; + + err = grub_cs5536_read_spd_byte (smbbase, dev, 0, &b); + if (err) + return err; + if (b == 0) + return grub_error (GRUB_ERR_IO, "no SPD found"); + size = b; + + ((grub_uint8_t *) res)[0] = b; + for (ptr = 1; ptr < size; ptr++) + { + err = grub_cs5536_read_spd_byte (smbbase, dev, ptr, + &((grub_uint8_t *) res)[ptr]); + if (err) + return err; + } + return GRUB_ERR_NONE; +} + diff --git a/bus/pci.c b/bus/pci.c index a08e53446..3e2126962 100644 --- a/bus/pci.c +++ b/bus/pci.c @@ -19,6 +19,49 @@ #include #include +#include + +#if GRUB_TARGET_SIZEOF_VOID_P == 4 +struct grub_pci_dma_chunk * +grub_memalign_dma32 (grub_size_t align, grub_size_t size) +{ + return grub_memalign (align, size); +} + +void +grub_dma_free (struct grub_pci_dma_chunk *ch) +{ + grub_free (ch); +} +#endif + +#ifdef GRUB_MACHINE_MIPS_YEELOONG +volatile void * +grub_dma_get_virt (struct grub_pci_dma_chunk *ch) +{ + return (void *) ((((grub_uint32_t) ch) & 0x1fffffff) | 0xa0000000); +} + +grub_uint32_t +grub_dma_get_phys (struct grub_pci_dma_chunk *ch) +{ + return (((grub_uint32_t) ch) & 0x1fffffff) | 0x80000000; +} +#else + +volatile void * +grub_dma_get_virt (struct grub_pci_dma_chunk *ch) +{ + return (void *) ch; +} + +grub_uint32_t +grub_dma_get_phys (struct grub_pci_dma_chunk *ch) +{ + return (grub_uint32_t) ch; +} + +#endif grub_pci_address_t grub_pci_make_address (grub_pci_device_t dev, int reg) @@ -48,6 +91,16 @@ grub_pci_iterate (grub_pci_iteratefunc_t hook) if (id >> 16 == 0xFFFF) continue; +#ifdef GRUB_MACHINE_MIPS_YEELOONG + /* Skip ghosts. */ + if (id == GRUB_YEELOONG_OHCI_PCIID + && dev.function == GRUB_YEELOONG_OHCI_GHOST_FUNCTION) + continue; + if (id == GRUB_YEELOONG_EHCI_PCIID + && dev.function == GRUB_YEELOONG_EHCI_GHOST_FUNCTION) + continue; +#endif + if (hook (dev, id)) return; diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 6d185bc7f..7294fe537 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -24,8 +24,9 @@ #include #include #include -#include +#include #include +#include struct grub_ohci_hcca { @@ -63,13 +64,15 @@ struct grub_ohci_td grub_uint32_t buffer_end; } __attribute__((packed)); -typedef struct grub_ohci_td *grub_ohci_td_t; -typedef struct grub_ohci_ed *grub_ohci_ed_t; +typedef volatile struct grub_ohci_td *grub_ohci_td_t; +typedef volatile struct grub_ohci_ed *grub_ohci_ed_t; struct grub_ohci { volatile grub_uint32_t *iobase; volatile struct grub_ohci_hcca *hcca; + grub_uint32_t hcca_addr; + struct grub_pci_dma_chunk *hcca_chunk; struct grub_ohci *next; }; @@ -91,10 +94,23 @@ typedef enum GRUB_OHCI_REG_BULKCURR, GRUB_OHCI_REG_DONEHEAD, GRUB_OHCI_REG_FRAME_INTERVAL, + GRUB_OHCI_REG_PERIODIC_START = 16, GRUB_OHCI_REG_RHUBA = 18, GRUB_OHCI_REG_RHUBPORT = 21 } grub_ohci_reg_t; +#define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300 +#define GRUB_OHCI_RHUB_PORT_ALL_POWERED 0x200 + +#define GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_MASK 0x8fff0000 +#define GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_SHIFT 16 +#define GRUB_OHCI_REG_FRAME_INTERVAL_FI_SHIFT 0 + +/* XXX: Is this choice of timings sane? */ +#define GRUB_OHCI_FSMPS 0x2778 +#define GRUB_OHCI_PERIODIC_START 0x257f +#define GRUB_OHCI_FRAME_INTERVAL 0x2edf + static grub_uint32_t grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg) { @@ -114,51 +130,81 @@ grub_ohci_writereg32 (struct grub_ohci *o, controller. If this is the case, initialize it. */ static int NESTED_FUNC_ATTR grub_ohci_pci_iter (grub_pci_device_t dev, - grub_pci_id_t pciid __attribute__((unused))) + grub_pci_id_t pciid) { - grub_uint32_t class_code; - grub_uint32_t class; - grub_uint32_t subclass; grub_uint32_t interf; grub_uint32_t base; grub_pci_address_t addr; struct grub_ohci *o; grub_uint32_t revision; - grub_uint32_t frame_interval; - - addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); - class_code = grub_pci_read (addr) >> 8; - - interf = class_code & 0xFF; - subclass = (class_code >> 8) & 0xFF; - class = class_code >> 16; - - /* If this is not an OHCI controller, just return. */ - if (class != 0x0c || subclass != 0x03 || interf != 0x10) - return 0; + int cs5536; /* Determine IO base address. */ - addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); - base = grub_pci_read (addr); + grub_dprintf ("ohci", "pciid = %x\n", pciid); + + if (pciid == GRUB_CS5536_PCIID) + { + grub_uint64_t basereg; + + cs5536 = 1; + basereg = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE); + if (!(basereg & GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE)) + { + /* Shouldn't happen. */ + grub_dprintf ("ohci", "No OHCI address is assigned\n"); + return 0; + } + base = (basereg & GRUB_CS5536_MSR_USB_BASE_ADDR_MASK); + basereg |= GRUB_CS5536_MSR_USB_BASE_BUS_MASTER; + basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_ENABLED; + basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_STATUS; + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE, basereg); + } + else + { + grub_uint32_t class_code; + grub_uint32_t class; + grub_uint32_t subclass; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class_code = grub_pci_read (addr) >> 8; + + interf = class_code & 0xFF; + subclass = (class_code >> 8) & 0xFF; + class = class_code >> 16; + + /* If this is not an OHCI controller, just return. */ + if (class != 0x0c || subclass != 0x03 || interf != 0x10) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + base = grub_pci_read (addr); #if 0 - /* Stop if there is no IO space base address defined. */ - if (! (base & 1)) - return 0; + /* Stop if there is no IO space base address defined. */ + if (! (base & 1)) + return 0; #endif + grub_dprintf ("ohci", "class=0x%02x 0x%02x interface 0x%02x\n", + class, subclass, interf); + } + /* Allocate memory for the controller and register it. */ o = grub_malloc (sizeof (*o)); if (! o) return 1; - o->iobase = (grub_uint32_t *) base; + o->iobase = grub_pci_device_map_range (dev, base, 0x100); + + grub_dprintf ("ohci", "base=%p\n", o->iobase); /* Reserve memory for the HCCA. */ - o->hcca = (struct grub_ohci_hcca *) grub_memalign (256, 256); - - grub_dprintf ("ohci", "class=0x%02x 0x%02x interface 0x%02x base=%p\n", - class, subclass, interf, o->iobase); + o->hcca_chunk = grub_memalign_dma32 (256, 256); + if (! o->hcca_chunk) + return 1; + o->hcca = grub_dma_get_virt (o->hcca_chunk); + o->hcca_addr = grub_dma_get_phys (o->hcca_chunk); /* Check if the OHCI revision is actually 1.0 as supported. */ revision = grub_ohci_readreg32 (o, GRUB_OHCI_REG_REVISION); @@ -166,19 +212,27 @@ grub_ohci_pci_iter (grub_pci_device_t dev, if ((revision & 0xFF) != 0x10) goto fail; - /* Backup the frame interval register. */ - frame_interval = grub_ohci_readreg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA, + (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) + & ~GRUB_OHCI_RHUB_PORT_POWER_MASK) + | GRUB_OHCI_RHUB_PORT_ALL_POWERED); /* Suspend the OHCI by issuing a reset. */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic. */ grub_millisleep (1); grub_dprintf ("ohci", "OHCI reset\n"); - /* Restore the frame interval register. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL, frame_interval); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL, + (GRUB_OHCI_FSMPS + << GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_SHIFT) + | (GRUB_OHCI_FRAME_INTERVAL + << GRUB_OHCI_REG_FRAME_INTERVAL_FI_SHIFT)); + + grub_ohci_writereg32 (o, GRUB_OHCI_REG_PERIODIC_START, + GRUB_OHCI_PERIODIC_START); /* Setup the HCCA. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, (grub_uint32_t) o->hcca); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr); grub_dprintf ("ohci", "OHCI HCCA\n"); /* Enable the OHCI. */ @@ -194,11 +248,13 @@ grub_ohci_pci_iter (grub_pci_device_t dev, return 0; fail: +#ifndef GRUB_MACHINE_MIPS_YEELOONG if (o) grub_free ((void *) o->hcca); +#endif grub_free (o); - return 1; + return 0; } @@ -229,7 +285,7 @@ grub_ohci_iterate (int (*hook) (grub_usb_controller_t dev)) static void grub_ohci_transaction (grub_ohci_td_t td, grub_transfer_type_t type, unsigned int toggle, - grub_size_t size, char *data) + grub_size_t size, grub_uint32_t data) { grub_uint32_t token; grub_uint32_t buffer; @@ -261,7 +317,7 @@ grub_ohci_transaction (grub_ohci_td_t td, token |= toggle << 24; token |= 1 << 25; - buffer = (grub_uint32_t) data; + buffer = data; buffer_end = buffer + size - 1; td->token = grub_cpu_to_le32 (token); @@ -276,7 +332,10 @@ grub_ohci_transfer (grub_usb_controller_t dev, { struct grub_ohci *o = (struct grub_ohci *) dev->data; grub_ohci_ed_t ed; + grub_uint32_t ed_addr; + struct grub_pci_dma_chunk *ed_chunk, *td_list_chunk; grub_ohci_td_t td_list; + grub_uint32_t td_list_addr; grub_uint32_t target; grub_uint32_t td_tail; grub_uint32_t td_head; @@ -286,18 +345,23 @@ grub_ohci_transfer (grub_usb_controller_t dev, int i; /* Allocate an Endpoint Descriptor. */ - ed = grub_memalign (16, sizeof (*ed)); - if (! ed) + ed_chunk = grub_memalign_dma32 (256, sizeof (*ed)); + if (! ed_chunk) return GRUB_USB_ERR_INTERNAL; + ed = grub_dma_get_virt (ed_chunk); + ed_addr = grub_dma_get_phys (ed_chunk); - td_list = grub_memalign (16, sizeof (*td_list) * (transfer->transcnt + 1)); - if (! td_list) + td_list_chunk = grub_memalign_dma32 (256, sizeof (*td_list) + * (transfer->transcnt + 1)); + if (! td_list_chunk) { - grub_free ((void *) ed); + grub_dma_free (ed_chunk); return GRUB_USB_ERR_INTERNAL; } + td_list = grub_dma_get_virt (td_list_chunk); + td_list_addr = grub_dma_get_phys (td_list_chunk); - grub_dprintf ("ohci", "alloc=%p\n", td_list); + grub_dprintf ("ohci", "alloc=%p/0x%x\n", td_list, td_list_addr); /* Setup all Transfer Descriptors. */ for (i = 0; i < transfer->transcnt; i++) @@ -307,7 +371,8 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_ohci_transaction (&td_list[i], tr->pid, tr->toggle, tr->size, tr->data); - td_list[i].next_td = grub_cpu_to_le32 (&td_list[i + 1]); + td_list[i].next_td = grub_cpu_to_le32 (td_list_addr + + (i + 1) * sizeof (td_list[0])); } /* Setup the Endpoint Descriptor. */ @@ -324,9 +389,9 @@ grub_ohci_transfer (grub_usb_controller_t dev, /* Set the maximum packet size. */ target |= transfer->max << 16; - td_head = (grub_uint32_t) td_list; + td_head = td_list_addr; - td_tail = (grub_uint32_t) &td_list[transfer->transcnt]; + td_tail = td_list_addr + transfer->transcnt * sizeof (*td_list); ed->target = grub_cpu_to_le32 (target); ed->td_head = grub_cpu_to_le32 (td_head); @@ -353,7 +418,7 @@ grub_ohci_transfer (grub_usb_controller_t dev, status &= ~(1 << 2); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, (grub_uint32_t) ed); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr); /* Enable the Bulk list. */ control |= 1 << 5; @@ -380,10 +445,9 @@ grub_ohci_transfer (grub_usb_controller_t dev, status &= ~(1 << 1); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, - (grub_uint32_t) ed); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1, - (grub_uint32_t) ed); + ed_addr); /* Enable the Control list. */ control |= 1 << 4; @@ -424,9 +488,12 @@ grub_ohci_transfer (grub_usb_controller_t dev, { grub_uint8_t errcode; grub_ohci_td_t tderr; + grub_uint32_t td_err_addr; - tderr = (grub_ohci_td_t) grub_ohci_readreg32 (o, - GRUB_OHCI_REG_DONEHEAD); + td_err_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD); + + tderr = (grub_ohci_td_t) ((char *) td_list + + (td_err_addr - td_list_addr)); errcode = tderr->token >> 28; switch (errcode) @@ -519,8 +586,8 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); /* XXX */ - grub_free (td_list); - grub_free (ed); + grub_dma_free (td_list_chunk); + grub_dma_free (ed_chunk); return err; } diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index 947f2367b..6e47c38ef 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -380,7 +380,7 @@ static grub_uhci_td_t grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, grub_transfer_type_t type, unsigned int addr, unsigned int toggle, grub_size_t size, - char *data) + grub_uint32_t data) { grub_uhci_td_t td; static const unsigned int tf[] = { 0x69, 0xE1, 0x2D }; @@ -398,7 +398,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, } grub_dprintf ("uhci", - "transaction: endp=%d, type=%d, addr=%d, toggle=%d, size=%d data=%p td=%p\n", + "transaction: endp=%d, type=%d, addr=%d, toggle=%d, size=%d data=0x%x td=%p\n", endp, type, addr, toggle, size, data, td); /* Don't point to any TD, just terminate. */ @@ -418,7 +418,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, td->token = ((size << 21) | (toggle << 19) | (endp << 15) | (addr << 8) | tf[type]); - td->buffer = (grub_uint32_t) data; + td->buffer = data; return td; } diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c index 09e7af83e..f6d3d30f9 100644 --- a/bus/usb/usbtrans.c +++ b/bus/usb/usbtrans.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -29,30 +30,59 @@ grub_usb_control_msg (grub_usb_device_t dev, grub_uint8_t request, grub_uint16_t value, grub_uint16_t index, - grub_size_t size, char *data) + grub_size_t size0, char *data_in) { int i; grub_usb_transfer_t transfer; int datablocks; - struct grub_usb_packet_setup setupdata; + volatile struct grub_usb_packet_setup *setupdata; + grub_uint32_t setupdata_addr; grub_usb_err_t err; unsigned int max; + struct grub_pci_dma_chunk *data_chunk, *setupdata_chunk; + volatile char *data; + grub_uint32_t data_addr; + grub_size_t size = size0; + + /* FIXME: avoid allocation any kind of buffer in a first place. */ + data_chunk = grub_memalign_dma32 (128, size ? : 16); + if (!data_chunk) + return GRUB_USB_ERR_INTERNAL; + data = grub_dma_get_virt (data_chunk); + data_addr = grub_dma_get_phys (data_chunk); + grub_memcpy ((char *) data, data_in, size); grub_dprintf ("usb", "control: reqtype=0x%02x req=0x%02x val=0x%02x idx=0x%02x size=%d\n", reqtype, request, value, index, size); /* Create a transfer. */ - transfer = grub_malloc (sizeof (struct grub_usb_transfer)); + transfer = grub_malloc (sizeof (*transfer)); if (! transfer) - return grub_errno; + { + grub_dma_free (data_chunk); + return grub_errno; + } + + setupdata_chunk = grub_memalign_dma32 (32, sizeof (*setupdata)); + if (! setupdata_chunk) + { + grub_free (transfer); + grub_dma_free (data_chunk); + return grub_errno; + } + + setupdata = grub_dma_get_virt (setupdata_chunk); + setupdata_addr = grub_dma_get_phys (setupdata_chunk); /* Determine the maximum packet size. */ - if (dev->initialized) + if (dev->initialized && dev->descdev.maxsize0) max = dev->descdev.maxsize0; else max = 64; + grub_dprintf ("usb", "transfer = %p, dev = %p\n", transfer, dev); + datablocks = (size + max - 1) / max; /* XXX: Discriminate between different types of control @@ -71,18 +101,20 @@ grub_usb_control_msg (grub_usb_device_t dev, if (! transfer->transactions) { grub_free (transfer); + grub_dma_free (setupdata_chunk); + grub_dma_free (data_chunk); return grub_errno; } /* Build a Setup packet. XXX: Endianness. */ - setupdata.reqtype = reqtype; - setupdata.request = request; - setupdata.value = value; - setupdata.index = index; - setupdata.length = size; - transfer->transactions[0].size = sizeof (setupdata); + setupdata->reqtype = reqtype; + setupdata->request = request; + setupdata->value = value; + setupdata->index = index; + setupdata->length = size; + transfer->transactions[0].size = sizeof (*setupdata); transfer->transactions[0].pid = GRUB_USB_TRANSFER_TYPE_SETUP; - transfer->transactions[0].data = (char *) &setupdata; + transfer->transactions[0].data = setupdata_addr; transfer->transactions[0].toggle = 0; /* Now the data... XXX: Is this the right way to transfer control @@ -99,13 +131,13 @@ grub_usb_control_msg (grub_usb_device_t dev, tr->pid = GRUB_USB_TRANSFER_TYPE_IN; else tr->pid = GRUB_USB_TRANSFER_TYPE_OUT; - tr->data = &data[i * max]; + tr->data = data_addr + i * max; size -= max; } /* End with an empty OUT transaction. */ transfer->transactions[datablocks + 1].size = 0; - transfer->transactions[datablocks + 1].data = NULL; + transfer->transactions[datablocks + 1].data = 0; if (reqtype & 128) transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT; else @@ -117,13 +149,17 @@ grub_usb_control_msg (grub_usb_device_t dev, grub_free (transfer->transactions); grub_free (transfer); + grub_dma_free (data_chunk); + grub_dma_free (setupdata_chunk); + + grub_memcpy (data_in, (char *) data, size0); return err; } static grub_usb_err_t grub_usb_bulk_readwrite (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data, + int endpoint, grub_size_t size0, char *data_in, grub_transfer_type_t type) { int i; @@ -132,6 +168,19 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, unsigned int max; grub_usb_err_t err; int toggle = dev->toggle[endpoint]; + volatile char *data; + grub_uint32_t data_addr; + struct grub_pci_dma_chunk *data_chunk; + grub_size_t size = size0; + + /* FIXME: avoid allocation any kind of buffer in a first place. */ + data_chunk = grub_memalign_dma32 (128, size); + if (!data_chunk) + return GRUB_USB_ERR_INTERNAL; + data = grub_dma_get_virt (data_chunk); + data_addr = grub_dma_get_phys (data_chunk); + if (type == GRUB_USB_TRANSFER_TYPE_OUT) + grub_memcpy ((char *) data, data_in, size); /* Use the maximum packet size given in the endpoint descriptor. */ if (dev->initialized) @@ -150,7 +199,10 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, /* Create a transfer. */ transfer = grub_malloc (sizeof (struct grub_usb_transfer)); if (! transfer) - return grub_errno; + { + grub_dma_free (data_chunk); + return grub_errno; + } datablocks = ((size + max - 1) / max); transfer->transcnt = datablocks; @@ -167,6 +219,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, if (! transfer->transactions) { grub_free (transfer); + grub_dma_free (data_chunk); return grub_errno; } @@ -181,7 +234,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, tr->toggle = toggle; toggle = toggle ? 0 : 1; tr->pid = type; - tr->data = &data[i * max]; + tr->data = data_addr + i * max; size -= tr->size; } @@ -191,6 +244,10 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, grub_free (transfer->transactions); grub_free (transfer); + grub_dma_free (data_chunk); + + if (type == GRUB_USB_TRANSFER_TYPE_IN) + grub_memcpy (data_in, (char *) data, size0); return err; } diff --git a/conf/i386.rmk b/conf/i386.rmk index 02ce39817..73e818f9e 100644 --- a/conf/i386.rmk +++ b/conf/i386.rmk @@ -56,6 +56,12 @@ pci_mod_SOURCES = bus/pci.c pci_mod_CFLAGS = $(COMMON_CFLAGS) pci_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For cs5536.mod +pkglib_MODULES += cs5536.mod +cs5536_mod_SOURCES = bus/cs5536.c +cs5536_mod_CFLAGS = $(COMMON_CFLAGS) +cs5536_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For lspci.mod pkglib_MODULES += lspci.mod lspci_mod_SOURCES = commands/lspci.c diff --git a/conf/mips-yeeloong.rmk b/conf/mips-yeeloong.rmk index 5ce8ede9d..2468985c5 100644 --- a/conf/mips-yeeloong.rmk +++ b/conf/mips-yeeloong.rmk @@ -4,7 +4,8 @@ target_machine=yeeloong COMMON_CFLAGS += -march=mips3 COMMON_ASFLAGS += -march=mips3 -kernel_img_HEADERS += pci.h bitmap.h video.h gfxterm.h font.h bitmap_scale.h bufio.h +kernel_img_HEADERS += pci.h bitmap.h video.h gfxterm.h font.h \ + bitmap_scale.h bufio.h cs5536.h machine/pci.h include $(srcdir)/conf/mips.mk @@ -26,6 +27,7 @@ kernel_img_SOURCES = kern/$(target_cpu)/startup.S \ video/fb/fbfill.c video/fb/fbutil.c video/bitmap.c \ video/bitmap_scale.c video/sm712.c bus/pci.c bus/bonito.c \ term/gfxterm.c commands/extcmd.c lib/arg.c \ + bus/cs5536.c \ symlist.c kernel_img_CFLAGS = $(COMMON_CFLAGS) -DUSE_ASCII_FAILBACK kernel_img_ASFLAGS = $(COMMON_ASFLAGS) @@ -69,5 +71,35 @@ linux_mod_CFLAGS = $(COMMON_CFLAGS) linux_mod_ASFLAGS = $(COMMON_ASFLAGS) linux_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For usb.mod +pkglib_MODULES += usb.mod +usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c +usb_mod_CFLAGS = $(COMMON_CFLAGS) +usb_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For usbtest.mod +pkglib_MODULES += usbtest.mod +usbtest_mod_SOURCES = commands/usbtest.c +usbtest_mod_CFLAGS = $(COMMON_CFLAGS) +usbtest_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For ohci.mod +pkglib_MODULES += ohci.mod +ohci_mod_SOURCES = bus/usb/ohci.c +ohci_mod_CFLAGS = $(COMMON_CFLAGS) +ohci_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For usbms.mod +pkglib_MODULES += usbms.mod +usbms_mod_SOURCES = disk/usbms.c +usbms_mod_CFLAGS = $(COMMON_CFLAGS) +usbms_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For usb_keyboard.mod +pkglib_MODULES += usb_keyboard.mod +usb_keyboard_mod_SOURCES = term/usb_keyboard.c +usb_keyboard_mod_CFLAGS = $(COMMON_CFLAGS) +usb_keyboard_mod_LDFLAGS = $(COMMON_LDFLAGS) + sbin_SCRIPTS += grub-install grub_install_SOURCES = util/grub-install.in diff --git a/include/grub/cs5536.h b/include/grub/cs5536.h new file mode 100644 index 000000000..cd17e11fc --- /dev/null +++ b/include/grub/cs5536.h @@ -0,0 +1,190 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#ifndef GRUB_CS5536_HEADER +#define GRUB_CS5536_HEADER 1 + +#ifndef ASM_FILE +#include +#include +#include +#endif + +#define GRUB_CS5536_PCIID 0x208f1022 +#define GRUB_CS5536_MSR_MAILBOX_ADDR 0xf4 +#define GRUB_CS5536_MSR_MAILBOX_DATA0 0xf8 +#define GRUB_CS5536_MSR_MAILBOX_DATA1 0xfc +#define GRUB_CS5536_MSR_IRQ_MAP_BAR 0x80000008 +#define GRUB_CS5536_MSR_SMB_BAR 0x8000000b + +#define GRUB_CS5536_SMBUS_REGS_SIZE 8 +#define GRUB_CS5536_GPIO_REGS_SIZE 256 +#define GRUB_CS5536_MFGPT_REGS_SIZE 64 +#define GRUB_CS5536_IRQ_MAP_REGS_SIZE 32 +#define GRUB_CS5536_PM_REGS_SIZE 128 +#define GRUB_CS5536_ACPI_REGS_SIZE 32 + +#define GRUB_CS5536_USB_OPTION_REGS_SIZE 0x1c +#define GRUB_CS5536_USB_OPTION_REG_UOCMUX 1 +#define GRUB_CS5536_USB_OPTION_REG_UOCMUX_PMUX_MASK 0x03 +#define GRUB_CS5536_USB_OPTION_REG_UOCMUX_PMUX_HC 0x02 + +#define GRUB_CS5536_DESTINATION_GLIU 0 +#define GRUB_CS5536_DESTINATION_GLPCI_SB 1 +#define GRUB_CS5536_DESTINATION_USB 2 +#define GRUB_CS5536_DESTINATION_IDE 3 +#define GRUB_CS5536_DESTINATION_DD 4 +#define GRUB_CS5536_DESTINATION_ACC 5 +#define GRUB_CS5536_DESTINATION_GLCP 7 + +#define GRUB_CS5536_P2D_DEST_SHIFT 61 +#define GRUB_CS5536_P2D_LOG_ALIGN 12 +#define GRUB_CS5536_P2D_ALIGN (1 << GRUB_CS5536_P2D_LOG_ALIGN) +#define GRUB_CS5536_P2D_BASE_SHIFT 20 +#define GRUB_CS5536_P2D_MASK_SHIFT 0 + +#define GRUB_CS5536_MSR_GL_IOD_START 0x000100e0 +#define GRUB_CS5536_IOD_DEST_SHIFT 61 +#define GRUB_CS5536_IOD_BASE_SHIFT 20 +#define GRUB_CS5536_IOD_MASK_SHIFT 0 +#define GRUB_CS5536_IOD_ADDR_MASK 0xfffff + +#define GRUB_CS5536_MSR_GPIO_BAR 0x8000000c +#define GRUB_CS5536_MSR_MFGPT_BAR 0x8000000d +#define GRUB_CS5536_MSR_ACPI_BAR 0x8000000e +#define GRUB_CS5536_MSR_PM_BAR 0x8000000f +#define GRUB_CS5536_MSR_DIVIL_LEG_IO 0x80000014 +#define GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE0 0x00000001 +#define GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE1 0x00000002 +#define GRUB_CS5536_MSR_DIVIL_LEG_IO_MODE_X86 0x10000000 +#define GRUB_CS5536_MSR_DIVIL_LEG_IO_F_REMAP 0x04000000 +#define GRUB_CS5536_MSR_DIVIL_IRQ_MAPPER_PRIMARY_MASK 0x80000024 +#define GRUB_CS5536_MSR_DIVIL_IRQ_MAPPER_LPC_MASK 0x80000025 +#define GRUB_CS5536_DIVIL_LPC_INTERRUPTS 0x1002 +#define GRUB_CS5536_MSR_DIVIL_LPC_SERIAL_IRQ_CONTROL 0x8000004e +#define GRUB_CS5536_MSR_DIVIL_LPC_SERIAL_IRQ_CONTROL_ENABLE 0x80 + +#define GRUB_CS5536_MSR_USB_OHCI_BASE 0x40000008 +#define GRUB_CS5536_MSR_USB_EHCI_BASE 0x40000009 +#define GRUB_CS5536_MSR_USB_CONTROLLER_BASE 0x4000000a +#define GRUB_CS5536_MSR_USB_OPTION_CONTROLLER_BASE 0x4000000b +#define GRUB_CS5536_MSR_USB_BASE_ADDR_MASK 0x00ffffff00ULL +#define GRUB_CS5536_MSR_USB_BASE_BUS_MASTER 0x0400000000ULL +#define GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE 0x0200000000ULL +#define GRUB_CS5536_MSR_USB_BASE_PME_ENABLED 0x0800000000ULL +#define GRUB_CS5536_MSR_USB_BASE_PME_STATUS 0x1000000000ULL +#define GRUB_CS5536_MSR_USB_EHCI_BASE_FLDJ_SHIFT 40 + +#define GRUB_CS5536_MSR_IDE_IO_BAR 0x60000008 +#define GRUB_CS5536_MSR_IDE_IO_BAR_UNITS 1 +#define GRUB_CS5536_MSR_IDE_IO_BAR_ADDR_MASK 0xfffffff0 +#define GRUB_CS5536_MSR_IDE_CFG 0x60000010 +#define GRUB_CS5536_MSR_IDE_CFG_CHANNEL_ENABLE 2 +#define GRUB_CS5536_MSR_IDE_TIMING 0x60000012 +#define GRUB_CS5536_MSR_IDE_TIMING_PIO0 0x98 +#define GRUB_CS5536_MSR_IDE_TIMING_DRIVE0_SHIFT 24 +#define GRUB_CS5536_MSR_IDE_TIMING_DRIVE1_SHIFT 16 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING 0x60000013 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_CMD_PIO0 0x99 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_CMD_SHIFT 24 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_DRIVE0_SHIFT 6 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_DRIVE1_SHIFT 4 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_PIO0 2 + +#define GRUB_CS5536_MSR_GL_PCI_CTRL 0x00000010 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_MEMORY_ENABLE 1 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_IO_ENABLE 2 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_LATENCY_SHIFT 35 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_OUT_THR_SHIFT 60 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_IN_THR_SHIFT 56 + +#define GRUB_CS5536_MSR_GL_REGIONS_START 0x00000020 +#define GRUB_CS5536_MSR_GL_REGIONS_NUM 16 +#define GRUB_CS5536_MSR_GL_REGION_ENABLE 1 +#define GRUB_CS5536_MSR_GL_REGION_IO 0x100000000ULL +#define GRUB_CS5536_MSR_GL_REGION_BASE_MASK 0xfffff000ULL +#define GRUB_CS5536_MSR_GL_REGION_IO_BASE_SHIFT 12 +#define GRUB_CS5536_MSR_GL_REGION_TOP_MASK 0xfffff00000000000ULL +#define GRUB_CS5536_MSR_GL_REGION_IO_TOP_SHIFT 44 + +#define GRUB_CS5536_MSR_GL_P2D_START 0x00010020 + +#define GRUB_CS5536_SMB_REG_DATA 0x0 +#define GRUB_CS5536_SMB_REG_STATUS 0x1 +#define GRUB_CS5536_SMB_REG_STATUS_SDAST (1 << 6) +#define GRUB_CS5536_SMB_REG_STATUS_BER (1 << 5) +#define GRUB_CS5536_SMB_REG_STATUS_NACK (1 << 4) +#define GRUB_CS5536_SMB_REG_CTRL1 0x3 +#define GRUB_CS5536_SMB_REG_CTRL1_START 0x01 +#define GRUB_CS5536_SMB_REG_CTRL1_STOP 0x02 +#define GRUB_CS5536_SMB_REG_CTRL1_ACK 0x10 +#define GRUB_CS5536_SMB_REG_ADDR 0x4 +#define GRUB_CS5536_SMB_REG_ADDR_MASTER 0x0 +#define GRUB_CS5536_SMB_REG_CTRL2 0x5 +#define GRUB_CS5536_SMB_REG_CTRL2_ENABLE 0x1 +#define GRUB_CS5536_SMB_REG_CTRL3 0x6 + +#ifdef ASM_FILE +#define GRUB_ULL(x) x +#else +#define GRUB_ULL(x) x ## ULL +#endif + +#define GRUB_CS5536_LBAR_ADDR_MASK GRUB_ULL (0x000000000000fff8) +#define GRUB_CS5536_LBAR_ENABLE GRUB_ULL (0x0000000100000000) +#define GRUB_CS5536_LBAR_MASK_MASK GRUB_ULL (0x0000f00000000000) +#define GRUB_CS5536_LBAR_TURN_ON (GRUB_CS5536_LBAR_ENABLE | GRUB_CS5536_LBAR_MASK_MASK) + +/* PMON-compatible LBARs. */ +#define GRUB_CS5536_LBAR_GPIO 0xb000 +#define GRUB_CS5536_LBAR_ACC 0xb200 +#define GRUB_CS5536_LBAR_PM 0xb280 +#define GRUB_CS5536_LBAR_MFGPT 0xb300 +#define GRUB_CS5536_LBAR_ACPI 0xb340 +#define GRUB_CS5536_LBAR_IRQ_MAP 0xb360 +#define GRUB_CS5536_LBAR_IDE 0xb380 +#define GRUB_CS5536_LBAR_SMBUS 0xb390 + +#define GRUB_GPIO_SMBUS_PINS ((1 << 14) | (1 << 15)) +#define GRUB_GPIO_REG_OUT_EN 0x4 +#define GRUB_GPIO_REG_OUT_AUX1 0x10 +#define GRUB_GPIO_REG_IN_EN 0x20 +#define GRUB_GPIO_REG_IN_AUX1 0x34 + +#ifndef ASM_FILE +int EXPORT_FUNC (grub_cs5536_find) (grub_pci_device_t *devp); + +grub_uint64_t EXPORT_FUNC (grub_cs5536_read_msr) (grub_pci_device_t dev, + grub_uint32_t addr); +void EXPORT_FUNC (grub_cs5536_write_msr) (grub_pci_device_t dev, + grub_uint32_t addr, + grub_uint64_t val); +grub_err_t grub_cs5536_read_spd_byte (grub_port_t smbbase, grub_uint8_t dev, + grub_uint8_t addr, grub_uint8_t *res); +grub_err_t EXPORT_FUNC (grub_cs5536_read_spd) (grub_port_t smbbase, + grub_uint8_t dev, + struct grub_smbus_spd *res); +grub_err_t grub_cs5536_smbus_wait (grub_port_t smbbase); +grub_err_t EXPORT_FUNC (grub_cs5536_init_smbus) (grub_pci_device_t dev, + grub_uint16_t divisor, + grub_port_t *smbbase); + +void grub_cs5536_init_geode (grub_pci_device_t dev); +#endif + +#endif diff --git a/include/grub/i386/pci.h b/include/grub/i386/pci.h index bab42adb6..795dec58b 100644 --- a/include/grub/i386/pci.h +++ b/include/grub/i386/pci.h @@ -80,7 +80,7 @@ grub_pci_device_map_range (grub_pci_device_t dev __attribute__ ((unused)), static inline void grub_pci_device_unmap_range (grub_pci_device_t dev __attribute__ ((unused)), - void *mem __attribute__ ((unused)), + volatile void *mem __attribute__ ((unused)), grub_size_t size __attribute__ ((unused))) { } diff --git a/include/grub/mips/yeeloong/pci.h b/include/grub/mips/yeeloong/pci.h index c7bd31d4f..199bac048 100644 --- a/include/grub/mips/yeeloong/pci.h +++ b/include/grub/mips/yeeloong/pci.h @@ -19,16 +19,30 @@ #ifndef GRUB_MACHINE_PCI_H #define GRUB_MACHINE_PCI_H 1 +#ifndef ASM_FILE #include #include +#endif + +#define GRUB_YEELOONG_OHCI_PCIID 0x00351033 +#define GRUB_YEELOONG_EHCI_PCIID 0x00e01033 +#define GRUB_YEELOONG_OHCI_GHOST_FUNCTION 4 +#define GRUB_YEELOONG_EHCI_GHOST_FUNCTION 5 #define GRUB_PCI_NUM_BUS 1 #define GRUB_PCI_NUM_DEVICES 16 -#define GRUB_MACHINE_PCI_IO_BASE 0xbfd00000 -#define GRUB_MACHINE_PCI_CONFSPACE 0xbfe80000 -#define GRUB_MACHINE_PCI_CONF_CTRL_REG (*(volatile grub_uint32_t *) 0xbfe00118) +#define GRUB_MACHINE_PCI_IO_BASE 0xbfd00000 +#define GRUB_MACHINE_PCI_CONFSPACE 0xbfe80000 +#define GRUB_MACHINE_PCI_CONTROLLER_HEADER 0xbfe00000 + +#define GRUB_MACHINE_PCI_CONF_CTRL_REG_ADDR 0xbfe00118 + +#ifndef ASM_FILE +#define GRUB_MACHINE_PCI_CONF_CTRL_REG (*(volatile grub_uint32_t *) \ + GRUB_MACHINE_PCI_CONF_CTRL_REG_ADDR) #define GRUB_MACHINE_PCI_IO_CTRL_REG (*(volatile grub_uint32_t *) 0xbfe00110) +#endif #define GRUB_MACHINE_PCI_WIN_MASK_SIZE 6 #define GRUB_MACHINE_PCI_WIN_MASK ((1 << GRUB_MACHINE_PCI_WIN_MASK_SIZE) - 1) @@ -46,6 +60,7 @@ #define GRUB_MACHINE_PCI_WIN2_ADDR 0xb4000000 #define GRUB_MACHINE_PCI_WIN3_ADDR 0xb8000000 +#ifndef ASM_FILE static inline grub_uint32_t grub_pci_read (grub_pci_address_t addr) { @@ -95,11 +110,12 @@ grub_pci_write_byte (grub_pci_address_t addr, grub_uint8_t data) } volatile void * -grub_pci_device_map_range (grub_pci_device_t dev __attribute__ ((unused)), - grub_addr_t base, grub_size_t size); +EXPORT_FUNC (grub_pci_device_map_range) (grub_pci_device_t dev, + grub_addr_t base, grub_size_t size); void -grub_pci_device_unmap_range (grub_pci_device_t dev __attribute__ ((unused)), - volatile void *mem, - grub_size_t size __attribute__ ((unused))); +EXPORT_FUNC (grub_pci_device_unmap_range) (grub_pci_device_t dev, + volatile void *mem, + grub_size_t size); +#endif #endif /* GRUB_MACHINE_PCI_H */ diff --git a/include/grub/pci.h b/include/grub/pci.h index 89bd1034a..e6d6488f0 100644 --- a/include/grub/pci.h +++ b/include/grub/pci.h @@ -19,8 +19,10 @@ #ifndef GRUB_PCI_H #define GRUB_PCI_H 1 +#ifndef ASM_FILE #include #include +#endif #define GRUB_PCI_ADDR_SPACE_MASK 0x01 #define GRUB_PCI_ADDR_SPACE_MEMORY 0x00 @@ -66,6 +68,20 @@ #define GRUB_PCI_REG_MIN_GNT 0x3e #define GRUB_PCI_REG_MAX_LAT 0x3f +#define GRUB_PCI_COMMAND_IO_ENABLED 0x0001 +#define GRUB_PCI_COMMAND_MEM_ENABLED 0x0002 +#define GRUB_PCI_COMMAND_BUS_MASTER 0x0004 +#define GRUB_PCI_COMMAND_PARITY_ERROR 0x0040 +#define GRUB_PCI_COMMAND_SERR_ENABLE 0x0100 + +#define GRUB_PCI_STATUS_CAPABILITIES 0x0010 +#define GRUB_PCI_STATUS_66MHZ_CAPABLE 0x0020 +#define GRUB_PCI_STATUS_FAST_B2B_CAPABLE 0x0080 + +#define GRUB_PCI_STATUS_DEVSEL_TIMING_SHIFT 9 +#define GRUB_PCI_STATUS_DEVSEL_TIMING_MASK 0x0600 + +#ifndef ASM_FILE typedef grub_uint32_t grub_pci_id_t; #ifdef GRUB_MACHINE_EMU @@ -107,4 +123,14 @@ grub_pci_address_t EXPORT_FUNC(grub_pci_make_address) (grub_pci_device_t dev, void EXPORT_FUNC(grub_pci_iterate) (grub_pci_iteratefunc_t hook); +struct grub_pci_dma_chunk; + +struct grub_pci_dma_chunk *EXPORT_FUNC(grub_memalign_dma32) (grub_size_t align, + grub_size_t size); +void EXPORT_FUNC(grub_dma_free) (struct grub_pci_dma_chunk *ch); +volatile void *EXPORT_FUNC(grub_dma_get_virt) (struct grub_pci_dma_chunk *ch); +grub_uint32_t EXPORT_FUNC(grub_dma_get_phys) (struct grub_pci_dma_chunk *ch); + +#endif + #endif /* GRUB_PCI_H */ diff --git a/include/grub/smbus.h b/include/grub/smbus.h new file mode 100644 index 000000000..0b8e6718f --- /dev/null +++ b/include/grub/smbus.h @@ -0,0 +1,70 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#ifndef GRUB_SMBUS_HEADER +#define GRUB_SMBUS_HEADER 1 + +#define GRUB_SMB_RAM_START_ADDR 0x50 +#define GRUB_SMB_RAM_NUM_MAX 0x08 + +#define GRUB_SMBUS_SPD_MEMORY_TYPE_ADDR 2 +#define GRUB_SMBUS_SPD_MEMORY_TYPE_DDR2 8 +#define GRUB_SMBUS_SPD_MEMORY_NUM_BANKS_ADDR 17 +#define GRUB_SMBUS_SPD_MEMORY_NUM_ROWS_ADDR 3 +#define GRUB_SMBUS_SPD_MEMORY_NUM_COLUMNS_ADDR 4 +#define GRUB_SMBUS_SPD_MEMORY_NUM_OF_RANKS_ADDR 5 +#define GRUB_SMBUS_SPD_MEMORY_NUM_OF_RANKS_MASK 0x7 +#define GRUB_SMBUS_SPD_MEMORY_CAS_LATENCY_ADDR 18 +#define GRUB_SMBUS_SPD_MEMORY_CAS_LATENCY_MIN_VALUE 5 +#define GRUB_SMBUS_SPD_MEMORY_TRAS_ADDR 30 +#define GRUB_SMBUS_SPD_MEMORY_TRTP_ADDR 38 + +#ifndef ASM_FILE + +struct grub_smbus_spd +{ + grub_uint8_t written_size; + grub_uint8_t log_total_flash_size; + grub_uint8_t memory_type; + union + { + grub_uint8_t unknown[253]; + struct { + grub_uint8_t num_rows; + grub_uint8_t num_columns; + grub_uint8_t num_of_ranks; + grub_uint8_t unused1[12]; + grub_uint8_t num_of_banks; + grub_uint8_t unused2[2]; + grub_uint8_t cas_latency; + grub_uint8_t unused3[9]; + grub_uint8_t rank_capacity; + grub_uint8_t unused4[1]; + grub_uint8_t tras; + grub_uint8_t unused5[7]; + grub_uint8_t trtp; + grub_uint8_t unused6[31]; + grub_uint8_t part_number[18]; + grub_uint8_t unused7[165]; + } ddr2; + }; +}; + +#endif + +#endif diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h index 7e4a9d7ee..8c739684e 100644 --- a/include/grub/usbtrans.h +++ b/include/grub/usbtrans.h @@ -37,7 +37,7 @@ struct grub_usb_transaction int size; int toggle; grub_transfer_type_t pid; - char *data; + grub_uint32_t data; }; typedef struct grub_usb_transaction *grub_usb_transaction_t; From 7fd08f56ae0d5f76e5f5c7a184414ae8e0e36a91 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 31 May 2010 14:01:38 +0200 Subject: [PATCH 11/12] Shut down OHCI on booting --- bus/usb/ohci.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 7294fe537..29ab5332a 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -654,6 +654,39 @@ grub_ohci_hubports (grub_usb_controller_t dev) return portinfo & 0xFF; } +static grub_err_t +grub_ohci_fini_hw (int noreturn __attribute__ ((unused))) +{ + struct grub_ohci *o; + + for (o = ohci; o; o = o->next) + { + int i, nports = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) & 0xff; + for (i = 0; i < nports; i++) + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + i, + GRUB_OHCI_CLEAR_PORT_ENABLE); + + grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_DONEHEAD, 0); + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_ohci_restore_hw (void) +{ + struct grub_ohci *o; + + for (o = ohci; o; o = o->next) + grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr); + + return GRUB_ERR_NONE; +} static struct grub_usb_controller_dev usb_controller = @@ -670,9 +703,12 @@ GRUB_MOD_INIT(ohci) { 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); } GRUB_MOD_FINI(ohci) { + grub_ohci_fini_hw (0); grub_usb_controller_dev_unregister (&usb_controller); } From 778ff32476c08a69d99e56fbfe9e7494e0c26bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Nesrsta?= Date: Mon, 31 May 2010 14:11:41 +0200 Subject: [PATCH 12/12] Fix a bunch of problems with USB. --- bus/usb/ohci.c | 342 +++++++++++++++++++++++++++++++--------- bus/usb/uhci.c | 17 +- bus/usb/usb.c | 12 +- bus/usb/usbtrans.c | 5 +- commands/usbtest.c | 2 + disk/scsi.c | 183 +++++++++++++++++++-- disk/usbms.c | 166 ++++++++++++------- include/grub/scsicmd.h | 62 ++++++-- include/grub/usb.h | 9 +- include/grub/usbtrans.h | 6 +- 10 files changed, 637 insertions(+), 167 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 29ab5332a..03768d39e 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -27,6 +27,7 @@ #include #include #include +#include struct grub_ohci_hcca { @@ -96,7 +97,11 @@ typedef enum GRUB_OHCI_REG_FRAME_INTERVAL, GRUB_OHCI_REG_PERIODIC_START = 16, GRUB_OHCI_REG_RHUBA = 18, - GRUB_OHCI_REG_RHUBPORT = 21 + GRUB_OHCI_REG_RHUBPORT = 21, + GRUB_OHCI_REG_LEGACY_CONTROL = 0x100, + GRUB_OHCI_REG_LEGACY_INPUT = 0x104, + GRUB_OHCI_REG_LEGACY_OUTPUT = 0x108, + GRUB_OHCI_REG_LEGACY_STATUS = 0x10c } grub_ohci_reg_t; #define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300 @@ -195,7 +200,7 @@ grub_ohci_pci_iter (grub_pci_device_t dev, if (! o) return 1; - o->iobase = grub_pci_device_map_range (dev, base, 0x100); + o->iobase = grub_pci_device_map_range (dev, base, 0x800); grub_dprintf ("ohci", "base=%p\n", o->iobase); @@ -212,10 +217,48 @@ grub_ohci_pci_iter (grub_pci_device_t dev, if ((revision & 0xFF) != 0x10) goto fail; - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA, - (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) - & ~GRUB_OHCI_RHUB_PORT_POWER_MASK) - | GRUB_OHCI_RHUB_PORT_ALL_POWERED); + + { + grub_uint32_t control; + /* Check SMM/BIOS ownership of OHCI (SMM = USB Legacy Support driver for BIOS) */ + control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); + if ((control & 0x100) != 0) + { + unsigned i; + grub_dprintf("ohci", "OHCI is owned by SMM\n"); + /* Do change of ownership */ + /* Ownership change request */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, (1<<3)); /* XXX: Magic. */ + /* Waiting for SMM deactivation */ + for (i=0; i < 10; i++) + { + if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & 0x100) == 0) + { + grub_dprintf("ohci", "Ownership changed normally.\n"); + break; + } + grub_millisleep (100); + } + if (i >= 10) + { + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, + grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~0x100); + grub_dprintf("ohci", "Ownership changing timeout, change forced !\n"); + } + } + else if (((control & 0x100) == 0) && + ((control & 0xc0) != 0)) /* Not owned by SMM nor reset */ + { + grub_dprintf("ohci", "OHCI is owned by BIOS\n"); + /* Do change of ownership - not implemented yet... */ + /* In fact we probably need to do nothing ...? */ + } + else + { + grub_dprintf("ohci", "OHCI is not owned by SMM nor BIOS\n"); + /* We can setup OHCI. */ + } + } /* Suspend the OHCI by issuing a reset. */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic. */ @@ -232,15 +275,58 @@ grub_ohci_pci_iter (grub_pci_device_t dev, GRUB_OHCI_PERIODIC_START); /* Setup the HCCA. */ + o->hcca->donehead = 0; grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr); grub_dprintf ("ohci", "OHCI HCCA\n"); + /* Misc. pre-sets. */ + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); + + /* Check OHCI Legacy Support */ + if ((revision & 0x100) != 0) + { + grub_dprintf ("ohci", "Legacy Support registers detected\n"); + grub_dprintf ("ohci", "Current state of legacy control reg.: 0x%04x\n", + grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL, + (grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)) & ~1); + grub_dprintf ("ohci", "OHCI Legacy Support disabled.\n"); + } + /* Enable the OHCI. */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, (2 << 6)); grub_dprintf ("ohci", "OHCI enable: 0x%02x\n", (grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3); + /* Power on all ports */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA, + (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) + & ~GRUB_OHCI_RHUB_PORT_POWER_MASK) + | GRUB_OHCI_RHUB_PORT_ALL_POWERED); + /* Wait for stable power (100ms) and stable attachment (100ms) */ + /* I.e. minimum wait time should be probably 200ms. */ + /* We assume that device is attached when ohci is loaded. */ + /* Some devices take long time to power-on or indicate attach. */ + /* Here is some experimental value which should probably mostly work. */ + /* Cameras with manual USB mode selection and maybe some other similar + * devices will not work in some cases - they are repowered during + * ownership change and then they are starting slowly and mostly they + * are wanting select proper mode again... + * The same situation can be on computers where BIOS not set-up OHCI + * to be at least powered USB bus (maybe it is Yeelong case...?) + * Possible workaround could be for example some prompt + * for user with confirmation of proper USB device connection. + * Another workaround - "rmmod usbms", "rmmod ohci", proper start + * and configuration of USB device and then "insmod ohci" + * and "insmod usbms". */ + grub_millisleep (500); + /* Link to ohci now that initialisation is successful. */ o->next = ohci; ohci = o; @@ -317,13 +403,29 @@ grub_ohci_transaction (grub_ohci_td_t td, token |= toggle << 24; token |= 1 << 25; + /* Set "Not accessed" error code */ + token |= 15 << 28; + buffer = data; buffer_end = buffer + size - 1; + /* Set correct buffer values in TD if zero transfer occurs */ + if (size) + { + buffer = (grub_uint32_t) data; + buffer_end = buffer + size - 1; + td->buffer = grub_cpu_to_le32 (buffer); + td->buffer_end = grub_cpu_to_le32 (buffer_end); + } + else + { + td->buffer = 0; + td->buffer_end = 0; + } + + /* Set the rest of TD */ td->token = grub_cpu_to_le32 (token); - td->buffer = grub_cpu_to_le32 (buffer); td->next_td = 0; - td->buffer_end = grub_cpu_to_le32 (buffer_end); } static grub_usb_err_t @@ -342,7 +444,9 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_uint32_t status; grub_uint32_t control; grub_usb_err_t err; - int i; + int i, j; + grub_uint64_t maxtime; + int err_timeout = 0; /* Allocate an Endpoint Descriptor. */ ed_chunk = grub_memalign_dma32 (256, sizeof (*ed)); @@ -375,13 +479,25 @@ grub_ohci_transfer (grub_usb_controller_t dev, + (i + 1) * sizeof (td_list[0])); } + /* The last-1 TD token we should change to enable interrupt when TD finishes. + * As OHCI interrupts are disabled, it does only setting of WDH bit in + * HcInterruptStatus register - and that is what we want to safely detect + * normal end of all transactions. */ + td_list[transfer->transcnt - 1].token &= ~(7 << 21); + + td_list[transfer->transcnt].token = 0; + td_list[transfer->transcnt].buffer = 0; + td_list[transfer->transcnt].buffer_end = 0; + td_list[transfer->transcnt].next_td = + (grub_uint32_t) &td_list[transfer->transcnt]; + /* Setup the Endpoint Descriptor. */ /* Set the device address. */ target = transfer->devaddr; - /* Set the endpoint. */ - target |= transfer->endpoint << 7; + /* Set the endpoint. It should be masked, we need 4 bits only. */ + target |= (transfer->endpoint & 15) << 7; /* Set the device speed. */ target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13; @@ -400,6 +516,30 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_dprintf ("ohci", "program OHCI\n"); + /* Disable the Control and Bulk lists. */ + control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); + control &= ~(3 << 4); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); + + /* Clear BulkListFilled and ControlListFilled. */ + status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); + status &= ~(3 << 1); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); + + /* Now we should wait for start of next frame. Because we are not using + * interrupt, we reset SF bit and wait when it goes to 1. */ + /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2)); + /* Wait for new SOF */ + while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0); + /* Now it should be safe to change CONTROL and BULK lists. */ + + /* This we do for safety's sake - it should be done in previous call + * of grub_ohci_transfer and nobody should change it in meantime... + * It should be done before start of control or bulk OHCI list. */ + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */ + /* Program the OHCI to actually transfer. */ switch (transfer->type) { @@ -407,24 +547,17 @@ grub_ohci_transfer (grub_usb_controller_t dev, { grub_dprintf ("ohci", "add to bulk list\n"); - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); - control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); - - /* Disable the Control and Bulk lists. */ - control &= ~(3 << 4); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); - - /* Clear BulkListFilled. */ - status &= ~(1 << 2); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - + /* Set BulkList Head and Current */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); /* Enable the Bulk list. */ + control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); control |= 1 << 5; grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); /* Set BulkListFilled. */ + status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); status |= 1 << 2; grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); @@ -433,21 +566,9 @@ grub_ohci_transfer (grub_usb_controller_t dev, case GRUB_USB_TRANSACTION_TYPE_CONTROL: { - grub_dprintf ("ohci", "add to control list\n"); - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); - control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); - - /* Disable the Control and Bulk lists. */ - control &= ~(3 << 4); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); - - /* Clear ControlListFilled. */ - status &= ~(1 << 1); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - + /* Set ControlList Head and Current */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1, - ed_addr); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); /* Enable the Control list. */ control |= 1 << 4; @@ -465,36 +586,77 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL), grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS)); + /* Safety measure to avoid a hang. */ + maxtime = grub_get_time_ms () + 1000; + /* Wait until the transfer is completed or STALLs. */ - while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf)) + do { grub_cpu_idle (); - grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail); + /* Detected a HALT. */ + if (grub_le_to_cpu32 (ed->td_head) & 1) + break; + + if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0) + { + if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf) + == td_list_addr + (transfer->transcnt - 1) * sizeof (td_list[0])) + break; - /* Detected a STALL. */ - if (ed->td_head & 1) + /* Done Head can be updated on some another place if ED is halted. */ + if (grub_le_to_cpu32 (ed->td_head) & 1) + break; + + /* If there is not HALT in ED, it is not correct, so debug it, reset + * donehead and WDH and continue waiting. */ + grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n", + o->hcca->donehead); + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); + continue; + } + /* Timeout ? */ + if (grub_get_time_ms () > maxtime) + { + /* Disable the Control and Bulk lists. */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, + grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~(3 << 4)); + err_timeout = 1; + break; + } + + if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf)) break; } + while (1); - grub_dprintf ("ohci", "complete\n"); - -/* if (ed->td_head & 1) */ -/* err = GRUB_USB_ERR_STALL; */ -/* else if (ed->td */ - - - if (ed->td_head & 1) + if (err_timeout) + { + err = GRUB_ERR_TIMEOUT; + grub_dprintf("ohci", "Timeout, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n", + grub_le_to_cpu32(ed->target), + grub_le_to_cpu32(ed->td_head), + grub_le_to_cpu32(ed->td_tail), + grub_le_to_cpu32(ed->next_ed)); + } + else if (grub_le_to_cpu32 (ed->td_head) & 1) { - grub_uint8_t errcode; - grub_ohci_td_t tderr; grub_uint32_t td_err_addr; + grub_uint8_t errcode; + grub_ohci_td_t tderr = NULL; - td_err_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD); + td_err_addr = (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf); + if (td_err_addr == 0) + /* If DONEHEAD==0 it means that correct address is in HCCA. + * It should be always now! */ + td_err_addr = (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf); tderr = (grub_ohci_td_t) ((char *) td_list + (td_err_addr - td_list_addr)); - errcode = tderr->token >> 28; + + errcode = grub_le_to_cpu32 (tderr->token) >> 28; + grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode); switch (errcode) { @@ -540,11 +702,17 @@ grub_ohci_transfer (grub_usb_controller_t dev, case 8: /* XXX: Data overrun error. */ err = GRUB_USB_ERR_DATA; + j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list); + grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", tderr, j); break; case 9: /* XXX: Data underrun error. */ err = GRUB_USB_ERR_DATA; + grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n", + 1 + grub_le_to_cpu32 (tderr->buffer_end) - grub_le_to_cpu32 (tderr->buffer)); + j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list); + grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr, j); break; case 10: @@ -582,43 +750,73 @@ grub_ohci_transfer (grub_usb_controller_t dev, /* Clear BulkListFilled and ControlListFilled. */ status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); - status &= ~((1 << 2) | (1 << 3)); + status &= ~(3 << 1); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - - /* XXX */ + + /* Set ED to be skipped - for safety */ + ed->target |= grub_cpu_to_le32 (1 << 14); + + /* Now we should wait for start of next frame. + * It is necessary because we will invalidate pointer to ED and it + * can be on OHCI active till SOF! + * Because we are not using interrupt, we reset SF bit and wait when + * it goes to 1. */ + /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2)); + /* Wait for new SOF */ + while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0); + /* Now it should be safe to change CONTROL and BULK lists. */ + + /* Important cleaning. */ + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); + + grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n", err); + grub_dma_free (td_list_chunk); grub_dma_free (ed_chunk); return err; } +#define GRUB_OHCI_SET_PORT_ENABLE (1 << 1) +#define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0) +#define GRUB_OHCI_SET_PORT_RESET (1 << 4) +#define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20) + static grub_err_t grub_ohci_portstatus (grub_usb_controller_t dev, unsigned int port, unsigned int enable) { struct grub_ohci *o = (struct grub_ohci *) dev->data; - grub_uint32_t status; - /* Reset the port. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - status |= (1 << 4); /* XXX: Magic. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status); - grub_millisleep (100); + grub_dprintf ("ohci", "begin of portstatus=0x%02x\n", + grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port)); - /* End the reset signaling. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - status |= (1 << 20); /* XXX: Magic. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_SET_PORT_RESET); + grub_millisleep (50); /* For root hub should be nominaly 50ms */ + + /* End the reset signaling. */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE); grub_millisleep (10); - /* Enable the port. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - status |= (enable << 1); /* XXX: Magic. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status); - - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - grub_dprintf ("ohci", "portstatus=0x%02x\n", status); + if (enable) + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_SET_PORT_ENABLE); + else + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_CLEAR_PORT_ENABLE); + grub_millisleep (10); + grub_dprintf ("ohci", "end of portstatus=0x%02x\n", + grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port)); + return GRUB_ERR_NONE; } diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index 6e47c38ef..eb533c926 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -174,14 +174,15 @@ grub_uhci_pci_iter (grub_pci_device_t dev, return 1; u->iobase = base & GRUB_UHCI_IOMASK; - grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x\n", - class, subclass, interf, u->iobase); /* Reserve a page for the frame list. */ u->framelist = grub_memalign (4096, 4096); if (! u->framelist) goto fail; + grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x framelist=%p\n", + class, subclass, interf, u->iobase, u->framelist); + /* The framelist pointer of UHCI is only 32 bits, make sure this code works on on 64 bits architectures. */ #if GRUB_CPU_SIZEOF_VOID_P == 8 @@ -221,6 +222,9 @@ grub_uhci_pci_iter (grub_pci_device_t dev, } #endif + grub_dprintf ("uhci", "QH=%p, TD=%p\n", + u->qh, u->td); + /* Link all Transfer Descriptors in a list of available Transfer Descriptors. */ for (i = 0; i < 256; i++) @@ -441,6 +445,8 @@ grub_uhci_transfer (grub_usb_controller_t dev, if (! qh) return grub_errno; + grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase); + for (i = 0; i < transfer->transcnt; i++) { grub_usb_transaction_t tr = &transfer->transactions[i]; @@ -548,7 +554,8 @@ grub_uhci_transfer (grub_usb_controller_t dev, fail: - grub_dprintf ("uhci", "transaction failed\n"); + if (err != GRUB_USB_ERR_NONE) + grub_dprintf ("uhci", "transaction failed\n"); /* Place the QH back in the free list and deallocate the associated TDs. */ @@ -583,6 +590,8 @@ grub_uhci_portstatus (grub_usb_controller_t dev, unsigned int status; grub_uint64_t endtime; + grub_dprintf ("uhci", "portstatus, iobase:%08x\n", u->iobase); + grub_dprintf ("uhci", "enable=%d port=%d\n", enable, port); if (port == 0) @@ -631,6 +640,8 @@ grub_uhci_detect_dev (grub_usb_controller_t dev, int port) int reg; unsigned int status; + grub_dprintf ("uhci", "detect_dev, iobase:%08x\n", u->iobase); + if (port == 0) reg = GRUB_UHCI_REG_PORTSC1; else if (port == 1) diff --git a/bus/usb/usb.c b/bus/usb/usb.c index 8289185da..7df148333 100644 --- a/bus/usb/usb.c +++ b/bus/usb/usb.c @@ -107,7 +107,7 @@ grub_usb_set_configuration (grub_usb_device_t dev, int configuration) { int i; - for (i = 0; i < 16; i++) + for (i = 0; i < 256; i++) dev->toggle[i] = 0; return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT @@ -163,6 +163,16 @@ grub_usb_device_initialize (grub_usb_device_t dev) grub_usb_err_t err; int i; + /* First we have to read first 8 bytes only and determine + * max. size of packet */ + dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be removed if it is sure it is zero here */ + err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, + 0, 8, (char *) &dev->descdev); + if (err) + return err; + + /* Now we have valid value in dev->descdev.maxsize0, + * so we can read whole device descriptor */ err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, 0, sizeof (struct grub_usb_desc_device), (char *) &dev->descdev); diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c index f6d3d30f9..0bf5ee6de 100644 --- a/bus/usb/usbtrans.c +++ b/bus/usb/usbtrans.c @@ -138,7 +138,7 @@ grub_usb_control_msg (grub_usb_device_t dev, /* End with an empty OUT transaction. */ transfer->transactions[datablocks + 1].size = 0; transfer->transactions[datablocks + 1].data = 0; - if (reqtype & 128) + if ((reqtype & 128) && datablocks) transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT; else transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_IN; @@ -148,6 +148,7 @@ grub_usb_control_msg (grub_usb_device_t dev, err = dev->controller.dev->transfer (&dev->controller, transfer); grub_free (transfer->transactions); + grub_free (transfer); grub_dma_free (data_chunk); grub_dma_free (setupdata_chunk); @@ -207,7 +208,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, datablocks = ((size + max - 1) / max); transfer->transcnt = datablocks; transfer->size = size - 1; - transfer->endpoint = endpoint; + transfer->endpoint = endpoint & 15; transfer->devaddr = dev->addr; transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK; transfer->max = max; diff --git a/commands/usbtest.c b/commands/usbtest.c index 191c4e4df..7d1f85cfd 100644 --- a/commands/usbtest.c +++ b/commands/usbtest.c @@ -148,6 +148,8 @@ usb_iterate (grub_usb_device_t dev) grub_printf ("%s speed device\n", usb_devspeed[dev->speed]); + return 0; + for (i = 0; i < descdev->configcnt; i++) { struct grub_usb_desc_config *config; diff --git a/disk/scsi.c b/disk/scsi.c index eba237287..e8bfb6a3f 100644 --- a/disk/scsi.c +++ b/disk/scsi.c @@ -25,6 +25,7 @@ #include #include #include +#include static grub_scsi_dev_t grub_scsi_dev_list; @@ -50,7 +51,62 @@ grub_scsi_dev_unregister (grub_scsi_dev_t dev) } -/* Determine the the device is removable and the type of the device +/* Check result of previous operation. */ +static grub_err_t +grub_scsi_request_sense (grub_scsi_t scsi) +{ + struct grub_scsi_request_sense rs; + struct grub_scsi_request_sense_data rsd; + grub_err_t err; + + rs.opcode = grub_scsi_cmd_request_sense; + rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + rs.reserved1 = 0; + rs.reserved2 = 0; + rs.alloc_length = 0x12; /* XXX: Hardcoded for now */ + rs.control = 0; + grub_memset (rs.pad, 0, sizeof(rs.pad)); + + err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs, + sizeof (rsd), (char *) &rsd); + if (err) + return err; + + return GRUB_ERR_NONE; +} +/* Self commenting... */ +static grub_err_t +grub_scsi_test_unit_ready (grub_scsi_t scsi) +{ + struct grub_scsi_test_unit_ready tur; + grub_err_t err; + grub_err_t err_sense; + + tur.opcode = grub_scsi_cmd_test_unit_ready; + tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + tur.reserved1 = 0; + tur.reserved2 = 0; + tur.reserved3 = 0; + tur.control = 0; + grub_memset (tur.pad, 0, sizeof(tur.pad)); + + err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur, + 0, NULL); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + if (err) + return err; + + return GRUB_ERR_NONE; +} + +/* Determine if the device is removable and the type of the device SCSI. */ static grub_err_t grub_scsi_inquiry (grub_scsi_t scsi) @@ -58,15 +114,26 @@ grub_scsi_inquiry (grub_scsi_t scsi) struct grub_scsi_inquiry iq; struct grub_scsi_inquiry_data iqd; grub_err_t err; + grub_err_t err_sense; iq.opcode = grub_scsi_cmd_inquiry; iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + iq.page = 0; iq.reserved = 0; iq.alloc_length = 0x24; /* XXX: Hardcoded for now */ - iq.reserved2 = 0; + iq.control = 0; + grub_memset (iq.pad, 0, sizeof(iq.pad)); err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq, sizeof (iqd), (char *) &iqd); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + if (err) return err; @@ -83,13 +150,27 @@ grub_scsi_read_capacity (grub_scsi_t scsi) struct grub_scsi_read_capacity rc; struct grub_scsi_read_capacity_data rcd; grub_err_t err; + grub_err_t err_sense; rc.opcode = grub_scsi_cmd_read_capacity; rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; - grub_memset (rc.reserved, 0, sizeof (rc.reserved)); - + rc.logical_block_addr = 0; + rc.reserved1 = 0; + rc.reserved2 = 0; + rc.PMI = 0; + rc.control = 0; + rc.pad = 0; + err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc, sizeof (rcd), (char *) &rcd); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; +/* err_sense is ignored for now and Request Sense Data also... */ + if (err) return err; @@ -107,6 +188,8 @@ grub_scsi_read10 (grub_disk_t disk, grub_disk_addr_t sector, { grub_scsi_t scsi; struct grub_scsi_read10 rd; + grub_err_t err; + grub_err_t err_sense; scsi = disk->data; @@ -118,7 +201,16 @@ grub_scsi_read10 (grub_disk_t disk, grub_disk_addr_t sector, rd.reserved2 = 0; rd.pad = 0; - return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; } /* Send a SCSI request for DISK: read SIZE sectors starting with @@ -129,6 +221,8 @@ grub_scsi_read12 (grub_disk_t disk, grub_disk_addr_t sector, { grub_scsi_t scsi; struct grub_scsi_read12 rd; + grub_err_t err; + grub_err_t err_sense; scsi = disk->data; @@ -139,7 +233,16 @@ grub_scsi_read12 (grub_disk_t disk, grub_disk_addr_t sector, rd.reserved = 0; rd.control = 0; - return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; } #if 0 @@ -151,6 +254,8 @@ grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector, { grub_scsi_t scsi; struct grub_scsi_write10 wr; + grub_err_t err; + grub_err_t err_sense; scsi = disk->data; @@ -162,7 +267,16 @@ grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector, wr.reserved2 = 0; wr.pad = 0; - return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; } /* Send a SCSI request for DISK: write the data stored in BUF to SIZE @@ -172,7 +286,9 @@ grub_scsi_write12 (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) { grub_scsi_t scsi; - struct grub_scsi_write10 wr; + struct grub_scsi_write12 wr; + grub_err_t err; + grub_err_t err_sense; scsi = disk->data; @@ -181,9 +297,18 @@ grub_scsi_write12 (grub_disk_t disk, grub_disk_addr_t sector, wr.lba = grub_cpu_to_be32 (sector); wr.size = grub_cpu_to_be32 (size); wr.reserved = 0; - wr.pad = 0; + wr.control = 0; - return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; } #endif @@ -235,6 +360,7 @@ grub_scsi_open (const char *name, grub_disk_t disk) grub_err_t err; int len; int lun; + grub_uint64_t maxtime; scsi = grub_malloc (sizeof (*scsi)); if (! scsi) @@ -292,6 +418,31 @@ grub_scsi_open (const char *name, grub_disk_t disk) else disk->has_partitions = 1; + + /* According to USB MS tests specification, issue Test Unit Ready + * until OK */ + maxtime = grub_get_time_ms () + 1000; + do + { + /* Timeout is necessary - for example in case when we have + * universal card reader with more LUNs and we have only + * one card inserted (or none), so only one LUN (or none) + * will be ready - and we want not to hang... */ + if (grub_get_time_ms () > maxtime) + { + err = GRUB_ERR_READ_ERROR; + grub_free (scsi); + grub_dprintf ("scsi", "LUN is not ready - timeout\n"); + return err; + } + err = grub_scsi_test_unit_ready (scsi); + } + while (err == GRUB_ERR_READ_ERROR); + /* Reset grub_errno ! + * It is set to some error code in loop before... */ + grub_errno = GRUB_ERR_NONE; + + /* Read capacity of media */ err = grub_scsi_read_capacity (scsi); if (err) { @@ -302,12 +453,14 @@ grub_scsi_open (const char *name, grub_disk_t disk) /* SCSI blocks can be something else than 512, although GRUB wants 512 byte blocks. */ - disk->total_sectors = ((scsi->size * scsi->blocksize) - << GRUB_DISK_SECTOR_BITS); + disk->total_sectors = ((grub_uint64_t)scsi->size + * (grub_uint64_t)scsi->blocksize) + >> GRUB_DISK_SECTOR_BITS; - grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n", - (unsigned long long) disk->total_sectors, - scsi->blocksize); + grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n", + scsi->size, scsi->blocksize); + grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n", + disk->total_sectors); return GRUB_ERR_NONE; } diff --git a/disk/usbms.c b/disk/usbms.c index 8554b224f..7b719fb84 100644 --- a/disk/usbms.c +++ b/disk/usbms.c @@ -84,7 +84,8 @@ grub_usbms_finddevs (void) struct grub_usb_desc_device *descdev = &usbdev->descdev; int i; - if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0) + if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0 + || descdev->configcnt == 0) return 0; /* XXX: Just check configuration 0 for now. */ @@ -93,19 +94,31 @@ grub_usbms_finddevs (void) struct grub_usbms_dev *usbms; struct grub_usb_desc_if *interf; int j; - grub_uint8_t luns; + grub_uint8_t luns = 0; + + grub_dprintf ("usbms", "alive\n"); interf = usbdev->config[0].interf[i].descif; /* If this is not a USB Mass Storage device with a supported protocol, just skip it. */ + grub_dprintf ("usbms", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n", + i, interf->class, interf->subclass, interf->protocol); + if (interf->class != GRUB_USB_CLASS_MASS_STORAGE - || interf->subclass != GRUB_USBMS_SUBCLASS_BULK + || ( interf->subclass != GRUB_USBMS_SUBCLASS_BULK && + /* Experimental support of RBC, MMC-2, UFI, SFF-8070i devices */ + interf->subclass != GRUB_USBMS_SUBCLASS_RBC && + interf->subclass != GRUB_USBMS_SUBCLASS_MMC2 && + interf->subclass != GRUB_USBMS_SUBCLASS_UFI && + interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 ) || interf->protocol != GRUB_USBMS_PROTOCOL_BULK) { continue; } + grub_dprintf ("usbms", "alive\n"); + devcnt++; usbms = grub_zalloc (sizeof (struct grub_usbms_dev)); if (! usbms) @@ -114,6 +127,8 @@ grub_usbms_finddevs (void) usbms->dev = usbdev; usbms->interface = i; + grub_dprintf ("usbms", "alive\n"); + /* Iterate over all endpoints of this interface, at least a IN and OUT bulk endpoint are required. */ for (j = 0; j < interf->endpointcnt; j++) @@ -125,14 +140,16 @@ grub_usbms_finddevs (void) { /* Bulk IN endpoint. */ usbms->in = endp; - grub_usb_clear_halt (usbdev, endp->endp_addr & 128); + /* Clear Halt is not possible yet! */ + /* grub_usb_clear_halt (usbdev, endp->endp_addr); */ usbms->in_maxsz = endp->maxpacket; } else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2) { /* Bulk OUT endpoint. */ usbms->out = endp; - grub_usb_clear_halt (usbdev, endp->endp_addr & 128); + /* Clear Halt is not possible yet! */ + /* grub_usb_clear_halt (usbdev, endp->endp_addr); */ usbms->out_maxsz = endp->maxpacket; } } @@ -143,51 +160,63 @@ grub_usbms_finddevs (void) return 0; } + grub_dprintf ("usbms", "alive\n"); + + /* XXX: Activate the first configuration. */ + grub_usb_set_configuration (usbdev, 1); + /* Query the amount of LUNs. */ err = grub_usb_control_msg (usbdev, 0xA1, 254, 0, i, 1, (char *) &luns); + if (err) { /* In case of a stall, clear the stall. */ if (err == GRUB_USB_ERR_STALL) { - grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3); - grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3); + grub_usb_clear_halt (usbdev, usbms->in->endp_addr); + grub_usb_clear_halt (usbdev, usbms->out->endp_addr); } - /* Just set the amount of LUNs to one. */ grub_errno = GRUB_ERR_NONE; usbms->luns = 1; } else - usbms->luns = luns; + /* luns = 0 means one LUN with ID 0 present ! */ + /* We get from device not number of LUNs but highest + * LUN number. LUNs are numbered from 0, + * i.e. number of LUNs is luns+1 ! */ + usbms->luns = luns + 1; - /* XXX: Check the magic values, does this really make - sense? */ - grub_usb_control_msg (usbdev, (1 << 6) | 1, 255, - 0, i, 0, 0); - - /* XXX: To make Qemu work? */ - if (usbms->luns == 0) - usbms->luns = 1; + grub_dprintf ("usbms", "alive\n"); usbms->next = grub_usbms_dev_list; grub_usbms_dev_list = usbms; - /* XXX: Activate the first configuration. */ - grub_usb_set_configuration (usbdev, 1); - +#if 0 /* All this part should be probably deleted. + * This make trouble on some devices if they are not in + * Phase Error state - and there they should be not in such state... + * Bulk only mass storage reset procedure should be used only + * on place and in time when it is really necessary. */ + /* Reset recovery procedure */ /* Bulk-Only Mass Storage Reset, after the reset commands will be accepted. */ grub_usbms_reset (usbdev, i); + grub_usb_clear_halt (usbdev, usbms->in->endp_addr); + grub_usb_clear_halt (usbdev, usbms->out->endp_addr); +#endif return 0; } + grub_dprintf ("usbms", "alive\n"); return 0; } + grub_dprintf ("usbms", "alive\n"); grub_usb_iterate (usb_iterate); + grub_dprintf ("usbms", "alive\n"); + } @@ -225,6 +254,7 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, static grub_uint32_t tag = 0; grub_usb_err_t err = GRUB_USB_ERR_NONE; int retrycnt = 3 + 1; + grub_size_t i; retry: retrycnt--; @@ -237,73 +267,89 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, cbw.tag = tag++; cbw.transfer_length = grub_cpu_to_le32 (size); cbw.flags = (!read_write) << GRUB_USBMS_DIRECTION_BIT; - cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place than in SCSI CDB, both should be set correctly. */ cbw.length = cmdsize; grub_memcpy (cbw.cbwcb, cmd, cmdsize); + + /* Debug print of CBW content. */ + grub_dprintf ("usb", "CBW: sign=0x%08x tag=0x%08x len=0x%08x\n", + cbw.signature, cbw.tag, cbw.transfer_length); + grub_dprintf ("usb", "CBW: flags=0x%02x lun=0x%02x CB_len=0x%02x\n", + cbw.flags, cbw.lun, cbw.length); + grub_dprintf ("usb", "CBW: cmd:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + cbw.cbwcb[ 0], cbw.cbwcb[ 1], cbw.cbwcb[ 2], cbw.cbwcb[ 3], + cbw.cbwcb[ 4], cbw.cbwcb[ 5], cbw.cbwcb[ 6], cbw.cbwcb[ 7], + cbw.cbwcb[ 8], cbw.cbwcb[ 9], cbw.cbwcb[10], cbw.cbwcb[11], + cbw.cbwcb[12], cbw.cbwcb[13], cbw.cbwcb[14], cbw.cbwcb[15]); - /* Write the request. */ - err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15, + /* Write the request. + * XXX: Error recovery is maybe still not fully correct. */ + err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, sizeof (cbw), (char *) &cbw); if (err) { if (err == GRUB_USB_ERR_STALL) { + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr); goto retry; } return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed"); } - /* Read/write the data. */ - if (read_write == 0) + /* Read/write the data, (maybe) according to specification. */ + if (size && (read_write == 0)) { - err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf); - grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); - if (err) - { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->in->endp_addr); - goto retry; - } - return grub_error (GRUB_ERR_READ_ERROR, - "can't read from USB Mass Storage device"); - } + err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf); + grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); + if (err) goto CheckCSW; + /* Debug print of received data. */ + grub_dprintf ("usb", "buf:\n"); + if (size <= 64) + for (i=0; idev, dev->in->endp_addr & 15, size, buf); + err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf); grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL); - if (err) - { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->out->endp_addr); - goto retry; - } - return grub_error (GRUB_ERR_WRITE_ERROR, - "can't write to USB Mass Storage device"); - } + grub_dprintf ("usb", "buf:\n"); + /* Debug print of sent data. */ + if (size <= 256) + for (i=0; idev, dev->in->endp_addr & 15, - sizeof (status), (char *) &status); + /* Read the status - (maybe) according to specification. */ +CheckCSW: + err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, + sizeof (status), (char *) &status); if (err) { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, + sizeof (status), (char *) &status); + if (err) + { /* Bulk-only reset device. */ + grub_usbms_reset (dev->dev, dev->interface); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); goto retry; - } - return grub_error (GRUB_ERR_READ_ERROR, - "can't read status from USB Mass Storage device"); + } } - /* XXX: Magic and check this code. */ + /* Debug print of CSW content. */ + grub_dprintf ("usb", "CSW: sign=0x%08x tag=0x%08x resid=0x%08x\n", + status.signature, status.tag, status.residue); + grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status); + + /* If phase error, do bulk-only reset device. */ if (status.status == 2) { - /* XXX: Phase error, reset device. */ grub_usbms_reset (dev->dev, dev->interface); grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr); diff --git a/include/grub/scsicmd.h b/include/grub/scsicmd.h index 40f237a17..f9a7cb200 100644 --- a/include/grub/scsicmd.h +++ b/include/grub/scsicmd.h @@ -25,14 +25,26 @@ #define GRUB_SCSI_REMOVABLE_BIT 7 #define GRUB_SCSI_LUN_SHIFT 5 +struct grub_scsi_test_unit_ready +{ + grub_uint8_t opcode; + grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */ + grub_uint8_t reserved1; + grub_uint8_t reserved2; + grub_uint8_t reserved3; + grub_uint8_t control; + grub_uint8_t pad[6]; /* To be ATAPI compatible */ +} __attribute__((packed)); + struct grub_scsi_inquiry { grub_uint8_t opcode; - grub_uint8_t lun; - grub_uint16_t reserved; - grub_uint16_t alloc_length; - grub_uint8_t reserved2; - grub_uint8_t pad[5]; + grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 EVPD */ + grub_uint8_t page; /* page code if EVPD=1 */ + grub_uint8_t reserved; + grub_uint8_t alloc_length; + grub_uint8_t control; + grub_uint8_t pad[6]; /* To be ATAPI compatible */ } __attribute__((packed)); struct grub_scsi_inquiry_data @@ -47,12 +59,42 @@ struct grub_scsi_inquiry_data char prodrev[4]; } __attribute__((packed)); +struct grub_scsi_request_sense +{ + grub_uint8_t opcode; + grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */ + grub_uint8_t reserved1; + grub_uint8_t reserved2; + grub_uint8_t alloc_length; + grub_uint8_t control; + grub_uint8_t pad[6]; /* To be ATAPI compatible */ +} __attribute__((packed)); + +struct grub_scsi_request_sense_data +{ + grub_uint8_t error_code; /* 7 Valid, 6-0 Err. code */ + grub_uint8_t segment_number; + grub_uint8_t sense_key; /*7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */ + grub_uint32_t information; + grub_uint8_t additional_sense_length; + grub_uint32_t cmd_specific_info; + grub_uint8_t additional_sense_code; + grub_uint8_t additional_sense_code_qualifier; + grub_uint8_t field_replaceable_unit_code; + grub_uint8_t sense_key_specific[3]; + /* there can be additional sense field */ +} __attribute__((packed)); + struct grub_scsi_read_capacity { grub_uint8_t opcode; - grub_uint8_t lun; - grub_uint8_t reserved[8]; - grub_uint8_t pad[2]; + grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 reserved */ + grub_uint32_t logical_block_addr; /* only if PMI=1 */ + grub_uint8_t reserved1; + grub_uint8_t reserved2; + grub_uint8_t PMI; + grub_uint8_t control; + grub_uint16_t pad; /* To be ATAPI compatible */ } __attribute__((packed)); struct grub_scsi_read_capacity_data @@ -106,11 +148,13 @@ struct grub_scsi_write12 typedef enum { grub_scsi_cmd_inquiry = 0x12, + grub_scsi_cmd_test_unit_ready = 0x00, grub_scsi_cmd_read_capacity = 0x25, grub_scsi_cmd_read10 = 0x28, grub_scsi_cmd_write10 = 0x2a, grub_scsi_cmd_read12 = 0xa8, - grub_scsi_cmd_write12 = 0xaa + grub_scsi_cmd_write12 = 0xaa, + grub_scsi_cmd_request_sense = 0x03 } grub_scsi_cmd_t; typedef enum diff --git a/include/grub/usb.h b/include/grub/usb.h index dc90e7879..cbbc39303 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -156,7 +156,7 @@ struct grub_usb_device int initialized; /* Data toggle values (used for bulk transfers only). */ - int toggle[16]; + int toggle[256]; /* Device-specific data. */ void *data; @@ -184,7 +184,12 @@ typedef enum typedef enum { - GRUB_USBMS_SUBCLASS_BULK = 0x06 + GRUB_USBMS_SUBCLASS_BULK = 0x06, + /* Experimental support for non-pure SCSI devices */ + GRUB_USBMS_SUBCLASS_RBC = 0x01, + GRUB_USBMS_SUBCLASS_MMC2 = 0x02, + GRUB_USBMS_SUBCLASS_UFI = 0x04, + GRUB_USBMS_SUBCLASS_SFF8070 = 0x05 } grub_usbms_subclass_t; typedef enum diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h index 8c739684e..cd818e1fc 100644 --- a/include/grub/usbtrans.h +++ b/include/grub/usbtrans.h @@ -86,9 +86,9 @@ typedef struct grub_usb_transfer *grub_usb_transfer_t; #define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00 -#define GRUB_USB_FEATURE_ENDP_HALT 0x01 -#define GRUB_USB_FEATURE_DEV_REMOTE_WU 0x02 -#define GRUB_USB_FEATURE_TEST_MODE 0x04 +#define GRUB_USB_FEATURE_ENDP_HALT 0x00 +#define GRUB_USB_FEATURE_DEV_REMOTE_WU 0x01 +#define GRUB_USB_FEATURE_TEST_MODE 0x02 #define GRUB_USB_HUB_STATUS_CONNECTED (1 << 0) #define GRUB_USB_HUB_STATUS_LOWSPEED (1 << 9)