diff options
| author | Michael Brown | 2005-03-08 19:53:11 +0100 |
|---|---|---|
| committer | Michael Brown | 2005-03-08 19:53:11 +0100 |
| commit | 3d6123e69ab879c72ff489afc5bf93ef0b7a94ce (patch) | |
| tree | 9f3277569153a550fa8d81ebd61bd88f266eb8da /src/filo | |
| download | ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.gz ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.xz ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.zip | |
Initial revision
Diffstat (limited to 'src/filo')
52 files changed, 16693 insertions, 0 deletions
diff --git a/src/filo/Config b/src/filo/Config new file mode 100644 index 000000000..9f39b3d99 --- /dev/null +++ b/src/filo/Config @@ -0,0 +1,68 @@ +# !!! NOTE !!! +# Do NOT add spaces or comments at the end of option lines. +# It confuses some versions of make. + +# Image filename for automatic boot and optional command line parameter +#AUTOBOOT_FILE = "hda3:/boot/vmlinuz root=/dev/hda3 console=tty0 console=ttyS0,115200" +AUTOBOOT_FILE = "hda2:/boot/vmlinuz initrd=/boot/initrd pci=noacpi ro root=/dev/hda2 console=tty0 console=ttyS0,115200" +#AUTOBOOT_FILE = "mem@0xfff80000" +#AUTOBOOT_FILE = "hde1@0" +#AUTOBOOT_FILE = "uda1:/ram0_2.5_2.6.5_k8.2_mydisk7.elf" +#AUTOBOOT_FILE = "hda5:/boot/vmlinuz initrd=/boot/initrd ro root=/dev/hda7 console=tty0 console=ttyS0,115200" + +# Time in second before booting AUTOBOOT_FILE +AUTOBOOT_DELAY = 2 + +# Driver for hard disk, CompactFlash, and CD-ROM on IDE bus +IDE_DISK = 1 + +# Driver for USB disk +USB_DISK = 1 + +# Filesystems +# To make filo.zelf < 32 k, You may not enable JFS, MINIX, XFS +# Is anyone still using these file system? BY LYH +FSYS_EXT2FS = 1 +FSYS_FAT = 1 +#FSYS_JFS = 1 +#FSYS_MINIX = 1 +FSYS_REISERFS = 1 +#FSYS_XFS = 1 +FSYS_ISO9660 = 1 + +# Support for boot disk image in bootable CD-ROM (El Torito) +ELTORITO = 1 + +# PCI support +SUPPORT_PCI = 1 + + +# Debugging +#DEBUG_ALL = 1 +#DEBUG_ELFBOOT = 1 +#DEBUG_ELFNOTE = 1 +#DEBUG_LINUXBIOS = 1 +#DEBUG_MALLOC = 1 +#DEBUG_MULTIBOOT = 1 +#DEBUG_SEGMENT = 1 +#DEBUG_SYS_INFO = 1 +#DEBUG_TIMER = 1 +#DEBUG_BLOCKDEV = 1 +#DEBUG_PCI = 1 +#DEBUG_LINUXLOAD = 1 +#DEBUG_IDE = 1 +#DEBUG_USB = 1 +#DEBUG_ELTORITO = 1 + +# i386 options + +# Loader for standard Linux kernel image, a.k.a. /vmlinuz +LINUX_LOADER = 1 + +# Boot FILO from Multiboot loader (eg. GRUB) +# You need to modify i386/multiboot.c to use it. change mmrange to e820entries. +# By LYH +#MULTIBOOT_IMAGE = 1 + +# Use PCI Configuration Mechanism #1 (most boards) +PCI_CONFIG_1 = 1 diff --git a/src/filo/README.filo b/src/filo/README.filo new file mode 100644 index 000000000..1e801d313 --- /dev/null +++ b/src/filo/README.filo @@ -0,0 +1,125 @@ +This is FILO, a bootloader which loads boot images from local filesystem, +without help from legacy BIOS services. + +Expected usage is to flash it into the BIOS ROM together with LinuxBIOS. + +FEATURES + + - Supported boot devices: IDE hard disk and CD-ROM, and system memory (ROM) + - Supported filesystems: ext2, fat, jfs, minix, reiserfs, xfs, and iso9660 + - Supported image formats: ELF and [b]zImage (a.k.a. /vmlinuz) + - Supports boot disk image of El Torito bootable CD-ROM + - Supports loading image from raw device with user-specified offset + - Console on VGA + keyboard, serial port, or both + - Line editing with ^H, ^W and ^U keys to type arbitrary filename to boot + - Full support for the ELF Boot Proposal (where is it btw, Eric?) + - Auxiliary tool to compute checksum of ELF boot images + - Full 32-bit code, no BIOS calls + +REQUIREMENT + + Only i386 PC architecture is currently supported. + + x86-64 (AMD 64) machines in 32-bit mode should also work. + (It looks like LinuxBIOS uses 32-bit mode and Linux kernel does + the transition to 64-bit mode) + + I'm using a VIA EPIA 5000 mini-ITX board, with a 2.5" IDE hard disk + and a 32x CD-RW, for testing, and Bochs and VMware for development. + + Recent version of GNU toolchain is required to build. + I have tested with Debian/woody (gcc 2.95.4, binutils 2.12.90.0.1, + make 3.79.1) and Debian/sid (gcc 3.3.2, binutils 2.14.90.0.6, + make 3.80). + +INSTALL + + First invocation of make creates the default Config file. + $ make + Edit this file as you like. It's fairly straightforward (I hope). + $ vi Config + Then running make again will build filo.elf, the ELF boot image of FILO. + $ make + + Use filo.elf as your payload of LinuxBIOS, or a boot image for + Etherboot. + + If you enable MULTIBOOT_IMAGE option in Config, you can + also boot filo.elf from GNU GRUB or other Multiboot bootloader. + This feature is intended for testing or development purpose. + +USING + + When FILO starts, it displays "boot:" prompt. + At "boot:" prompt, type the name of your boot image, and optionally + the kernel parameter, in the form: + DEVICE:FILENAME[ PARAM] + for example: + boot: hda1:/vmlinuz root=/dev/hda1 + + Notation of DEVICE for IDE disk and CD-ROM is same as in Linux + (eg. hda1 means the first partition of master device on primary + IDE channel). + + FILENAME can be standard bzImage/zImage (vmlinuz) Linux kernels, + Linux-compatible images such as memtest.bin of Memtest86, + and any bootable ELF images, which include Linux kernel converted + by mkelfImage, Etherboot .elf and .zelf, Memtest86, FILO itself, etc. + + If AUTOBOOT_FILE is set in Config, FILO tries to boot this file + first, and falls back to boot: prompt if it fails. + + If AUTOBOOT_DELAY is also set, FILO waits for specified time in + seconds before booting AUTOBOOT_FILE. If <Esc> key is pressed + during this time period, automatic boot is canceled. + Pressing <Enter> key also cancels the delay, but in this case + AUTOBOOT_FILE is booted immediately. + + Even if AUTOBOOT_DELAY is not set, automatic boot can be disabled + by pressing <Esc> key beforehand. + + FILO can also load separate initrd images along with vmlinuz + kernels. (For ELF kernel, initrd images are embedded into the + ELF file and cannot be altered). + To do so, add "initrd=NAME" parameter to the kernel command line. + NAME uses the same notation as kernel image name. + (eg. boot: hda1:/vmlinuz initrd=hda1:/root.gz root=/dev/ram) + + To boot an image in the BIOS flash (or whatever is mapped in the system + memory space), use the notation "mem@OFFSET[,LENGTH]", like: + boot: mem@0xfffe0000 + In this example, it loads the boot image from the last 128KB of BIOS + flash. + + The same notation can be used with IDE devices, eg: + boot: hda@512,697344 initrd=hda@1M,4M + In this case the 697344 bytes starting from second sector of IDE drive + is loaded as kernel, and 4M bytes of offset 1M bytes of the same disk + is loaded as initrd. + Note that when you load vmlinuz kernel or initrd this way, + you must specify the LENGTH parameter. You can omit it for ELF + images since they have segment length internally. + OFFSET and LENGTH parameters must be multiple of 512. + +BUG REPORTING + + If you have problem with FILO, set DEBUG_ALL in Config and send its + console output to me at <ts1@tsn.or.jp>. + +ACKNOWLEDGEMENTS + + Filesystem code is taken from GNU GRUB and patches for it. + IDE driver is originally taken from Etherboot. + Steve Gehlbach wrote the original bzImage loader for FILO. + + Besides, I have taken pieces of code and/or learned concepts + from various standalone programs, including GNU GRUB, Etherboot, + polled IDE patch by Adam Agnew, Memtest86, LinuxBIOS, and Linux. + I must say thanks to all the developers of these wonderful software, + especially to Eric Biederman for his great development work in this area. + +LICENSE + + Copyright (C) 2003 by SONE Takeshi <ts1@tsn.or.jp> and others. + This program is licensed under the terms of GNU General Public License. + See the COPYING file for details. diff --git a/src/filo/README.filo_in_etherboot b/src/filo/README.filo_in_etherboot new file mode 100644 index 000000000..dc203f635 --- /dev/null +++ b/src/filo/README.filo_in_etherboot @@ -0,0 +1,48 @@ +Moved from FILO into Etherboot, yhlu add boot from SATA disk and move usb boot framework +from Steven James baremetal in LinuxBIOS, also add the OHCI support. + + +1. refer to README.filo + but don't need to use make config. +2. CFLAG added + CONSOLE_BTEXT --- for btext console support + CONSOLE_PC_KBD --- for direct pc keyboard support + CONFIG_FILO --- It will make main call pci_init +3. to make: + make bin/filo.zelf + or + make bin/tg3--filo.zelf + + You can not use filo and ide_disk at the same time. + +Some input for boot: + +boot from BIOS ROM area +4G-128K +mem@0xfffe0000 +4G-512K +mem@0xfff80000 + +boot from suse +hda2:/boot/vmlinuz initrd=/boot/initrd ro root=/dev/hda2 console=tty0 console=ttyS0,115200 +for suse install from CD +hdc:/boot/loader/linux initrd=/boot/loader/initrd ramdisk_size=65536 splash=silent showopts console=tty0 console=ttyS0,115200 + +boot from RH +for RH install from CD +hdc:/isolinux/vmlinuz initrd=/isolinux/initrd.img expert nofb acpi=off devfs=nomount ramdisk_size=65536 console=ttyS0,115200 + +for serial ATA support (using port1 and port2 only) +1) if your kernel think SATA as SCSI +hde2:/boot/vmlinuz initrd=/boot/initrd ro root=/dev/sda2 console=tty0 console=ttyS0,115200 +2) if your kernel think SATA as normal IDE +hde2:/boot/vmlinuz initrd=/boot/initrd ro root=/dev/hde2 console=tty0 console=ttyS0,115200 + +for usb support +uda1:/ram0_2.5_2.6.5_k8.2_mydisk7.elf + + +Yinghai Lu yhlu@tyan.com + +to do: + add menu to filo boot diff --git a/src/filo/README.usb b/src/filo/README.usb new file mode 100644 index 000000000..1724e7bb2 --- /dev/null +++ b/src/filo/README.usb @@ -0,0 +1,15 @@ +It is from steven james's baremetal in linuxbios util. +yhlu seperate common functions from uhci.c to usb.c and create ohci.c to support ohci. +ohci.c mainly cames from kernel 2.4.22 dirvers/usb/host/usb-ohci.c. +it includes several parts +1. UHCI+OHCI--->USB: privide usb init and usb_control_msg and usb_bulk_msg interface +2. USB_SCSI: bulk only device +3. USB_X interface to FILO + +other changes in Etherboot +1. Add allot2 and forget2, it will produce the required aligned memory. + +todo: +1. EHCI support + +yhlu 6/2/2004 diff --git a/src/filo/drivers/ide_x.c b/src/filo/drivers/ide_x.c new file mode 100644 index 000000000..09c634e23 --- /dev/null +++ b/src/filo/drivers/ide_x.c @@ -0,0 +1,1129 @@ +/* Derived from Etherboot 5.1 */ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_IDE +#include <debug.h> + +#define IDE_MAX_CONTROLLERS 4 +#define IDE_MAX_DRIVES (IDE_MAX_CONTROLLERS * 2) + +#define BSY_SET_DURING_SPINUP 1 +/* + * UBL, The Universal Talkware Boot Loader + * Copyright (C) 2000 Universal Talkware Inc. + * Copyright (C) 2002 Eric Biederman + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ +struct controller { + uint16_t cmd_base; + uint16_t ctrl_base; +}; + +struct harddisk_info { + struct controller *ctrl; + uint16_t heads; + uint16_t cylinders; + uint16_t sectors_per_track; + uint8_t model_number[41]; + uint8_t slave; + sector_t sectors; + int address_mode; +#define ADDRESS_MODE_CHS 0 +#define ADDRESS_MODE_LBA 1 +#define ADDRESS_MODE_LBA48 2 +#define ADDRESS_MODE_PACKET 3 + uint32_t hw_sector_size; + unsigned drive_exists : 1; + unsigned slave_absent : 1; + unsigned removable : 1; +}; + +#define IDE_SECTOR_SIZE 0x200 +#define CDROM_SECTOR_SIZE 0x800 + +#define IDE_BASE0 (0x1F0u) /* primary controller */ +#define IDE_BASE1 (0x170u) /* secondary */ +#define IDE_BASE2 (0x1E8u) /* third */ +#define IDE_BASE3 (0x168u) /* fourth */ +#define IDE_BASE4 (0x1E0u) /* fifth */ +#define IDE_BASE5 (0x160u) /* sixth */ + +#define IDE_REG_EXTENDED_OFFSET (0x204u) + +#define IDE_REG_DATA(ctrl) ((ctrl)->cmd_base + 0u) /* word register */ +#define IDE_REG_ERROR(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_PRECOMP(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_FEATURE(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_SECTOR_COUNT(ctrl) ((ctrl)->cmd_base + 2u) +#define IDE_REG_SECTOR_NUMBER(ctrl) ((ctrl)->cmd_base + 3u) +#define IDE_REG_LBA_LOW(ctrl) ((ctrl)->cmd_base + 3u) +#define IDE_REG_CYLINDER_LSB(ctrl) ((ctrl)->cmd_base + 4u) +#define IDE_REG_LBA_MID(ctrl) ((ctrl)->cmd_base + 4u) +#define IDE_REG_CYLINDER_MSB(ctrl) ((ctrl)->cmd_base + 5u) +#define IDE_REG_LBA_HIGH(ctrl) ((ctrl)->cmd_base + 5u) +#define IDE_REG_DRIVEHEAD(ctrl) ((ctrl)->cmd_base + 6u) +#define IDE_REG_DEVICE(ctrl) ((ctrl)->cmd_base + 6u) +#define IDE_REG_STATUS(ctrl) ((ctrl)->cmd_base + 7u) +#define IDE_REG_COMMAND(ctrl) ((ctrl)->cmd_base + 7u) +#define IDE_REG_ALTSTATUS(ctrl) ((ctrl)->ctrl_base + 2u) +#define IDE_REG_DEVICE_CONTROL(ctrl) ((ctrl)->ctrl_base + 2u) + +struct ide_pio_command +{ + uint8_t feature; + uint8_t sector_count; + uint8_t lba_low; + uint8_t lba_mid; + uint8_t lba_high; + uint8_t device; +# define IDE_DH_DEFAULT (0xA0) +# define IDE_DH_HEAD(x) ((x) & 0x0F) +# define IDE_DH_MASTER (0x00) +# define IDE_DH_SLAVE (0x10) +# define IDE_DH_LBA (0x40) +# define IDE_DH_CHS (0x00) + uint8_t command; + uint8_t sector_count2; + uint8_t lba_low2; + uint8_t lba_mid2; + uint8_t lba_high2; +}; + +#define IDE_DEFAULT_COMMAND { 0xFFu, 0x01, 0x00, 0x0000, IDE_DH_DEFAULT } + +#define IDE_ERR_ICRC 0x80 /* ATA Ultra DMA bad CRC */ +#define IDE_ERR_BBK 0x80 /* ATA bad block */ +#define IDE_ERR_UNC 0x40 /* ATA uncorrected error */ +#define IDE_ERR_MC 0x20 /* ATA media change */ +#define IDE_ERR_IDNF 0x10 /* ATA id not found */ +#define IDE_ERR_MCR 0x08 /* ATA media change request */ +#define IDE_ERR_ABRT 0x04 /* ATA command aborted */ +#define IDE_ERR_NTK0 0x02 /* ATA track 0 not found */ +#define IDE_ERR_NDAM 0x01 /* ATA address mark not found */ + +#define IDE_STATUS_BSY 0x80 /* busy */ +#define IDE_STATUS_RDY 0x40 /* ready */ +#define IDE_STATUS_DF 0x20 /* device fault */ +#define IDE_STATUS_WFT 0x20 /* write fault (old name) */ +#define IDE_STATUS_SKC 0x10 /* seek complete */ +#define IDE_STATUS_DRQ 0x08 /* data request */ +#define IDE_STATUS_CORR 0x04 /* corrected */ +#define IDE_STATUS_IDX 0x02 /* index */ +#define IDE_STATUS_ERR 0x01 /* error (ATA) */ +#define IDE_STATUS_CHK 0x01 /* check (ATAPI) */ + +#define IDE_CTRL_HD15 0x08 /* bit should always be set to one */ +#define IDE_CTRL_SRST 0x04 /* soft reset */ +#define IDE_CTRL_NIEN 0x02 /* disable interrupts */ + + +/* Most mandtory and optional ATA commands (from ATA-3), */ + +#define IDE_CMD_CFA_ERASE_SECTORS 0xC0 +#define IDE_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define IDE_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define IDE_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define IDE_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define IDE_CMD_CHECK_POWER_MODE1 0xE5 +#define IDE_CMD_CHECK_POWER_MODE2 0x98 +#define IDE_CMD_DEVICE_RESET 0x08 +#define IDE_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define IDE_CMD_FLUSH_CACHE 0xE7 +#define IDE_CMD_FORMAT_TRACK 0x50 +#define IDE_CMD_IDENTIFY_DEVICE 0xEC +#define IDE_CMD_IDENTIFY_DEVICE_PACKET 0xA1 +#define IDE_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define IDE_CMD_IDLE1 0xE3 +#define IDE_CMD_IDLE2 0x97 +#define IDE_CMD_IDLE_IMMEDIATE1 0xE1 +#define IDE_CMD_IDLE_IMMEDIATE2 0x95 +#define IDE_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91 +#define IDE_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define IDE_CMD_NOP 0x00 +#define IDE_CMD_PACKET 0xA0 +#define IDE_CMD_READ_BUFFER 0xE4 +#define IDE_CMD_READ_DMA 0xC8 +#define IDE_CMD_READ_DMA_QUEUED 0xC7 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_READ_SECTORS 0x20 +#define IDE_CMD_READ_SECTORS_EXT 0x24 +#define IDE_CMD_READ_VERIFY_SECTORS 0x40 +#define IDE_CMD_RECALIBRATE 0x10 +#define IDE_CMD_SEEK 0x70 +#define IDE_CMD_SET_FEATURES 0xEF +#define IDE_CMD_SET_MAX_ADDR_EXT 0x24 +#define IDE_CMD_SET_MULTIPLE_MODE 0xC6 +#define IDE_CMD_SLEEP1 0xE6 +#define IDE_CMD_SLEEP2 0x99 +#define IDE_CMD_STANDBY1 0xE2 +#define IDE_CMD_STANDBY2 0x96 +#define IDE_CMD_STANDBY_IMMEDIATE1 0xE0 +#define IDE_CMD_STANDBY_IMMEDIATE2 0x94 +#define IDE_CMD_WRITE_BUFFER 0xE8 +#define IDE_CMD_WRITE_DMA 0xCA +#define IDE_CMD_WRITE_DMA_QUEUED 0xCC +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_WRITE_SECTORS 0x30 +#define IDE_CMD_WRITE_VERIFY 0x3C + +/* IDE_CMD_SET_FEATURE sub commands */ +#define IDE_FEATURE_CFA_ENABLE_8BIT_PIO 0x01 +#define IDE_FEATURE_ENABLE_WRITE_CACHE 0x02 +#define IDE_FEATURE_SET_TRANSFER_MODE 0x03 +#define IDE_FEATURE_ENABLE_POWER_MANAGEMENT 0x05 +#define IDE_FEATURE_ENABLE_POWERUP_IN_STANDBY 0x06 +#define IDE_FEATURE_STANDBY_SPINUP_DRIVE 0x07 +#define IDE_FEATURE_CFA_ENABLE_POWER_MODE1 0x0A +#define IDE_FEATURE_DISABLE_MEDIA_STATUS_NOTIFICATION 0x31 +#define IDE_FEATURE_ENABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0x42 +#define IDE_FEATURE_SET_MAXIMUM_HOST_INTERFACE_SECTOR_TIMES 0x43 +#define IDE_FEATURE_DISABLE_READ_LOOKAHEAD 0x55 +#define IDE_FEATURE_ENABLE_RELEASE_INTERRUPT 0x5D +#define IDE_FEATURE_ENABLE_SERVICE_INTERRUPT 0x5E +#define IDE_FEATURE_DISABLE_REVERTING_TO_POWERON_DEFAULTS 0x66 +#define IDE_FEATURE_CFA_DISABLE_8BIT_PIO 0x81 +#define IDE_FEATURE_DISABLE_WRITE_CACHE 0x82 +#define IDE_FEATURE_DISABLE_POWER_MANAGEMENT 0x85 +#define IDE_FEATURE_DISABLE_POWERUP_IN_STANDBY 0x86 +#define IDE_FEATURE_CFA_DISABLE_POWER_MODE1 0x8A +#define IDE_FEATURE_ENABLE_MEDIA_STATUS_NOTIFICATION 0x95 +#define IDE_FEATURE_ENABLE_READ_LOOKAHEAD 0xAA +#define IDE_FEATURE_DISABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0xC2 +#define IDE_FEATURE_ENABLE_REVERTING_TO_POWERON_DEFAULTS 0xCC +#define IDE_FEATURE_DISABLE_SERVICE_INTERRUPT 0xDE + +static unsigned short ide_base[] = { + IDE_BASE0, + IDE_BASE1, + IDE_BASE2, + IDE_BASE3, + 0 +}; + +static struct controller controllers[IDE_MAX_CONTROLLERS]; +static struct harddisk_info harddisk_info[IDE_MAX_DRIVES]; + +static unsigned char ide_buffer[IDE_SECTOR_SIZE]; + +static int await_ide(int (*done)(struct controller *ctrl), + struct controller *ctrl, unsigned long timeout) +{ + int result; + for(;;) { + result = done(ctrl); + if (result) { + return 0; + } + //poll_interruptions(); + if ((timeout == 0) || (currticks() > timeout)) { + break; + } + } + printf("IDE time out\n"); + return -1; +} + +/* The maximum time any IDE command can last 31 seconds, + * So if any IDE commands takes this long we know we have problems. + */ +#define IDE_TIMEOUT (32*TICKS_PER_SEC) + +static int not_bsy(struct controller *ctrl) +{ + return !(inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY); +} + +/* IDE drives assert BSY bit within 400 nsec when SRST is set. + * Use 2 msec since our tick is 1 msec */ +#define IDE_RESET_PULSE (2*TICKS_PER_SEC / 18) + +static int bsy(struct controller *ctrl) +{ + return inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY; +} + +#if !BSY_SET_DURING_SPINUP +static int timeout(struct controller *ctrl) +{ + return 0; +} +#endif + +static void print_status(struct controller *ctrl) +{ + debug("IDE: status=%#x, err=%#x\n", + inb(IDE_REG_STATUS(ctrl)), inb(IDE_REG_ERROR(ctrl))); +} + +static int ide_software_reset(struct controller *ctrl) +{ + /* Wait a little bit in case this is immediately after + * hardware reset. + */ + mdelay(2); + /* A software reset should not be delivered while the bsy bit + * is set. If the bsy bit does not clear in a reasonable + * amount of time give up. + */ + debug("Waiting for ide%d to become ready for reset... ", + ctrl - controllers); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + debug("failed\n"); + return -1; + } + debug("ok\n"); + + /* Disable Interrupts and reset the ide bus */ + outb(IDE_CTRL_HD15 | IDE_CTRL_SRST | IDE_CTRL_NIEN, + IDE_REG_DEVICE_CONTROL(ctrl)); + /* If BSY bit is not asserted within 400ns, no device there */ + if (await_ide(bsy, ctrl, currticks() + IDE_RESET_PULSE) < 0) { + return -1; + } + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + mdelay(2); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + return 0; +} + +static void pio_set_registers( + struct controller *ctrl, const struct ide_pio_command *cmd) +{ + uint8_t device; + /* Disable Interrupts */ + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + + /* Possibly switch selected device */ + device = inb(IDE_REG_DEVICE(ctrl)); + outb(cmd->device, IDE_REG_DEVICE(ctrl)); + if ((device & (1UL << 4)) != (cmd->device & (1UL << 4))) { + /* Allow time for the selected drive to switch, + * The linux ide code suggests 50ms is the right + * amount of time to use here. + */ + mdelay(50); + } + outb(cmd->feature, IDE_REG_FEATURE(ctrl)); + if (cmd->command == IDE_CMD_READ_SECTORS_EXT) { + outb(cmd->sector_count2, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->lba_low2, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_mid2, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_high2, IDE_REG_LBA_HIGH(ctrl)); + } + outb(cmd->sector_count, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->lba_low, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_mid, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_high, IDE_REG_LBA_HIGH(ctrl)); + outb(cmd->command, IDE_REG_COMMAND(ctrl)); +} + + +static int pio_non_data(struct controller *ctrl, const struct ide_pio_command *cmd) +{ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + pio_set_registers(ctrl, cmd); + ndelay(400); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + /* FIXME is there more error checking I could do here? */ + return 0; +} + +static int pio_data_in(struct controller *ctrl, const struct ide_pio_command *cmd, + void *buffer, size_t bytes) +{ + unsigned int status; + + /* FIXME handle commands with multiple blocks */ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + /* How do I tell if INTRQ is asserted? */ + pio_set_registers(ctrl, cmd); + ndelay(400); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(ctrl)); + if (!(status & IDE_STATUS_DRQ)) { + print_status(ctrl); + return -1; + } + insw(IDE_REG_DATA(ctrl), buffer, bytes/2); + status = inb(IDE_REG_STATUS(ctrl)); + if (status & IDE_STATUS_DRQ) { + print_status(ctrl); + return -1; + } + return 0; +} + +static int pio_packet(struct harddisk_info *info, int in, + const void *packet, int packet_len, + void *buffer, int buffer_len) +{ + unsigned int status; + struct ide_pio_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, info->ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + /* Issue a PACKET command */ + cmd.lba_mid = (uint8_t) buffer_len; + cmd.lba_high = (uint8_t) (buffer_len >> 8); + cmd.device = IDE_DH_DEFAULT | info->slave; + cmd.command = IDE_CMD_PACKET; + pio_set_registers(info->ctrl, &cmd); + ndelay(400); + if (await_ide(not_bsy, info->ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(info->ctrl)); + if (!(status & IDE_STATUS_DRQ)) { + debug("no drq after PACKET\n"); + print_status(info->ctrl); + return -1; + } + + /* Send the packet */ + outsw(IDE_REG_DATA(info->ctrl), packet, packet_len/2); + + if (await_ide(not_bsy, info->ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(info->ctrl)); + if (buffer_len == 0) { + if (status & IDE_STATUS_DRQ) { + debug("drq after non-data command\n"); + print_status(info->ctrl); + return -1; + } + return 0; + } + + if (!(status & IDE_STATUS_DRQ)) { + debug("no drq after sending packet\n"); + print_status(info->ctrl); + return -1; + } + + insw(IDE_REG_DATA(info->ctrl), buffer, buffer_len/2); + + status = inb(IDE_REG_STATUS(info->ctrl)); + if (status & IDE_STATUS_DRQ) { + debug("drq after insw\n"); + print_status(info->ctrl); + return -1; + } + return 0; +} + +static inline int ide_read_sector_chs( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + unsigned int track; + unsigned int offset; + unsigned int cylinder; + + memset(&cmd, 0, sizeof(cmd)); + cmd.sector_count = 1; + + //debug("ide_read_sector_chs: sector= %ld.\n",sector); + + track = sector / info->sectors_per_track; + /* Sector number */ + offset = 1 + (sector % info->sectors_per_track); + cylinder = track / info->heads; + cmd.lba_low = offset; + cmd.lba_mid = cylinder & 0xff; + cmd.lba_high = (cylinder >> 8) & 0xff; + cmd.device = IDE_DH_DEFAULT | + IDE_DH_HEAD(track % info->heads) | + info->slave | + IDE_DH_CHS; + cmd.command = IDE_CMD_READ_SECTORS; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.device = IDE_DH_DEFAULT | + ((sector >> 24) & 0x0f) | + info->slave | + IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS; + //debug("%s: sector= %ld, device command= 0x%x.\n",__FUNCTION__,(unsigned long) sector, cmd.device); + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba48( + struct harddisk_info *info, void *buffer, sector_t sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + //debug("ide_read_sector_lba48: sector= %ld.\n",(unsigned long) sector); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.lba_low2 = (sector >> 24) & 0xff; + cmd.lba_mid2 = (sector >> 32) & 0xff; + cmd.lba_high2 = (sector >> 40) & 0xff; + cmd.device = info->slave | IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS_EXT; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_packet( + struct harddisk_info *info, void *buffer, sector_t sector) +{ + char packet[12]; + static uint8_t cdbuffer[CDROM_SECTOR_SIZE]; + static struct harddisk_info *last_disk = 0; + static sector_t last_sector = (sector_t) -1; + uint8_t *buf; + uint32_t hw_sector; + + //debug("sector=%Ld\n", sector); + + if (info->hw_sector_size == CDROM_SECTOR_SIZE) { + buf = cdbuffer; + hw_sector = sector >> 2; + } else { + buf = buffer; + hw_sector = sector; + } + + if (buf==buffer || info != last_disk || hw_sector != last_sector) { + //debug("hw_sector=%u\n", hw_sector); + memset(packet, 0, sizeof packet); + packet[0] = 0x28; /* READ */ + packet[2] = hw_sector >> 24; + packet[3] = hw_sector >> 16; + packet[4] = hw_sector >> 8; + packet[5] = hw_sector >> 0; + packet[7] = 0; + packet[8] = 1; /* length */ + + if (pio_packet(info, 1, packet, sizeof packet, + buf, info->hw_sector_size) != 0) { + debug("read error\n"); + return -1; + } + last_disk = info; + last_sector = hw_sector; + } + + if (buf != buffer) + memcpy(buffer, &cdbuffer[(sector & 3) << 9], IDE_SECTOR_SIZE); + return 0; +} + +int ide_read(int drive, sector_t sector, void *buffer) +{ + struct harddisk_info *info = &harddisk_info[drive]; + int result; + + //debug("drive=%d, sector=%ld\n",drive,(unsigned long) sector); + /* Report the buffer is empty */ + if (sector > info->sectors) { + return -1; + } + if (info->address_mode == ADDRESS_MODE_CHS) { + result = ide_read_sector_chs(info, buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA) { + result = ide_read_sector_lba(info, buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA48) { + result = ide_read_sector_lba48(info, buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_PACKET) { + result = ide_read_sector_packet(info, buffer, sector); + } + else { + result = -1; + } + return result; +} + +static int init_drive_x(struct harddisk_info *info, struct controller *ctrl, + int slave, int drive, unsigned char *buffer, int ident_command) +{ + uint16_t* drive_info; + struct ide_pio_command cmd; + int i; + + + info->ctrl = ctrl; + info->heads = 0u; + info->cylinders = 0u; + info->sectors_per_track = 0u; + info->address_mode = IDE_DH_CHS; + info->sectors = 0ul; + info->drive_exists = 0; + info->slave_absent = 0; + info->removable = 0; + info->hw_sector_size = IDE_SECTOR_SIZE; + info->slave = slave?IDE_DH_SLAVE: IDE_DH_MASTER; + + debug("Testing for hd%c\n", 'a'+drive); + + /* Select the drive that we are testing */ + outb(IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave, + IDE_REG_DEVICE(ctrl)); + mdelay(50); + + /* Test to see if the drive registers exist, + * In many cases this quickly rules out a missing drive. + */ + for(i = 0; i < 4; i++) { + outb(0xaa + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0xaa + i) { + return 1; + } + } + for(i = 0; i < 4; i++) { + outb(0x55 + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0x55 + i) { + return 1; + } + } + debug("Probing for hd%c\n", 'a'+drive); + + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.command = ident_command; + + + if (pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) { + /* Well, if that command didn't work, we probably don't have drive. */ + return 1; + } + + /* Now suck the data out */ + drive_info = (uint16_t *)buffer; + if (drive_info[2] == 0x37C8) { + /* If the response is incomplete spin up the drive... */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.feature = IDE_FEATURE_STANDBY_SPINUP_DRIVE; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If the command doesn't work give up on the drive */ + return 1; + } + + } + if ((drive_info[2] == 0x37C8) || (drive_info[2] == 0x8C73)) { + /* The response is incomplete retry the drive info command */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.command = ident_command; + if(pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) { + /* If the command didn't work give up on the drive. */ + return 1; + } + } + if ((drive_info[2] != 0x37C8) && + (drive_info[2] != 0x738C) && + (drive_info[2] != 0x8C73) && + (drive_info[2] != 0xC837) && + (drive_info[2] != 0x0000)) { + debugx("Invalid IDE Configuration: %hx\n", drive_info[2]); + return 1; + } + for(i = 27; i < 47; i++) { + info->model_number[((i-27)<< 1)] = (drive_info[i] >> 8) & 0xff; + info->model_number[((i-27)<< 1)+1] = drive_info[i] & 0xff; + } + info->model_number[40] = '\0'; + info->drive_exists = 1; + + /* See if LBA is supported */ + if (ident_command == IDE_CMD_IDENTIFY_PACKET_DEVICE) { + info->address_mode = ADDRESS_MODE_PACKET; + info->removable = 1; /* XXX */ + } else if (drive_info[49] & (1 << 9)) { + info->address_mode = ADDRESS_MODE_LBA; + info->sectors = (drive_info[61] << 16) | (drive_info[60]); +// debug("LBA mode, sectors=%Ld\n", info->sectors); + /* Enable LBA48 mode if it is present */ + if (drive_info[83] & (1 <<10)) { + /* Should LBA48 depend on LBA? */ + info->address_mode = ADDRESS_MODE_LBA48; + info->sectors = + (((sector_t)drive_info[103]) << 48) | + (((sector_t)drive_info[102]) << 32) | + (((sector_t)drive_info[101]) << 16) | + (((sector_t)drive_info[100]) << 0); +// debug("LBA48 mode, sectors=%Ld\n", info->sectors); + } + } else { + info->address_mode = ADDRESS_MODE_CHS; + info->heads = drive_info[3]; + info->cylinders = drive_info[1]; + info->sectors_per_track = drive_info[6]; + info->sectors = + info->sectors_per_track * + info->heads * + info->cylinders; + debug("CHS mode, sectors_per_track=[%d], heads=[%d], cylinders=[%d]\n", + info->sectors_per_track, + info->heads, + info->cylinders); +// debug("sectors=%Ld\n", info->sectors); + } + /* See if we have a slave */ + if (!info->slave && (((drive_info[93] >> 14) & 3) == 1)) { + info->slave_absent = !(drive_info[93] & (1 << 5)); + } + + /* See if we need to put the device in CFA power mode 1 */ + if ((drive_info[160] & ((1 << 15) | (1 << 13)| (1 << 12))) == + ((1 << 15) | (1 << 13)| (1 << 12))) { + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.feature = IDE_FEATURE_CFA_ENABLE_POWER_MODE1; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If I need to power up the drive, and I can't + * give up. + */ + debugx("Cannot power up CFA device\n"); + return 1; + } + } + + /* Some extra steps for older drives.. */ + if (info->address_mode != ADDRESS_MODE_PACKET) { + /* Initialize drive parameters + * This is an obsolete command (disappeared as of ATA-6) + * but old drives need it before accessing media. */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(drive_info[3] - 1) + | info->slave; + cmd.sector_count = drive_info[6]; + cmd.command = IDE_CMD_INITIALIZE_DRIVE_PARAMETERS; + debug("Init device params... "); + if (pio_non_data(ctrl, &cmd) < 0) { + debug("failed (ok for newer drives)\n"); + } + else{ + debug("ok\n"); + } + } + + printf("hd%c: %s", + 'a'+drive, + (info->address_mode==ADDRESS_MODE_CHS) ? "CHS" : + (info->address_mode==ADDRESS_MODE_LBA) ? "LBA" : + (info->address_mode==ADDRESS_MODE_LBA48) ? "LBA48" : + (info->address_mode==ADDRESS_MODE_PACKET) ? "ATAPI" : "???"); +#if 0 +// can not pass compiler + if (info->sectors > (10LL*1000*1000*1000/512)) + printf(" %uGB", (unsigned) (info->sectors / (1000*1000*1000/512))); + else if (info->sectors > (10*1000*1000/512)) + printf(" %uMB", (unsigned) (info->sectors / (1000*1000/512))); + else if (info->sectors > 0) + printf(" %uKB", (unsigned) (info->sectors / 2)); +#endif + printf(": %s\n", info->model_number); + return 0; +} + +/* Experimental floating bus detection + * As Eric mentions, we get stuck when the bus has no drive + * and floating high. To avoid this, try some heuristics. + * This is based on a paper on Phoenix website. --ts1 */ +static int ide_bus_floating(struct controller *ctrl) +{ + unsigned long timeout; + unsigned char status; + + /* Test 1: if status reads 0xff, probably no device is present + * on the bus. Repeat this for 20msec. */ + timeout = currticks() + 20 * TICKS_PER_SEC / 18; + status = 0; + do { + /* Take logical OR to avoid chattering */ + status |= inb(IDE_REG_STATUS(ctrl)); + /* If it makes 0xff, it's possible to be floating, + * do test2 to ensure. */ + if (status == 0xff) + goto test2; + /* If BSY bit is not set, it's harmless to continue probing. */ + if ((status & IDE_STATUS_BSY) == 0) + return 0; + } while (currticks() < timeout); + /* Timed out. Logical ORed status didn't make 0xFF. + * We have something there. */ + return 0; + +test2: + /* Test 2: write something to registers, then read back and + * compare. Note that ATA spec inhibits this while BSY is set, + * but for many drives this works. This is a confirmation step anyway. + */ + outb(0xaa, ctrl->cmd_base + 2); + outb(0x55, ctrl->cmd_base + 3); + outb(0xff, ctrl->cmd_base + 4); + if (inb(ctrl->cmd_base+2) == 0xaa + && inb(ctrl->cmd_base+3) == 0x55 + && inb(ctrl->cmd_base+4) == 0xff) { + /* We have some registers there. + * Though this does not mean it is not a NIC or something... */ + return 0; + } + + /* Status port is 0xFF, and other registers are not there. + * Most certainly this bus is floating. */ + debugx("Detected floating bus\n"); + return 1; +} + +static int init_controller(struct controller *ctrl, int drive, unsigned char *buffer) +{ + struct harddisk_info *info; + + /* Put the drives ide channel in a know state and wait + * for the drives to spinup. + * + * In practice IDE disks tend not to respond to commands until + * they have spun up. This makes IDE hard to deal with + * immediately after power up, as the delays can be quite + * long, so we must be very careful here. + * + * There are two pathological cases that must be dealt with: + * + * - The BSY bit not being set while the IDE drives spin up. + * In this cases only a hard coded delay will work. As + * I have not reproduced it, and this is out of spec for + * IDE drives the work around can be enabled by setting + * BSY_SET_DURING_SPINUP to 0. + * + * - The BSY bit floats high when no drives are plugged in. + * This case will not be detected except by timing out but + * we avoid the problems by only probing devices we are + * supposed to boot from. If we don't do the probe we + * will not experience the problem. + * + * So speed wise I am only slow if the BSY bit is not set + * or not reported by the IDE controller during spinup, which + * is quite rare. + * + */ +#if !BSY_SET_DURING_SPINUP + if (await_ide(timeout, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } +#endif + /* ts1: Try some heuristics to avoid waiting for floating bus */ + if (ide_bus_floating(ctrl)) + return -1; + + if (ide_software_reset(ctrl) < 0) { + return -1; + } + + /* Note: I have just done a software reset. It may be + * reasonable to just read the boot time signatures + * off of the drives to see if they are present. + * + * For now I will go with just sending commands to the drives + * and assuming filtering out missing drives by detecting registers + * that won't set and commands that fail to execute properly. + */ + + /* Now initialize the individual drives */ + info = &harddisk_info[drive]; + init_drive_x(info, ctrl, 0, drive, buffer, IDE_CMD_IDENTIFY_DEVICE); + if (!info->drive_exists) + init_drive_x(info, ctrl, 0, drive, buffer, + IDE_CMD_IDENTIFY_PACKET_DEVICE); + if (info->drive_exists && !info->slave_absent) { + drive++; + info++; + init_drive_x(info, ctrl, 1, drive, buffer, + IDE_CMD_IDENTIFY_DEVICE); + if (!info->drive_exists) + init_drive_x(info, ctrl, 1, drive, buffer, + IDE_CMD_IDENTIFY_PACKET_DEVICE); + } + + return 0; +} + +static int +atapi_request_sense(struct harddisk_info *info, uint8_t *asc, uint8_t *ascq) +{ + uint8_t packet[12]; + uint8_t buf[18]; + int i; + + memset(packet, 0, sizeof packet); + packet[0] = 0x03; /* REQUEST SENSE */ + packet[4] = sizeof buf; + if (pio_packet(info, 1, packet, sizeof packet, buf, sizeof buf) != 0) + return -1; + + for (i = 0; i < sizeof buf; i++) + debug("%02x ", buf[i]); + debug("\n"); + + if (asc) + *asc = buf[12]; + if (ascq) + *ascq = buf[13]; + return 0; +} + +static int atapi_detect_medium(struct harddisk_info *info) +{ + uint8_t packet[12]; + uint8_t buf[8]; + uint32_t block_len, sectors; + unsigned long timeout; + uint8_t asc, ascq; + int in_progress; + + memset(packet, 0, sizeof packet); + packet[0] = 0x25; /* READ CAPACITY */ + + /* Retry READ CAPACITY for 5 seconds unless MEDIUM NOT PRESENT + * is reported by the drive. If the drive reports "IN PROGRESS", + * 30 seconds is added. */ + timeout = currticks() + 5*TICKS_PER_SEC; + in_progress = 0; + while (currticks() < timeout) { + if (pio_packet(info, 1, packet, sizeof packet, buf, sizeof buf) + == 0) + goto ok; + + if (atapi_request_sense(info, &asc, &ascq) == 0) { + if (asc == 0x3a) { /* MEDIUM NOT PRESENT */ + debug("Device reports MEDIUM NOT PRESENT\n"); + return -1; + } + + if (asc == 0x04 && ascq == 0x01 && !in_progress) { + /* IN PROGRESS OF BECOMING READY */ + printf("Waiting for drive to detect " + "the medium... "); + /* Allow 30 seconds more */ + timeout = currticks() + 30*TICKS_PER_SEC; + in_progress = 1; + } + } + + mdelay(100); + } + debug("read capacity failed\n"); + return -1; +ok: + + block_len = (uint32_t) buf[4] << 24 + | (uint32_t) buf[5] << 16 + | (uint32_t) buf[6] << 8 + | (uint32_t) buf[7] << 0; + debug("block_len=%u\n", block_len); + if (block_len != IDE_SECTOR_SIZE && block_len != CDROM_SECTOR_SIZE) { + debugx("Unsupported sector size %u\n", block_len); + return -1; + } + info->hw_sector_size = block_len; + + sectors = (uint32_t) buf[0] << 24 + | (uint32_t) buf[1] << 16 + | (uint32_t) buf[2] << 8 + | (uint32_t) buf[3] << 0; + + debug("sectors=%u\n", sectors); + if (info->hw_sector_size == CDROM_SECTOR_SIZE) + sectors <<= 2; /* # of sectors in 512-byte "soft" sector */ + if (sectors != info->sectors) + printf("%dMB medium detected\n", sectors>>(20-9)); + info->sectors = sectors; + return 0; +} + +static int detect_medium(struct harddisk_info *info) +{ + if (info->address_mode == ADDRESS_MODE_PACKET) { + if (atapi_detect_medium(info) != 0) + return -1; + } else { + debug("not implemented for non-ATAPI device\n"); + return -1; + } + return 0; +} + +static int find_ide_controller_compat(struct controller *ctrl, int index) +{ + if (index >= IDE_MAX_CONTROLLERS) + return -1; + ctrl->cmd_base = ide_base[index]; + ctrl->ctrl_base = ide_base[index] + IDE_REG_EXTENDED_OFFSET; + return 0; +} + +#ifdef SUPPORT_PCI +static int find_ide_controller(struct controller *ctrl, int ctrl_index) +{ + int pci_index; + struct pci_device *dev; + unsigned int mask; + uint32_t x; + + /* A PCI IDE controller has two channels (pri, sec) */ + pci_index = ctrl_index >> 1; + + /* Find a IDE storage class device */ + dev = pci_find_device_2(-1, -1, 0x0101, 0x0180, -1, pci_index); + if (!dev) { + debug("PCI IDE #%d not found\n", pci_index); + return -1; + } + + debug("found PCI IDE controller %04x:%04x prog_if=%#x\n", + dev->vendor, dev->dev_id, ((dev->class>>8) & 0xff)); + + /* See how this controller is configured */ + mask = (ctrl_index & 1) ? 4 : 1; + debug("%s channel: ", (ctrl_index & 1) ? "secodary" : "primary"); + if ( (((dev->class>>8) & 0xff) & mask) || ((dev->class>>16)!= 0x0101)) { // 0x0180 and other must use native PCI mode + debug("native PCI mode\n"); + if ((ctrl_index & 1) == 0) { + + /* Primary channel */ + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0,&x); + ctrl->cmd_base = x; + pci_read_config_dword(dev, PCI_BASE_ADDRESS_1,&x); + ctrl->ctrl_base = x; + } else { + /* Secondary channel */ + pci_read_config_dword(dev, PCI_BASE_ADDRESS_2,&x); + ctrl->cmd_base = x; + pci_read_config_dword(dev, PCI_BASE_ADDRESS_3,&x); + ctrl->ctrl_base = x; + } + ctrl->cmd_base &= ~3; + ctrl->ctrl_base &= ~3; + } else { + debug("compatibility mode\n"); + if (find_ide_controller_compat(ctrl, ctrl_index) != 0) + return -1; + } + + debug("cmd_base=%#x ctrl_base=%#x\n", ctrl->cmd_base, ctrl->ctrl_base); +#if 0 + debug("cmd+0=%0#x\n", inb(ctrl->cmd_base+0)); + debug("cmd+1=%0#x\n", inb(ctrl->cmd_base+1)); + debug("cmd+2=%0#x\n", inb(ctrl->cmd_base+2)); + debug("cmd+3=%0#x\n", inb(ctrl->cmd_base+3)); + debug("cmd+4=%0#x\n", inb(ctrl->cmd_base+4)); + debug("cmd+5=%0#x\n", inb(ctrl->cmd_base+5)); + debug("cmd+6=%0#x\n", inb(ctrl->cmd_base+6)); + debug("cmd+7=%0#x\n", inb(ctrl->cmd_base+7)); + debug("ctrl+0=%0#x\n", inb(ctrl->ctrl_base+0)); + debug("ctrl+1=%0#x\n", inb(ctrl->ctrl_base+1)); + debug("ctrl+2=%0#x\n", inb(ctrl->ctrl_base+2)); + debug("ctrl+3=%0#x\n", inb(ctrl->ctrl_base+3)); +#endif + return 0; +} +#else /* !SUPPORT_PCI */ +# define find_ide_controller find_ide_controller_compat +#endif + +int ide_probe(int drive) +{ + struct controller *ctrl; + int ctrl_index; + struct harddisk_info *info; + + if (drive >= IDE_MAX_DRIVES) { + debugx("Unsupported drive number\n"); + return -1; + } + + /* A controller has two drives (master, slave) */ + ctrl_index = drive >> 1; + + ctrl = &controllers[ctrl_index]; + if (ctrl->cmd_base == 0) { + if (find_ide_controller(ctrl, ctrl_index) != 0) { + debugx("IDE channel %d not found\n", ctrl_index); + return -1; + } + if (init_controller(ctrl, drive & ~1, ide_buffer) != 0) { + printf("No drive detected on IDE channel %d\n", + ctrl_index); + return -1; + } + } + info = &harddisk_info[drive]; + if (!info->drive_exists) { + printf("Drive %d does not exist\n", drive); + return -1; + } + + if (info->removable) { + if (detect_medium(info) != 0) { + printf("Media detection failed\n"); + return -1; + } + } + + return 0; +} + +/* vim:set sts=8 sw=8: */ diff --git a/src/filo/fs/blockdev.c b/src/filo/fs/blockdev.c new file mode 100644 index 000000000..f159ff2f5 --- /dev/null +++ b/src/filo/fs/blockdev.c @@ -0,0 +1,383 @@ +#include <etherboot.h> + +#include <lib.h> +#include <fs.h> + +#define DEBUG_THIS DEBUG_BLOCKDEV +#include <debug.h> + +#define NUM_CACHE 64 +static unsigned char buf_cache[NUM_CACHE][512]; +static unsigned long cache_sect[NUM_CACHE]; + +static char dev_name[256]; + +int dev_type = -1; +int dev_drive = -1; +unsigned long part_start; +unsigned long part_length; +int using_devsize; + +static inline int has_pc_part_magic(unsigned char *sect) +{ + return sect[510]==0x55 && sect[511]==0xAA; +} + +static inline int is_pc_extended_part(unsigned char type) +{ + return type==5 || type==0xf || type==0x85; +} + +/* IBM-PC/MS-DOS style partitioning scheme */ +static int open_pc_partition(int part, unsigned long *start_p, + unsigned long *length_p) +{ + /* Layout of PC partition table */ + struct pc_partition { + unsigned char boot; + unsigned char head; + unsigned char sector; + unsigned char cyl; + unsigned char type; + unsigned char e_head; + unsigned char e_sector; + unsigned char e_cyl; + unsigned char start_sect[4]; /* unaligned little endian */ + unsigned char nr_sects[4]; /* ditto */ + } *p; + unsigned char buf[512]; + + /* PC partition probe */ + if (!devread(0, 0, sizeof(buf), buf)) { + debug("device read failed\n"); + return 0; + } + if (!has_pc_part_magic(buf)) { + debug("pc partition magic number not found\n"); + //debug_hexdump(buf, 512); + return PARTITION_UNKNOWN; + } + p = (struct pc_partition *) (buf + 0x1be); + if (part < 4) { + /* Primary partition */ + p += part; + if (p->type==0 || is_pc_extended_part(p->type)) { + printf("Partition %d does not exist\n", part+1); + return 0; + } + *start_p = get_le32(p->start_sect); + *length_p = get_le32(p->nr_sects); + return 1; + } else { + /* Extended partition */ + int i; + int cur_part; + unsigned long ext_start, cur_table; + /* Search for the extended partition + * which contains logical partitions */ + for (i = 0; i < 4; i++) { + if (is_pc_extended_part(p[i].type)) + break; + } + if (i >= 4) { + printf("Extended partition not found\n"); + return 0; + } + debug("Extended partition at %d\n", i+1); + /* Visit each logical partition labels */ + ext_start = get_le32(p[i].start_sect); + cur_table = ext_start; + cur_part = 4; + for (;;) { + debug("cur_part=%d at %lu\n", cur_part, cur_table); + if (!devread(cur_table, 0, sizeof(buf), buf)) + return 0; + if (!has_pc_part_magic(buf)) { + debug("no magic\n"); + break; + } + + p = (struct pc_partition *) (buf + 0x1be); + /* First entry is the logical partition */ + if (cur_part == part) { + if (p->type==0) { + printf("Partition %d is empty\n", part+1); + return 0; + } + *start_p = cur_table + get_le32(p->start_sect); + *length_p = get_le32(p->nr_sects); + return 1; + } + /* Second entry is link to next partition */ + if (!is_pc_extended_part(p[1].type)) { + debug("no link\n"); + break; + } + cur_table = ext_start + get_le32(p[1].start_sect); + + cur_part++; + } + printf("Logical partition %d not exist\n", part+1); + return 0; + } +} + +static void flush_cache(void) +{ + int i; + for (i = 0; i < NUM_CACHE; i++) + cache_sect[i] = (unsigned long) -1; +} + +static int parse_device_name(const char *name, int *type, int *drive, + int *part, uint64_t *offset, uint64_t *length) +{ + *offset = *length = 0; + + if (memcmp(name, "hd", 2) == 0) { + *type = DISK_IDE; + name += 2; + if (*name < 'a' || *name > 'z') { + printf("Invalid drive\n"); + return 0; + } + *drive = *name - 'a'; + name++; + } else if (memcmp(name, "mem", 3) == 0) { + *type = DISK_MEM; + name += 3; + *drive = 0; + } else if (memcmp(name, "ud", 2) == 0) { + *type = DISK_USB; + name += 2; + if (*name < 'a' || *name > 'z') { + printf("Invalid drive\n"); + return 0; + } + *drive = *name - 'a'; + name++; + } else { + printf("Unknown device type\n"); + return 0; + } + + *part = (int) simple_strtoull(name, (char **)&name, 0); + + if (*name == '@') { + name++; + *offset = strtoull_with_suffix(name, (char **)&name, 0); + if (*name == ',') + *length = strtoull_with_suffix(name+1, (char **)&name, 0); +// debug("offset=%#Lx length=%#Lx\n", *offset, *length); + } + + if (*name != '\0') { + printf("Can't parse device name\n"); + return 0; + } + + return 1; +} + +int devopen(const char *name, int *reopen) +{ + int type, drive, part; + uint64_t offset, length; + uint32_t disk_size = 0; + + /* Don't re-open the device that's already open */ + if (strcmp(name, dev_name) == 0) { + debug("already open\n"); + *reopen = 1; + return 1; + } + *reopen = 0; + + if (!parse_device_name(name, &type, &drive, &part, &offset, &length)) { + debug("failed to parse device name: %s\n", name); + return 0; + } + + /* Do simple sanity check first */ + if (offset & 0x1ff) { + printf("Device offset must be multiple of 512\n"); + return 0; + } + if (length & 0x1ff) { + debugx("WARNING: length is rounded up to multiple of 512\n"); + length = (length + 0x1ff) & ~0x1ff; + } + + switch (type) { +#ifdef IDE_DISK + case DISK_IDE: + if (ide_probe(drive) != 0) { + debug("failed to open ide\n"); + return 0; + } + disk_size = (uint32_t) -1; /* FIXME */ + break; +#endif + case DISK_MEM: + disk_size = 1 << (32 - 9); /* 4GB/512-byte */ + break; +#ifdef USB_DISK + case DISK_USB: + if (usb_probe(drive) != 0) { + debug("failed to open usb\n"); + return 0; + } + disk_size = (uint32_t) -1; /* FIXME */ + break; +#endif + default: + printf("Unknown device type %d\n", type); + return 0; + } + + if (dev_type != type || dev_drive != drive) + flush_cache(); + + /* start with whole disk */ + dev_type = type; + dev_drive = drive; + part_start = 0; + part_length = disk_size; + using_devsize = 1; + + if (part != 0) { + /* partition is specified */ + int ret; + ret = open_pc_partition(part - 1, &part_start, &part_length); + if (ret == PARTITION_UNKNOWN) { + ret = open_eltorito_image(part - 1, &part_start, &part_length); + if (ret == PARTITION_UNKNOWN) { + printf("Unrecognized partitioning scheme\n"); + return 0; + } + } + if (ret == 0) { + debug("can't open partition %d\n", part); + return 0; + } + + debug("Partition %d start %lu length %lu\n", part, + part_start, part_length); + } + + if (offset) { + if (offset >= (uint64_t) part_length << 9) { + printf("Device offset is too high\n"); + return 0; + } + part_start += offset >> 9; + part_length -= offset >> 9; + debug("after offset: start %lu, length %lu\n", part_start, part_length); + } + + if (length) { + if (length > (uint64_t) part_length << 9) { + printf("Specified length exceeds the size of device\n"); + return 0; + } + part_length = length >> 9; + debug("after length: length %lu\n", part_length); + using_devsize = 0; + } + + strncpy(dev_name, name, sizeof(dev_name)-1); + + return 1; +} + +/* Read a sector from opened device with simple/stupid buffer cache */ +static void *read_sector(unsigned long sector) +{ + unsigned int hash; + void *buf; + int i; + + /* If reading memory, just return the memory as the buffer */ + if (dev_type == DISK_MEM) { + unsigned long phys = sector << 9; + //debug("mem: %#lx\n", phys); + return phys_to_virt(phys); + } + + /* Search in the cache */ + hash = sector % NUM_CACHE; + buf = buf_cache[hash]; + if (cache_sect[hash] != sector) { + cache_sect[hash] = (unsigned long) -1; + switch (dev_type) { +#ifdef IDE_DISK + case DISK_IDE: + if (ide_read(dev_drive, sector, buf) != 0) + goto readerr; + break; +#endif +#ifdef USB_DISK + case DISK_USB: + if (usb_read(dev_drive, sector, buf) != 0) + goto readerr; + break; +#endif + default: + printf("read_sector: device not open\n"); + return 0; + } + cache_sect[hash] = sector; + } +#if 0 + printf("in read_sector:\n"); + for(i=0;i<128;i++) { + if((i%4)==0) printf("\n %08x:",i*4); + printf(" %08x ",(uint32_t)*((uint32_t *)buf+i)); + } +#endif + + return buf; + +readerr: + printf("Disk read error dev_type=%d drive=%d sector=%x\n", + dev_type, dev_drive, sector); + dev_name[0] = '\0'; /* force re-open the device next time */ + return 0; +} + +int devread(unsigned long sector, unsigned long byte_offset, + unsigned long byte_len, void *buf) +{ + char *sector_buffer; + char *dest = buf; + unsigned long len; + int i; + + sector += byte_offset >> 9; + byte_offset &= 0x1ff; + + if (sector + ((byte_len + 0x1ff) >> 9) > part_length) { + printf("Attempt to read out of device/partition\n"); + debug("sector=%x part_length=%x byte_len=%x\n", + sector, part_length, byte_len); + return 0; + } + + while (byte_len > 0) { + sector_buffer = read_sector(part_start + sector); + if (!sector_buffer) { + debug("read sector failed\n"); + return 0; + } + len = 512 - byte_offset; + if (len > byte_len) + len = byte_len; + memcpy(dest, sector_buffer + byte_offset, len); + sector++; + byte_offset = 0; + byte_len -= len; + dest += len; + } + + return 1; +} diff --git a/src/filo/fs/eltorito.c b/src/filo/fs/eltorito.c new file mode 100644 index 000000000..30b663d0d --- /dev/null +++ b/src/filo/fs/eltorito.c @@ -0,0 +1,147 @@ + +#include <etherboot.h> +#include <fs.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_ELTORITO +#include <debug.h> + +#define ELTORITO_PLATFORM_X86 0 +#define ELTORITO_PLATFORM_PPC 1 +#define ELTORITO_PLATFORM_MAC 2 +#include <bits/eltorito.h> + +#ifndef ELTORITO_PLATFORM +#error "ELTORITO_PLATFORM is not defined for this arch" +#endif + +/* El Torito boot record at sector 0x11 of bootable CD */ +struct boot_record { + uint8_t ind; + uint8_t iso_id[5]; + uint8_t version; + uint8_t boot_id[32]; + uint8_t reserved[32]; + uint8_t catalog_offset[4]; +}; + +/* First entry of the catalog */ +struct validation_entry { + uint8_t header_id; + uint8_t platform; + uint8_t reserved[2]; + uint8_t id[24]; + uint8_t checksum[2]; + uint8_t key55; + uint8_t keyAA; +}; + +/* Initial/Default catalog entry */ +struct default_entry { + uint8_t boot_id; + uint8_t media_type; +#define MEDIA_MASK 0x0f +#define MEDIA_NOEMU 0 +#define MEDIA_1200_FD 1 +#define MEDIA_1440_FD 2 +#define MEDIA_2880_FD 3 +#define MEDIA_HD 4 + uint8_t load_segment[2]; + uint8_t system_type; + uint8_t reserved; + uint8_t sector_count[2]; + uint8_t start_sector[4]; + uint8_t reserved_too[20]; +}; + +/* Find El-Torito boot disk image */ +int open_eltorito_image(int part, unsigned long *offset_p, + unsigned long *length_p) +{ + struct boot_record boot_record; + uint32_t cat_offset; + uint8_t catalog[2048]; + struct validation_entry *ve; + int i, sum; + struct default_entry *de; + + /* We always use 512-byte "soft sector", but + * El-Torito uses 2048-byte CD-ROM sector */ + + /* Boot Record is at sector 0x11 */ + if (!devread(0x11<<2, 0, sizeof boot_record, &boot_record)) + return 0; + + if (boot_record.ind != 0 + || memcmp(boot_record.iso_id, "CD001", 5) != 0 + || memcmp(boot_record.boot_id, "EL TORITO SPECIFICATION", 23) + != 0) { + debug("No El-Torito signature\n"); + return PARTITION_UNKNOWN; + } + + if (part != 0) { + printf("El-Torito entries other than Initial/Default is not supported\n"); + return 0; + } + + cat_offset = get_le32(boot_record.catalog_offset); + debug("El-Torito boot catalog at sector %u\n", cat_offset); + if (!devread(cat_offset<<2, 0, 2048, catalog)) + return 0; + + /* Validate the catalog */ + ve = (void *) catalog; + //debug_hexdump(ve, sizeof *ve); + if (ve->header_id != 1 || ve->key55 != 0x55 || ve->keyAA != 0xAA) { + printf("Invalid El Torito boot catalog\n"); + return 0; + } + /* All words must sum up to zero */ + sum = 0; + for (i = 0; i < sizeof(*ve); i += 2) + sum += get_le16(&catalog[i]); + sum &= 0xffff; + if (sum != 0) { + printf("El Torito boot catalog verify failed\n"); + return 0; + } + debug("id='%.*s'\n", sizeof ve->id, ve->id); + + /* Platform check is warning only, because we won't directly execute + * the image. Just mounting it should be safe. */ + if (ve->platform != ELTORITO_PLATFORM){ + debugx("WARNING: Boot disk for different platform: %d\n", ve->platform); + } + + /* Just support initial/default entry for now */ + de = (void *) (ve + 1); + if (de->boot_id != 0x88) { + debugx("WARNING: Default boot entry is not bootable\n"); + } + + switch (de->media_type & MEDIA_MASK) { + case MEDIA_NOEMU: + printf("Disc doesn't use boot disk emulation\n"); + return 0; + case MEDIA_1200_FD: + *length_p = 1200*1024/512; + break; + case MEDIA_1440_FD: + *length_p = 1440*1024/512; + break; + case MEDIA_2880_FD: + *length_p = 2880*1024/512; + break; + case MEDIA_HD: + /* FIXME: read partition table and return first partition. + * Spec states emulation HD has only one partition and it must + * be the first partition */ + printf("Disc uses hard disk emulation - not supported\n"); + return 0; + } + *offset_p = get_le32(de->start_sector) << 2; + debug("offset=%#lx length=%#lx\n", *offset_p, *length_p); + + return 1; +} diff --git a/src/filo/fs/fat.h b/src/filo/fs/fat.h new file mode 100644 index 000000000..7fed6bacb --- /dev/null +++ b/src/filo/fs/fat.h @@ -0,0 +1,100 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/* + * Defines for the FAT BIOS Parameter Block (embedded in the first block + * of the partition. + */ + +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +/* Note that some shorts are not aligned, and must therefore + * be declared as array of two bytes. + */ +struct fat_bpb { + __s8 ignored[3]; /* Boot strap short or near jump */ + __s8 system_id[8]; /* Name - can be used to special case + partition manager volumes */ + __u8 bytes_per_sect[2]; /* bytes per logical sector */ + __u8 sects_per_clust;/* sectors/cluster */ + __u8 reserved_sects[2]; /* reserved sectors */ + __u8 num_fats; /* number of FATs */ + __u8 dir_entries[2]; /* root directory entries */ + __u8 short_sectors[2]; /* number of sectors */ + __u8 media; /* media code (unused) */ + __u16 fat_length; /* sectors/FAT */ + __u16 secs_track; /* sectors per track */ + __u16 heads; /* number of heads */ + __u32 hidden; /* hidden sectors (unused) */ + __u32 long_sectors; /* number of sectors (if short_sectors == 0) */ + + /* The following fields are only used by FAT32 */ + __u32 fat32_length; /* sectors/FAT */ + __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ + __u8 version[2]; /* major, minor filesystem version */ + __u32 root_cluster; /* first cluster in root directory */ + __u16 info_sector; /* filesystem info sector */ + __u16 backup_boot; /* backup boot sector */ + __u16 reserved2[6]; /* Unused */ +}; + +#define FAT_CVT_U16(bytarr) (* (__u16*)(bytarr)) + +/* + * Defines how to differentiate a 12-bit and 16-bit FAT. + */ + +#define FAT_MAX_12BIT_CLUST 4087 /* 4085 + 2 */ + +/* + * Defines for the file "attribute" byte + */ + +#define FAT_ATTRIB_OK_MASK 0x37 +#define FAT_ATTRIB_NOT_OK_MASK 0xC8 +#define FAT_ATTRIB_DIR 0x10 +#define FAT_ATTRIB_LONGNAME 0x0F + +/* + * Defines for FAT directory entries + */ + +#define FAT_DIRENTRY_LENGTH 32 + +#define FAT_DIRENTRY_ATTRIB(entry) \ + (*((unsigned char *) (entry+11))) +#define FAT_DIRENTRY_VALID(entry) \ + ( ((*((unsigned char *) entry)) != 0) \ + && ((*((unsigned char *) entry)) != 0xE5) \ + && !(FAT_DIRENTRY_ATTRIB(entry) & FAT_ATTRIB_NOT_OK_MASK) ) +#define FAT_DIRENTRY_FIRST_CLUSTER(entry) \ + ((*((unsigned short *) (entry+26)))+(*((unsigned short *) (entry+20)) << 16)) +#define FAT_DIRENTRY_FILELENGTH(entry) \ + (*((unsigned long *) (entry+28))) + +#define FAT_LONGDIR_ID(entry) \ + (*((unsigned char *) (entry))) +#define FAT_LONGDIR_ALIASCHECKSUM(entry) \ + (*((unsigned char *) (entry+13))) diff --git a/src/filo/fs/filesys.h b/src/filo/fs/filesys.h new file mode 100644 index 000000000..f81bebe78 --- /dev/null +++ b/src/filo/fs/filesys.h @@ -0,0 +1,233 @@ +/* GRUB compatibility header */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2003 Free Software Foundation, Inc. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +//#include <lib.h> +#include <etherboot.h> + +#include <fs.h> + +/* This disables some portion of code */ +#define STAGE1_5 1 + +static inline int +substring (const char *s1, const char *s2) +{ + while (*s1 == *s2) + { + /* The strings match exactly. */ + if (! *(s1++)) + return 0; + s2 ++; + } + + /* S1 is a substring of S2. */ + if (*s1 == 0) + return -1; + + /* S1 isn't a substring. */ + return 1; +} + +#define grub_memmove memmove +#define grub_strcmp strcmp + +#define MAXINT 0x7fffffff + +/* This is only used by fsys_* to determine if it's hard disk. If it is, + * they try to guess filesystem type by partition type. I guess it is + * not necessory, so hardcoded to 0 (first floppy) --ts1 */ +#define current_drive 0 + +/* Ditto */ +#define current_slice 0 + +extern unsigned long part_start; +extern unsigned long part_length; +extern int filepos; +extern int filemax; +extern int fsmax; + +/* Error codes (descriptions are in common.c) */ +typedef enum +{ + ERR_NONE = 0, + ERR_BAD_FILENAME, + ERR_BAD_FILETYPE, + ERR_BAD_GZIP_DATA, + ERR_BAD_GZIP_HEADER, + ERR_BAD_PART_TABLE, + ERR_BAD_VERSION, + ERR_BELOW_1MB, + ERR_BOOT_COMMAND, + ERR_BOOT_FAILURE, + ERR_BOOT_FEATURES, + ERR_DEV_FORMAT, + ERR_DEV_VALUES, + ERR_EXEC_FORMAT, + ERR_FILELENGTH, + ERR_FILE_NOT_FOUND, + ERR_FSYS_CORRUPT, + ERR_FSYS_MOUNT, + ERR_GEOM, + ERR_NEED_LX_KERNEL, + ERR_NEED_MB_KERNEL, + ERR_NO_DISK, + ERR_NO_PART, + ERR_NUMBER_PARSING, + ERR_OUTSIDE_PART, + ERR_READ, + ERR_SYMLINK_LOOP, + ERR_UNRECOGNIZED, + ERR_WONT_FIT, + ERR_WRITE, + ERR_BAD_ARGUMENT, + ERR_UNALIGNED, + ERR_PRIVILEGED, + ERR_DEV_NEED_INIT, + ERR_NO_DISK_SPACE, + ERR_NUMBER_OVERFLOW, + + MAX_ERR_NUM +} grub_error_t; + +extern grub_error_t errnum; + +#define grub_open file_open +#define grub_read file_read +#define grub_seek file_seek +#define grub_close file_close + +/* instrumentation variables */ +/* (Not used in FILO) */ +extern void (*disk_read_hook) (int, int, int); +extern void (*disk_read_func) (int, int, int); + +#define FSYS_BUFLEN 0x8000 +extern char FSYS_BUF[FSYS_BUFLEN]; + +#define print_possibilities 0 + +#define SECTOR_SIZE 512 +#define SECTOR_BITS 9 + +#ifdef FSYS_FAT +int fat_mount (void); +int fat_read (char *buf, int len); +int fat_dir (char *dirname); +#endif + +#ifdef FSYS_EXT2FS +int ext2fs_mount (void); +int ext2fs_read (char *buf, int len); +int ext2fs_dir (char *dirname); +#endif + +#ifdef FSYS_MINIX +int minix_mount (void); +int minix_read (char *buf, int len); +int minix_dir (char *dirname); +#endif + +#ifdef FSYS_REISERFS +int reiserfs_mount (void); +int reiserfs_read (char *buf, int len); +int reiserfs_dir (char *dirname); +int reiserfs_embed (int *start_sector, int needed_sectors); +#endif + +#ifdef FSYS_JFS +int jfs_mount (void); +int jfs_read (char *buf, int len); +int jfs_dir (char *dirname); +int jfs_embed (int *start_sector, int needed_sectors); +#endif + +#ifdef FSYS_XFS +int xfs_mount (void); +int xfs_read (char *buf, int len); +int xfs_dir (char *dirname); +#endif + +#ifdef FSYS_ISO9660 +int iso9660_mount (void); +int iso9660_read (char *buf, int len); +int iso9660_dir (char *dirname); +#endif + +/* This is not a flag actually, but used as if it were a flag. */ +#define PC_SLICE_TYPE_HIDDEN_FLAG 0x10 + +#define PC_SLICE_TYPE_NONE 0 +#define PC_SLICE_TYPE_FAT12 1 +#define PC_SLICE_TYPE_FAT16_LT32M 4 +#define PC_SLICE_TYPE_EXTENDED 5 +#define PC_SLICE_TYPE_FAT16_GT32M 6 +#define PC_SLICE_TYPE_FAT32 0xb +#define PC_SLICE_TYPE_FAT32_LBA 0xc +#define PC_SLICE_TYPE_FAT16_LBA 0xe +#define PC_SLICE_TYPE_WIN95_EXTENDED 0xf +#define PC_SLICE_TYPE_EZD 0x55 +#define PC_SLICE_TYPE_MINIX 0x80 +#define PC_SLICE_TYPE_LINUX_MINIX 0x81 +#define PC_SLICE_TYPE_EXT2FS 0x83 +#define PC_SLICE_TYPE_LINUX_EXTENDED 0x85 +#define PC_SLICE_TYPE_VSTAFS 0x9e +#define PC_SLICE_TYPE_DELL_UTIL 0xde +#define PC_SLICE_TYPE_LINUX_RAID 0xfd + +/* For convinience. */ +/* Check if TYPE is a FAT partition type. Clear the hidden flag before + the check, to allow the user to mount a hidden partition in GRUB. */ +#define IS_PC_SLICE_TYPE_FAT(type) \ + ({ int _type = (type) & ~PC_SLICE_TYPE_HIDDEN_FLAG; \ + _type == PC_SLICE_TYPE_FAT12 \ + || _type == PC_SLICE_TYPE_FAT16_LT32M \ + || _type == PC_SLICE_TYPE_FAT16_GT32M \ + || _type == PC_SLICE_TYPE_FAT16_LBA \ + || _type == PC_SLICE_TYPE_FAT32 \ + || _type == PC_SLICE_TYPE_FAT32_LBA \ + || _type == PC_SLICE_TYPE_DELL_UTIL; }) + +#define IS_PC_SLICE_TYPE_MINIX(type) \ + (((type) == PC_SLICE_TYPE_MINIX) \ + || ((type) == PC_SLICE_TYPE_LINUX_MINIX)) + +#define IS_PC_SLICE_TYPE_BSD_WITH_FS(type,fs) 0 + +/* possible values for the *BSD-style partition type */ +#define FS_UNUSED 0 /* unused */ +#define FS_SWAP 1 /* swap */ +#define FS_V6 2 /* Sixth Edition */ +#define FS_V7 3 /* Seventh Edition */ +#define FS_SYSV 4 /* System V */ +#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ +#define FS_V8 6 /* Eighth Edition, 4K blocks */ +#define FS_BSDFFS 7 /* 4.2BSD fast file system */ +#define FS_MSDOS 8 /* MSDOS file system */ +#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */ +#define FS_OTHER 10 /* in use, but unknown/unsupported */ +#define FS_HPFS 11 /* OS/2 high-performance file system */ +#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */ +#define FS_BOOT 13 /* partition contains bootstrap */ +#define FS_ADOS 14 /* AmigaDOS fast file system */ +#define FS_HFS 15 /* Macintosh HFS */ +#define FS_FILECORE 16 /* Acorn Filecore Filing System */ +#define FS_EXT2FS 17 /* Linux Extended 2 file system */ diff --git a/src/filo/fs/fsys_ext2fs.c b/src/filo/fs/fsys_ext2fs.c new file mode 100644 index 000000000..62c6e80fa --- /dev/null +++ b/src/filo/fs/fsys_ext2fs.c @@ -0,0 +1,779 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999, 2001 Free Software Foundation, Inc. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef FSYS_EXT2FS + +#include "shared.h" +#include "filesys.h" +#include <lib.h> +#include "string.h" + +static int mapblock1, mapblock2; + +/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ +#define DEV_BSIZE 512 + +/* include/linux/fs.h */ +#define BLOCK_SIZE 1024 /* initial block size for superblock read */ +/* made up, defaults to 1 but can be passed via mount_opts */ +#define WHICH_SUPER 1 +/* kind of from fs/ext2/super.c */ +#define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE) /* = 2 */ + +/* include/asm-i386/types.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +/* + * Constants relative to the data blocks, from ext2_fs.h + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* include/linux/ext2_fs.h */ +struct ext2_super_block + { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __s32 s_log_frag_size; /* Fragment size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_frags_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_pad; + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + __u32 s_reserved[235]; /* Padding to the end of the block */ + }; + +struct ext2_group_desc + { + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_pad; + __u32 bg_reserved[3]; + }; + +struct ext2_inode + { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Owner Uid */ + __u32 i_size; /* 4: Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* 12: Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* 20: Deletion Time */ + __u16 i_gid; /* Group Id */ + __u16 i_links_count; /* 24: Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* 32: File flags */ + union + { + struct + { + __u32 l_i_reserved1; + } + linux1; + struct + { + __u32 h_i_translator; + } + hurd1; + struct + { + __u32 m_i_reserved1; + } + masix1; + } + osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS]; /* 40: Pointers to blocks */ + __u32 i_version; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ + union + { + struct + { + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u32 l_i_reserved2[2]; + } + linux2; + struct + { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } + hurd2; + struct + { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } + masix2; + } + osd2; /* OS dependent 2 */ + }; + +/* linux/limits.h */ +#define NAME_MAX 255 /* # chars in a file name */ + +/* linux/posix_type.h */ +typedef long linux_off_t; + +/* linux/ext2fs.h */ +#define EXT2_NAME_LEN 255 +struct ext2_dir_entry + { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ + }; + +/* linux/ext2fs.h */ +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + + +/* ext2/super.c */ +#define log2(n) ffz(~(n)) + +#define EXT2_SUPER_MAGIC 0xEF53 /* include/linux/ext2_fs.h */ +#define EXT2_ROOT_INO 2 /* include/linux/ext2_fs.h */ +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* made up, these are pointers into FSYS_BUF */ +/* read once, always stays there: */ +#define SUPERBLOCK \ + ((struct ext2_super_block *)(FSYS_BUF)) +#define GROUP_DESC \ + ((struct ext2_group_desc *) \ + ((int)SUPERBLOCK + sizeof(struct ext2_super_block))) +#define INODE \ + ((struct ext2_inode *)((int)GROUP_DESC + EXT2_BLOCK_SIZE(SUPERBLOCK))) +#define DATABLOCK1 \ + ((int)((int)INODE + sizeof(struct ext2_inode))) +#define DATABLOCK2 \ + ((int)((int)DATABLOCK1 + EXT2_BLOCK_SIZE(SUPERBLOCK))) + +/* linux/ext2_fs.h */ +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) +#define EXT2_ADDR_PER_BLOCK_BITS(s) (log2(EXT2_ADDR_PER_BLOCK(s))) + +/* linux/ext2_fs.h */ +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +/* kind of from ext2/super.c */ +#define EXT2_BLOCK_SIZE(s) (1 << EXT2_BLOCK_SIZE_BITS(s)) +/* linux/ext2fs.h */ +#define EXT2_DESC_PER_BLOCK(s) \ + (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) +/* linux/stat.h */ +#define S_IFMT 00170000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFDIR 0040000 +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + +/* include/asm-i386/bitops.h */ +/* + * ffz = Find First Zero in word. Undefined if no zero exists, + * so code should check against ~0UL first.. + */ +static __inline__ unsigned long +ffz (unsigned long word) +{ + __asm__ ("bsfl %1,%0" +: "=r" (word) +: "r" (~word)); + return word; +} + +/* check filesystem types and read superblock into memory buffer */ +int +ext2fs_mount (void) +{ + int retval = 1; + + if ((((current_drive & 0x80) || (current_slice != 0)) + && (current_slice != PC_SLICE_TYPE_EXT2FS) + && (current_slice != PC_SLICE_TYPE_LINUX_RAID) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_EXT2FS)) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER))) + || part_length < (SBLOCK + (sizeof (struct ext2_super_block) / DEV_BSIZE)) + || !devread (SBLOCK, 0, sizeof (struct ext2_super_block), + (char *) SUPERBLOCK) + || SUPERBLOCK->s_magic != EXT2_SUPER_MAGIC) + retval = 0; + + return retval; +} + +/* Takes a file system block number and reads it into BUFFER. */ +static int +ext2_rdfsb (int fsblock, int buffer) +{ +#ifdef E2DEBUG + printf ("fsblock %d buffer %d\n", fsblock, buffer); +#endif /* E2DEBUG */ + return devread (fsblock * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), 0, + EXT2_BLOCK_SIZE (SUPERBLOCK), (char *) buffer); +} + +/* from + ext2/inode.c:ext2_bmap() +*/ +/* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into + a physical block (the location in the file system) via an inode. */ +static int +ext2fs_block_map (int logical_block) +{ + +#ifdef E2DEBUG + unsigned char *i; + for (i = (unsigned char *) INODE; + i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); + i++) + { + printf ("%c", "0123456789abcdef"[*i >> 4]); + printf ("%c", "0123456789abcdef"[*i % 16]); + if (!((i + 1 - (unsigned char *) INODE) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } + printf ("logical block %d\n", logical_block); +#endif /* E2DEBUG */ + + /* if it is directly pointed to by the inode, return that physical addr */ + if (logical_block < EXT2_NDIR_BLOCKS) + { +#ifdef E2DEBUG + printf ("returning %d\n", (unsigned char *) (INODE->i_block[logical_block])); + printf ("returning %d\n", INODE->i_block[logical_block]); +#endif /* E2DEBUG */ + return INODE->i_block[logical_block]; + } + /* else */ + logical_block -= EXT2_NDIR_BLOCKS; + /* try the indirect block */ + if (logical_block < EXT2_ADDR_PER_BLOCK (SUPERBLOCK)) + { + if (mapblock1 != 1 + && !ext2_rdfsb (INODE->i_block[EXT2_IND_BLOCK], DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 1; + return ((__u32 *) DATABLOCK1)[logical_block]; + } + /* else */ + logical_block -= EXT2_ADDR_PER_BLOCK (SUPERBLOCK); + /* now try the double indirect block */ + if (logical_block < (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2))) + { + int bnum; + if (mapblock1 != 2 + && !ext2_rdfsb (INODE->i_block[EXT2_DIND_BLOCK], DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 2; + if ((bnum = (((__u32 *) DATABLOCK1) + [logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)])) + != mapblock2 + && !ext2_rdfsb (bnum, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock2 = bnum; + return ((__u32 *) DATABLOCK2) + [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; + } + /* else */ + mapblock2 = -1; + logical_block -= (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2)); + if (mapblock1 != 3 + && !ext2_rdfsb (INODE->i_block[EXT2_TIND_BLOCK], DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 3; + if (!ext2_rdfsb (((__u32 *) DATABLOCK1) + [logical_block >> (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) + * 2)], + DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + if (!ext2_rdfsb (((__u32 *) DATABLOCK2) + [(logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)) + & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)], + DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + return ((__u32 *) DATABLOCK2) + [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; +} + +/* preconditions: all preconds of ext2fs_block_map */ +int +ext2fs_read (char *buf, int len) +{ + int logical_block; + int offset; + int map; + int ret = 0; + int size = 0; + +#ifdef E2DEBUG + static char hexdigit[] = "0123456789abcdef"; + unsigned char *i; + for (i = (unsigned char *) INODE; + i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); + i++) + { + printf ("%c", hexdigit[*i >> 4]); + printf ("%c", hexdigit[*i % 16]); + if (!((i + 1 - (unsigned char *) INODE) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } +#endif /* E2DEBUG */ + while (len > 0) + { + /* find the (logical) block component of our location */ + logical_block = filepos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); + offset = filepos & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); + map = ext2fs_block_map (logical_block); +#ifdef E2DEBUG + printf ("map=%d\n", map); +#endif /* E2DEBUG */ + if (map < 0) + break; + + size = EXT2_BLOCK_SIZE (SUPERBLOCK); + size -= offset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + devread (map * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), + offset, size, buf); + + disk_read_func = NULL; + + buf += size; + len -= size; + filepos += size; + ret += size; + } + + if (errnum) + ret = 0; + + return ret; +} + + +/* Based on: + def_blk_fops points to + blkdev_open, which calls (I think): + sys_open() + do_open() + open_namei() + dir_namei() which accesses current->fs->root + fs->root was set during original mount: + (something)... which calls (I think): + ext2_read_super() + iget() + __iget() + read_inode() + ext2_read_inode() + uses desc_per_block_bits, which is set in ext2_read_super() + also uses group descriptors loaded during ext2_read_super() + lookup() + ext2_lookup() + ext2_find_entry() + ext2_getblk() + +*/ + +/* preconditions: ext2fs_mount already executed, therefore supblk in buffer + * known as SUPERBLOCK + * returns: 0 if error, nonzero iff we were able to find the file successfully + * postconditions: on a nonzero return, buffer known as INODE contains the + * inode of the file we were trying to look up + * side effects: messes up GROUP_DESC buffer area + */ +int +ext2fs_dir (char *dirname) +{ + int current_ino = EXT2_ROOT_INO; /* start at the root */ + int updir_ino = current_ino; /* the parent of the current directory */ + int group_id; /* which group the inode is in */ + int group_desc; /* fs pointer to that group */ + int desc; /* index within that group */ + int ino_blk; /* fs pointer of the inode's information */ + int str_chk = 0; /* used to hold the results of a string compare */ + struct ext2_group_desc *gdp; + struct ext2_inode *raw_inode; /* inode info corresponding to current_ino */ + + char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ + int link_count = 0; + + char *rest; + char ch; /* temp char holder */ + + int off; /* offset within block of directory entry (off mod blocksize) */ + int loc; /* location within a directory */ + int blk; /* which data blk within dir entry (off div blocksize) */ + long map; /* fs pointer of a particular block from dir entry */ + struct ext2_dir_entry *dp; /* pointer to directory entry */ +#ifdef E2DEBUG + unsigned char *i; +#endif /* E2DEBUG */ + + /* loop invariants: + current_ino = inode to lookup + dirname = pointer to filename component we are cur looking up within + the directory known pointed to by current_ino (if any) + */ + + while (1) + { +#ifdef E2DEBUG + printf ("inode %d\n", current_ino); + printf ("dirname=%s\n", dirname); +#endif /* E2DEBUG */ + + /* look up an inode */ + group_id = (current_ino - 1) / (SUPERBLOCK->s_inodes_per_group); + group_desc = group_id >> log2 (EXT2_DESC_PER_BLOCK (SUPERBLOCK)); + desc = group_id & (EXT2_DESC_PER_BLOCK (SUPERBLOCK) - 1); +#ifdef E2DEBUG + printf ("ipg=%d, dpb=%d\n", SUPERBLOCK->s_inodes_per_group, + EXT2_DESC_PER_BLOCK (SUPERBLOCK)); + printf ("group_id=%d group_desc=%d desc=%d\n", group_id, group_desc, desc); +#endif /* E2DEBUG */ + if (!ext2_rdfsb ( + (WHICH_SUPER + group_desc + SUPERBLOCK->s_first_data_block), + (int) GROUP_DESC)) + { + return 0; + } + gdp = GROUP_DESC; + ino_blk = gdp[desc].bg_inode_table + + (((current_ino - 1) % (SUPERBLOCK->s_inodes_per_group)) + >> log2 (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode))); +#ifdef E2DEBUG + printf ("inode table fsblock=%d\n", ino_blk); +#endif /* E2DEBUG */ + if (!ext2_rdfsb (ino_blk, (int) INODE)) + { + return 0; + } + + /* reset indirect blocks! */ + mapblock2 = mapblock1 = -1; + + raw_inode = INODE + + ((current_ino - 1) + & (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode) - 1)); +#ifdef E2DEBUG + printf ("ipb=%d, sizeof(inode)=%d\n", + (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)), + sizeof (struct ext2_inode)); + printf ("inode=%x, raw_inode=%x\n", INODE, raw_inode); + printf ("offset into inode table block=%d\n", (int) raw_inode - (int) INODE); + for (i = (unsigned char *) INODE; i <= (unsigned char *) raw_inode; + i++) + { + printf ("%c", "0123456789abcdef"[*i >> 4]); + printf ("%c", "0123456789abcdef"[*i % 16]); + if (!((i + 1 - (unsigned char *) INODE) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } + printf ("first word=%x\n", *((int *) raw_inode)); +#endif /* E2DEBUG */ + + /* copy inode to fixed location */ + memmove ((void *) INODE, (void *) raw_inode, sizeof (struct ext2_inode)); + +#ifdef E2DEBUG + printf ("first word=%x\n", *((int *) INODE)); +#endif /* E2DEBUG */ + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (INODE->i_mode)) + { + int len; + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + /* Get the symlink size. */ + filemax = (INODE->i_size); + if (filemax + len > sizeof (linkbuf) - 2) + { + errnum = ERR_FILELENGTH; + return 0; + } + + if (len) + { + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + memmove (linkbuf + filemax, dirname, len); + } + linkbuf[filemax + len] = '\0'; + + /* Read the symlink data. */ + if (INODE->i_blocks) + { + /* Read the necessary blocks, and reset the file pointer. */ + len = grub_read (linkbuf, filemax); + filepos = 0; + if (!len) + return 0; + } + else + { + /* Copy the data directly from the inode. */ + len = filemax; + memmove (linkbuf, (char *) INODE->i_block, len); + } + +#ifdef E2DEBUG + printf ("symlink=%s\n", linkbuf); +#endif + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + current_ino = EXT2_ROOT_INO; + updir_ino = current_ino; + } + else + { + /* Relative, so look it up in our parent directory. */ + current_ino = updir_ino; + } + + /* Try again using the new name. */ + continue; + } + + /* if end of filename, INODE points to the file's inode */ + if (!*dirname || isspace (*dirname)) + { + if (!S_ISREG (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filemax = (INODE->i_size); + return 1; + } + + /* else we have to traverse a directory */ + updir_ino = current_ino; + + /* skip over slashes */ + while (*dirname == '/') + dirname++; + + /* if this isn't a directory of sufficient size to hold our file, abort */ + if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + /* skip to next slash or end of filename (space) */ + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; + rest++); + + /* look through this directory and find the next filename component */ + /* invariant: rest points to slash after the next filename component */ + *rest = 0; + loc = 0; + + do + { + +#ifdef E2DEBUG + printf ("dirname=%s, rest=%s, loc=%d\n", dirname, rest, loc); +#endif /* E2DEBUG */ + + /* if our location/byte offset into the directory exceeds the size, + give up */ + if (loc >= INODE->i_size) + { + if (print_possibilities < 0) + { +# if 0 + putchar ('\n'); +# endif + } + else + { + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + return (print_possibilities < 0); + } + + /* else, find the (logical) block component of our location */ + blk = loc >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); + + /* we know which logical block of the directory entry we are looking + for, now we have to translate that to the physical (fs) block on + the disk */ + map = ext2fs_block_map (blk); +#ifdef E2DEBUG + printf ("fs block=%d\n", map); +#endif /* E2DEBUG */ + mapblock2 = -1; + if ((map < 0) || !ext2_rdfsb (map, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + *rest = ch; + return 0; + } + off = loc & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); + dp = (struct ext2_dir_entry *) (DATABLOCK2 + off); + /* advance loc prematurely to next on-disk directory entry */ + loc += dp->rec_len; + + /* NOTE: ext2fs filenames are NOT null-terminated */ + +#ifdef E2DEBUG + printf ("directory entry ino=%d\n", dp->inode); + if (dp->inode) + printf ("entry=%s\n", dp->name); +#endif /* E2DEBUG */ + + if (dp->inode) + { + int saved_c = dp->name[dp->name_len]; + + dp->name[dp->name_len] = 0; + str_chk = substring (dirname, dp->name); + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && (!*dirname || str_chk <= 0)) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (dp->name); + } +# endif + + dp->name[dp->name_len] = saved_c; + } + + } + while (!dp->inode || (str_chk || (print_possibilities && ch != '/'))); + + current_ino = dp->inode; + *(dirname = rest) = ch; + } + /* never get here */ +} + +#endif /* FSYS_EXT2_FS */ diff --git a/src/filo/fs/fsys_fat.c b/src/filo/fs/fsys_fat.c new file mode 100644 index 000000000..3f85bd6cb --- /dev/null +++ b/src/filo/fs/fsys_fat.c @@ -0,0 +1,494 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef FSYS_FAT + +#include "shared.h" +#include "filesys.h" +#include "fat.h" +#include <lib.h> +#include "string.h" + +struct fat_superblock +{ + int fat_offset; + int fat_length; + int fat_size; + int root_offset; + int root_max; + int data_offset; + + int num_sectors; + int num_clust; + int clust_eof_marker; + int sects_per_clust; + int sectsize_bits; + int clustsize_bits; + int root_cluster; + + int cached_fat; + int file_cluster; + int current_cluster_num; + int current_cluster; +}; + +/* pointer(s) into filesystem info buffer for DOS stuff */ +#define FAT_SUPER ( (struct fat_superblock *) \ + ( FSYS_BUF + 32256) )/* 512 bytes long */ +#define FAT_BUF ( FSYS_BUF + 30208 ) /* 4 sector FAT buffer */ +#define NAME_BUF ( FSYS_BUF + 29184 ) /* Filename buffer (833 bytes) */ + +#define FAT_CACHE_SIZE 2048 + +static __inline__ unsigned long +log2 (unsigned long word) +{ + __asm__ ("bsfl %1,%0" + : "=r" (word) + : "r" (word)); + return word; +} + +int +fat_mount (void) +{ + struct fat_bpb bpb; + __u32 magic, first_fat; + + /* Check partition type for harddisk */ + if (((current_drive & 0x80) || (current_slice != 0)) + && ! IS_PC_SLICE_TYPE_FAT (current_slice) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_MSDOS))) + return 0; + + /* Read bpb */ + if (! devread (0, 0, sizeof (bpb), (char *) &bpb)) + return 0; + + /* Check if the number of sectors per cluster is zero here, to avoid + zero division. */ + if (bpb.sects_per_clust == 0) + return 0; + + FAT_SUPER->sectsize_bits = log2 (FAT_CVT_U16 (bpb.bytes_per_sect)); + FAT_SUPER->clustsize_bits + = FAT_SUPER->sectsize_bits + log2 (bpb.sects_per_clust); + + /* Fill in info about super block */ + FAT_SUPER->num_sectors = FAT_CVT_U16 (bpb.short_sectors) + ? FAT_CVT_U16 (bpb.short_sectors) : bpb.long_sectors; + + /* FAT offset and length */ + FAT_SUPER->fat_offset = FAT_CVT_U16 (bpb.reserved_sects); + FAT_SUPER->fat_length = + bpb.fat_length ? bpb.fat_length : bpb.fat32_length; + + /* Rootdir offset and length for FAT12/16 */ + FAT_SUPER->root_offset = + FAT_SUPER->fat_offset + bpb.num_fats * FAT_SUPER->fat_length; + FAT_SUPER->root_max = FAT_DIRENTRY_LENGTH * FAT_CVT_U16(bpb.dir_entries); + + /* Data offset and number of clusters */ + FAT_SUPER->data_offset = + FAT_SUPER->root_offset + + ((FAT_SUPER->root_max - 1) >> FAT_SUPER->sectsize_bits) + 1; + FAT_SUPER->num_clust = + 2 + ((FAT_SUPER->num_sectors - FAT_SUPER->data_offset) + / bpb.sects_per_clust); + FAT_SUPER->sects_per_clust = bpb.sects_per_clust; + + if (!bpb.fat_length) + { + /* This is a FAT32 */ + if (FAT_CVT_U16(bpb.dir_entries)) + return 0; + + if (bpb.flags & 0x0080) + { + /* FAT mirroring is disabled, get active FAT */ + int active_fat = bpb.flags & 0x000f; + if (active_fat >= bpb.num_fats) + return 0; + FAT_SUPER->fat_offset += active_fat * FAT_SUPER->fat_length; + } + + FAT_SUPER->fat_size = 8; + FAT_SUPER->root_cluster = bpb.root_cluster; + + /* Yes the following is correct. FAT32 should be called FAT28 :) */ + FAT_SUPER->clust_eof_marker = 0xffffff8; + } + else + { + if (!FAT_SUPER->root_max) + return 0; + + FAT_SUPER->root_cluster = -1; + if (FAT_SUPER->num_clust > FAT_MAX_12BIT_CLUST) + { + FAT_SUPER->fat_size = 4; + FAT_SUPER->clust_eof_marker = 0xfff8; + } + else + { + FAT_SUPER->fat_size = 3; + FAT_SUPER->clust_eof_marker = 0xff8; + } + } + + + /* Now do some sanity checks */ + + if (FAT_CVT_U16(bpb.bytes_per_sect) != (1 << FAT_SUPER->sectsize_bits) + || FAT_CVT_U16(bpb.bytes_per_sect) != SECTOR_SIZE + || bpb.sects_per_clust != (1 << (FAT_SUPER->clustsize_bits + - FAT_SUPER->sectsize_bits)) + || FAT_SUPER->num_clust <= 2 + || (FAT_SUPER->fat_size * FAT_SUPER->num_clust / (2 * SECTOR_SIZE) + > FAT_SUPER->fat_length)) + return 0; + + /* kbs: Media check on first FAT entry [ported from PUPA] */ + + if (!devread(FAT_SUPER->fat_offset, 0, + sizeof(first_fat), (char *)&first_fat)) + return 0; + + if (FAT_SUPER->fat_size == 8) + { + first_fat &= 0x0fffffff; + magic = 0x0fffff00; + } + else if (FAT_SUPER->fat_size == 4) + { + first_fat &= 0x0000ffff; + magic = 0xff00; + } + else + { + first_fat &= 0x00000fff; + magic = 0x0f00; + } + + if (first_fat != (magic | bpb.media)) + return 0; + + FAT_SUPER->cached_fat = - 2 * FAT_CACHE_SIZE; + return 1; +} + +int +fat_read (char *buf, int len) +{ + int logical_clust; + int offset; + int ret = 0; + int size; + int count = 64; + + if (FAT_SUPER->file_cluster < 0) + { + /* root directory for fat16 */ + size = FAT_SUPER->root_max - filepos; + if (size > len) + size = len; + if (!devread(FAT_SUPER->root_offset, filepos, size, buf)) + return 0; + filepos += size; + return size; + } + + logical_clust = filepos >> FAT_SUPER->clustsize_bits; + offset = (filepos & ((1 << FAT_SUPER->clustsize_bits) - 1)); + if (logical_clust < FAT_SUPER->current_cluster_num) + { + FAT_SUPER->current_cluster_num = 0; + FAT_SUPER->current_cluster = FAT_SUPER->file_cluster; + } + + while (len > 0) + { + int sector; + while (logical_clust > FAT_SUPER->current_cluster_num) + { + /* calculate next cluster */ + int fat_entry = + FAT_SUPER->current_cluster * FAT_SUPER->fat_size; + int next_cluster; + int cached_pos = (fat_entry - FAT_SUPER->cached_fat); + + if (cached_pos < 0 || + (cached_pos + FAT_SUPER->fat_size) > 2*FAT_CACHE_SIZE) + { + FAT_SUPER->cached_fat = (fat_entry & ~(2*SECTOR_SIZE - 1)); + cached_pos = (fat_entry - FAT_SUPER->cached_fat); + sector = FAT_SUPER->fat_offset + + FAT_SUPER->cached_fat / (2*SECTOR_SIZE); + if (!devread (sector, 0, FAT_CACHE_SIZE, (char*) FAT_BUF)) + return 0; + } + next_cluster = * (unsigned long *) (FAT_BUF + (cached_pos >> 1)); + if (FAT_SUPER->fat_size == 3) + { + if (cached_pos & 1) + next_cluster >>= 4; + next_cluster &= 0xFFF; + } + else if (FAT_SUPER->fat_size == 4) + next_cluster &= 0xFFFF; + + if (next_cluster >= FAT_SUPER->clust_eof_marker) + return ret; + if (next_cluster < 2 || next_cluster >= FAT_SUPER->num_clust) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + + FAT_SUPER->current_cluster = next_cluster; + FAT_SUPER->current_cluster_num++; + } + + sector = FAT_SUPER->data_offset + + ((FAT_SUPER->current_cluster - 2) << (FAT_SUPER->clustsize_bits + - FAT_SUPER->sectsize_bits)); + size = (1 << FAT_SUPER->clustsize_bits) - offset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + devread(sector, offset, size, buf); + + disk_read_func = NULL; + + len -= size; + buf += size; + ret += size; + filepos += size; + logical_clust++; + offset = 0; + if(count--==0) { + count = 32; + printf("."); + } + } + +// printf("\n"); + return errnum ? 0 : ret; +} + +int +fat_dir (char *dirname) +{ + char *rest, ch, dir_buf[FAT_DIRENTRY_LENGTH]; + char *filename = (char *) NAME_BUF; + int attrib = FAT_ATTRIB_DIR; +#ifndef STAGE1_5 + int do_possibilities = 0; +#endif + + /* XXX I18N: + * the positions 2,4,6 etc are high bytes of a 16 bit unicode char + */ + static unsigned char longdir_pos[] = + { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; + int slot = -2; + int alias_checksum = -1; + + FAT_SUPER->file_cluster = FAT_SUPER->root_cluster; + filepos = 0; + FAT_SUPER->current_cluster_num = MAXINT; + + /* main loop to find desired directory entry */ + loop: + + /* if we have a real file (and we're not just printing possibilities), + then this is where we want to exit */ + + if (!*dirname || isspace (*dirname)) + { + if (attrib & FAT_ATTRIB_DIR) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + return 1; + } + + /* continue with the file/directory name interpretation */ + + while (*dirname == '/') + dirname++; + + if (!(attrib & FAT_ATTRIB_DIR)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + /* Directories don't have a file size */ + filemax = MAXINT; + + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + + *rest = 0; + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/') + do_possibilities = 1; +# endif + + while (1) + { + if (fat_read (dir_buf, FAT_DIRENTRY_LENGTH) != FAT_DIRENTRY_LENGTH + || dir_buf[0] == 0) + { + if (!errnum) + { +# ifndef STAGE1_5 + if (print_possibilities < 0) + { +#if 0 + putchar ('\n'); +#endif + return 1; + } +# endif /* STAGE1_5 */ + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + + return 0; + } + + if (FAT_DIRENTRY_ATTRIB (dir_buf) == FAT_ATTRIB_LONGNAME) + { + /* This is a long filename. The filename is build from back + * to front and may span multiple entries. To bind these + * entries together they all contain the same checksum over + * the short alias. + * + * The id field tells if this is the first entry (the last + * part) of the long filename, and also at which offset this + * belongs. + * + * We just write the part of the long filename this entry + * describes and continue with the next dir entry. + */ + int i, offset; + unsigned char id = FAT_LONGDIR_ID(dir_buf); + + if ((id & 0x40)) + { + id &= 0x3f; + slot = id; + filename[slot * 13] = 0; + alias_checksum = FAT_LONGDIR_ALIASCHECKSUM(dir_buf); + } + + if (id != slot || slot == 0 + || alias_checksum != FAT_LONGDIR_ALIASCHECKSUM(dir_buf)) + { + alias_checksum = -1; + continue; + } + + slot--; + offset = slot * 13; + + for (i=0; i < 13; i++) + filename[offset+i] = dir_buf[longdir_pos[i]]; + continue; + } + + if (!FAT_DIRENTRY_VALID (dir_buf)) + continue; + + if (alias_checksum != -1 && slot == 0) + { + int i; + unsigned char sum; + + slot = -2; + for (sum = 0, i = 0; i< 11; i++) + sum = ((sum >> 1) | (sum << 7)) + dir_buf[i]; + + if (sum == alias_checksum) + { +# ifndef STAGE1_5 + if (do_possibilities) + goto print_filename; +# endif /* STAGE1_5 */ + + if (substring (dirname, filename) == 0) + break; + } + } + + /* XXX convert to 8.3 filename format here */ + { + int i, j, c; + + for (i = 0; i < 8 && (c = filename[i] = tolower (dir_buf[i])) + && !isspace (c); i++); + + filename[i++] = '.'; + + for (j = 0; j < 3 && (c = filename[i + j] = tolower (dir_buf[8 + j])) + && !isspace (c); j++); + + if (j == 0) + i--; + + filename[i + j] = 0; + } + +# ifndef STAGE1_5 + if (do_possibilities) + { + print_filename: + if (substring (dirname, filename) <= 0) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (filename); + } + continue; + } +# endif /* STAGE1_5 */ + + if (substring (dirname, filename) == 0) + break; + } + + *(dirname = rest) = ch; + + attrib = FAT_DIRENTRY_ATTRIB (dir_buf); + filemax = FAT_DIRENTRY_FILELENGTH (dir_buf); + filepos = 0; + FAT_SUPER->file_cluster = FAT_DIRENTRY_FIRST_CLUSTER (dir_buf); + FAT_SUPER->current_cluster_num = MAXINT; + + /* go back to main loop at top of function */ + goto loop; +} + +#endif /* FSYS_FAT */ diff --git a/src/filo/fs/fsys_iso9660.c b/src/filo/fs/fsys_iso9660.c new file mode 100644 index 000000000..9e6679145 --- /dev/null +++ b/src/filo/fs/fsys_iso9660.c @@ -0,0 +1,348 @@ +/* + * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) + * including Rock Ridge Extensions support + * + * Copyright (C) 1998, 1999 Kousuke Takai <tak@kmc.kyoto-u.ac.jp> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * References: + * linux/fs/isofs/rock.[ch] + * mkisofs-1.11.1/diag/isoinfo.c + * mkisofs-1.11.1/iso9660.h + * (all are written by Eric Youngdale) + * + * Modifications by: + * Leonid Lisovskiy <lly@pisem.net> 2003 + */ + +/* + * Modified to make it work with FILO + * 2003-10 by SONE Takeshi + */ + +#ifdef FSYS_ISO9660 + +#include <lib.h> +#include "string.h" +#include "shared.h" +#include "filesys.h" +#include "iso9660.h" +#define DEBUG_THIS 1 +#include <debug.h> + +struct iso_superblock { + unsigned long vol_sector; + + unsigned long file_start; +}; + +#define ISO_SUPER ((struct iso_superblock *)(FSYS_BUF)) +#define PRIMDESC ((struct iso_primary_descriptor *)(FSYS_BUF + 2048)) +#define DIRREC ((struct iso_directory_record *)(FSYS_BUF + 4096)) +#define RRCONT_BUF ((unsigned char *)(FSYS_BUF + 6144)) +#define NAME_BUF ((unsigned char *)(FSYS_BUF + 8192)) + + +static inline unsigned long +log2 (unsigned long word) +{ + asm volatile ("bsfl %1,%0" +: "=r" (word) +: "r" (word)); + return word; +} + +static int +iso9660_devread (int sector, int byte_offset, int byte_len, char *buf) +{ + /* FILO uses 512-byte "soft" sector, and ISO-9660 uses 2048-byte + * CD-ROM sector */ + return devread(sector<<2, byte_offset, byte_len, buf); +} + +int +iso9660_mount (void) +{ + unsigned int sector; + + /* + * Because there is no defined slice type ID for ISO-9660 filesystem, + * this test will pass only either (1) if entire disk is used, or + * (2) if current partition is BSD style sub-partition whose ID is + * ISO-9660. + */ + /*if ((current_partition != 0xFFFFFF) + && !IS_PC_SLICE_TYPE_BSD_WITH_FS(current_slice, FS_ISO9660)) + return 0;*/ + + /* + * Currently, only FIRST session of MultiSession disks are supported !!! + */ + for (sector = 16 ; sector < 32 ; sector++) + { + if (!iso9660_devread(sector, 0, sizeof(*PRIMDESC), (char *)PRIMDESC)) + break; + /* check ISO_VD_PRIMARY and ISO_STANDARD_ID */ + if (CHECK4(&PRIMDESC->type, ISO_VD_PRIMARY, 'C', 'D', '0') + && CHECK2(PRIMDESC->id + 3, '0', '1')) + { + ISO_SUPER->vol_sector = sector; + ISO_SUPER->file_start = 0; + fsmax = PRIMDESC->volume_space_size.l; + return 1; + } + } + + return 0; +} + +int +iso9660_dir (char *dirname) +{ + struct iso_directory_record *idr; + RR_ptr_t rr_ptr; + struct rock_ridge *ce_ptr; + unsigned int pathlen; + int size; + unsigned int extent; + unsigned int rr_len; + unsigned char file_type; + unsigned char rr_flag; + + idr = &PRIMDESC->root_directory_record; + ISO_SUPER->file_start = 0; + + do + { + while (*dirname == '/') /* skip leading slashes */ + dirname++; + /* pathlen = strcspn(dirname, "/\n\t "); */ + for (pathlen = 0 ; + dirname[pathlen] + && !isspace(dirname[pathlen]) && dirname[pathlen] != '/' ; + pathlen++) + ; + + size = idr->size.l; + extent = idr->extent.l; + + while (size > 0) + { + if (!iso9660_devread(extent, 0, ISO_SECTOR_SIZE, (char *)DIRREC)) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + extent++; + + idr = (struct iso_directory_record *)DIRREC; + for (; idr->length.l > 0; + idr = (struct iso_directory_record *)((char *)idr + idr->length.l) ) + { + const char *name = idr->name; + unsigned int name_len = idr->name_len.l; + + file_type = (idr->flags.l & 2) ? ISO_DIRECTORY : ISO_REGULAR; + if (name_len == 1) + { + if ((name[0] == 0) || /* self */ + (name[0] == 1)) /* parent */ + continue; + } + if (name_len > 2 && CHECK2(name + name_len - 2, ';', '1')) + { + name_len -= 2; /* truncate trailing file version */ + if (name_len > 1 && name[name_len - 1] == '.') + name_len--; /* truncate trailing dot */ + } + + /* + * Parse Rock-Ridge extension + */ + rr_len = (idr->length.l - idr->name_len.l + - (unsigned char)sizeof(struct iso_directory_record) + + (unsigned char)sizeof(idr->name)); + rr_ptr.ptr = ((unsigned char *)idr + idr->name_len.l + + sizeof(struct iso_directory_record) + - sizeof(idr->name)); + if (rr_ptr.i & 1) + rr_ptr.i++, rr_len--; + ce_ptr = NULL; + rr_flag = RR_FLAG_NM | RR_FLAG_PX; + + while (rr_len >= 4) + { + if (rr_ptr.rr->version != 1) + { +#ifndef STAGE1_5 + if (debug) + printf( + "Non-supported version (%d) RockRidge chunk " + "`%c%c'\n", rr_ptr.rr->version, + rr_ptr.rr->signature & 0xFF, + rr_ptr.rr->signature >> 8); +#endif + } + else if (rr_ptr.rr->signature == RRMAGIC('R', 'R') + && rr_ptr.rr->len >= 5) + rr_flag &= rr_ptr.rr->u.rr.flags.l; + else if (rr_ptr.rr->signature == RRMAGIC('N', 'M')) + { + name = rr_ptr.rr->u.nm.name; + name_len = rr_ptr.rr->len - 5; + rr_flag &= ~RR_FLAG_NM; + } + else if (rr_ptr.rr->signature == RRMAGIC('P', 'X') + && rr_ptr.rr->len >= 36) + { + file_type = ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) + == POSIX_S_IFREG + ? ISO_REGULAR + : ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) + == POSIX_S_IFDIR + ? ISO_DIRECTORY : ISO_OTHER)); + rr_flag &= ~RR_FLAG_PX; + } + else if (rr_ptr.rr->signature == RRMAGIC('C', 'E') + && rr_ptr.rr->len >= 28) + ce_ptr = rr_ptr.rr; + if (!rr_flag) + /* + * There is no more extension we expects... + */ + break; + rr_len -= rr_ptr.rr->len; + rr_ptr.ptr += rr_ptr.rr->len; + if (rr_len < 4 && ce_ptr != NULL) + { + /* preserve name before loading new extent. */ + if( RRCONT_BUF <= (unsigned char *)name + && (unsigned char *)name < RRCONT_BUF + ISO_SECTOR_SIZE ) + { + memcpy(NAME_BUF, name, name_len); + name = NAME_BUF; + } + rr_ptr.ptr = RRCONT_BUF + ce_ptr->u.ce.offset.l; + rr_len = ce_ptr->u.ce.size.l; + if (!iso9660_devread(ce_ptr->u.ce.extent.l, 0, ISO_SECTOR_SIZE, RRCONT_BUF)) + { + errnum = 0; /* this is not fatal. */ + break; + } + ce_ptr = NULL; + } + } /* rr_len >= 4 */ + + filemax = MAXINT; + if (name_len >= pathlen + && !__builtin_memcmp(name, dirname, pathlen)) + { + if (dirname[pathlen] == '/' || !print_possibilities) + { + /* + * DIRNAME is directory component of pathname, + * or we are to open a file. + */ + if (pathlen == name_len) + { + if (dirname[pathlen] == '/') + { + if (file_type != ISO_DIRECTORY) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + goto next_dir_level; + } + if (file_type != ISO_REGULAR) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + ISO_SUPER->file_start = idr->extent.l; + filepos = 0; + filemax = idr->size.l; + return 1; + } + } + else /* Completion */ + { +#ifndef STAGE1_5 + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + memcpy(NAME_BUF, name, name_len); + NAME_BUF[name_len] = '\0'; + print_a_completion (NAME_BUF); +#endif + } + } + } /* for */ + + size -= ISO_SECTOR_SIZE; + } /* size>0 */ + + if (dirname[pathlen] == '/' || print_possibilities >= 0) + { + errnum = ERR_FILE_NOT_FOUND; + return 0; + } + +next_dir_level: + dirname += pathlen; + + } while (*dirname == '/'); + + return 1; +} + +int +iso9660_read (char *buf, int len) +{ + int sector, blkoffset, size, ret; + + if (ISO_SUPER->file_start == 0) + return 0; + + ret = 0; + blkoffset = filepos & (ISO_SECTOR_SIZE - 1); + sector = filepos >> ISO_SECTOR_BITS; + while (len > 0) + { + size = ISO_SECTOR_SIZE - blkoffset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + if (!iso9660_devread(ISO_SUPER->file_start + sector, blkoffset, size, buf)) + return 0; + + disk_read_func = NULL; + + len -= size; + buf += size; + ret += size; + filepos += size; + sector++; + blkoffset = 0; + } + + return ret; +} + +#endif /* FSYS_ISO9660 */ + diff --git a/src/filo/fs/fsys_jfs.c b/src/filo/fs/fsys_jfs.c new file mode 100644 index 000000000..307f83633 --- /dev/null +++ b/src/filo/fs/fsys_jfs.c @@ -0,0 +1,403 @@ +/* fsys_jfs.c - an implementation for the IBM JFS file system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2001,2002 Free Software Foundation, Inc. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef FSYS_JFS + +#include "shared.h" +#include "filesys.h" +#include "jfs.h" + +#define MAX_LINK_COUNT 8 + +#define DTTYPE_INLINE 0 +#define DTTYPE_PAGE 1 + +struct jfs_info +{ + int bsize; + int l2bsize; + int bdlog; + int xindex; + int xlastindex; + int sindex; + int slastindex; + int de_index; + int dttype; + xad_t *xad; + ldtentry_t *de; +}; + +static struct jfs_info jfs; + +#define xtpage ((xtpage_t *)FSYS_BUF) +#define dtpage ((dtpage_t *)((char *)FSYS_BUF + 4096)) +#define fileset ((dinode_t *)((char *)FSYS_BUF + 8192)) +#define inode ((dinode_t *)((char *)FSYS_BUF + 8192 + sizeof(dinode_t))) +#define dtroot ((dtroot_t *)(&inode->di_btroot)) + +static ldtentry_t de_always[2] = { + {1, -1, 2, {'.', '.'}}, + {1, -1, 1, {'.'}} +}; + +static int +isinxt (s64 key, s64 offset, s64 len) +{ + return (key >= offset) ? (key < offset + len ? 1 : 0) : 0; +} + +static xad_t * +first_extent (dinode_t *di) +{ + xtpage_t *xtp; + + jfs.xindex = 2; + xtp = (xtpage_t *)&di->di_btroot; + jfs.xad = &xtp->xad[2]; + if (xtp->header.flag & BT_LEAF) { + jfs.xlastindex = xtp->header.nextindex; + } else { + do { + devread (addressXAD (jfs.xad) << jfs.bdlog, 0, + sizeof(xtpage_t), (char *)xtpage); + jfs.xad = &xtpage->xad[2]; + } while (!(xtpage->header.flag & BT_LEAF)); + jfs.xlastindex = xtpage->header.nextindex; + } + + return jfs.xad; +} + +static xad_t * +next_extent (void) +{ + if (++jfs.xindex < jfs.xlastindex) { + } else if (xtpage->header.next) { + devread (xtpage->header.next << jfs.bdlog, 0, + sizeof(xtpage_t), (char *)xtpage); + jfs.xlastindex = xtpage->header.nextindex; + jfs.xindex = XTENTRYSTART; + jfs.xad = &xtpage->xad[XTENTRYSTART]; + } else { + return NULL; + } + return ++jfs.xad; +} + + +static void +di_read (u32 inum, dinode_t *di) +{ + s64 key; + u32 xd, ioffset; + s64 offset; + xad_t *xad; + pxd_t pxd; + + key = (((inum >> L2INOSPERIAG) << L2INOSPERIAG) + 4096) >> jfs.l2bsize; + xd = (inum & (INOSPERIAG - 1)) >> L2INOSPEREXT; + ioffset = ((inum & (INOSPERIAG - 1)) & (INOSPEREXT - 1)) << L2DISIZE; + xad = first_extent (fileset); + do { + offset = offsetXAD (xad); + if (isinxt (key, offset, lengthXAD (xad))) { + devread ((addressXAD (xad) + key - offset) << jfs.bdlog, + 3072 + xd*sizeof(pxd_t), sizeof(pxd_t), (char *)&pxd); + devread (addressPXD (&pxd) << jfs.bdlog, + ioffset, DISIZE, (char *)di); + break; + } + } while ((xad = next_extent ())); +} + +static ldtentry_t * +next_dentry (void) +{ + ldtentry_t *de; + s8 *stbl; + + if (jfs.dttype == DTTYPE_INLINE) { + if (jfs.sindex < jfs.slastindex) { + return (ldtentry_t *)&dtroot->slot[(int)dtroot->header.stbl[jfs.sindex++]]; + } + } else { + de = (ldtentry_t *)dtpage->slot; + stbl = (s8 *)&de[(int)dtpage->header.stblindex]; + if (jfs.sindex < jfs.slastindex) { + return &de[(int)stbl[jfs.sindex++]]; + } else if (dtpage->header.next) { + devread (dtpage->header.next << jfs.bdlog, 0, + sizeof(dtpage_t), (char *)dtpage); + jfs.slastindex = dtpage->header.nextindex; + jfs.sindex = 1; + return &de[(int)((s8 *)&de[(int)dtpage->header.stblindex])[0]]; + } + } + + return (jfs.de_index < 2) ? &de_always[jfs.de_index++] : NULL; +} + +static ldtentry_t * +first_dentry (void) +{ + dtroot_t *dtr; + pxd_t *xd; + idtentry_t *de; + + dtr = (dtroot_t *)&inode->di_btroot; + jfs.sindex = 0; + jfs.de_index = 0; + + de_always[0].inumber = inode->di_parent; + de_always[1].inumber = inode->di_number; + if (dtr->header.flag & BT_LEAF) { + jfs.dttype = DTTYPE_INLINE; + jfs.slastindex = dtr->header.nextindex; + } else { + de = (idtentry_t *)dtpage->slot; + jfs.dttype = DTTYPE_PAGE; + xd = &((idtentry_t *)dtr->slot)[(int)dtr->header.stbl[0]].xd; + for (;;) { + devread (addressPXD (xd) << jfs.bdlog, 0, + sizeof(dtpage_t), (char *)dtpage); + if (dtpage->header.flag & BT_LEAF) + break; + xd = &de[(int)((s8 *)&de[(int)dtpage->header.stblindex])[0]].xd; + } + jfs.slastindex = dtpage->header.nextindex; + } + + return next_dentry (); +} + + +static dtslot_t * +next_dslot (int next) +{ + return (jfs.dttype == DTTYPE_INLINE) + ? (dtslot_t *)&dtroot->slot[next] + : &((dtslot_t *)dtpage->slot)[next]; +} + +static void +uni2ansi (UniChar *uni, char *ansi, int len) +{ + for (; len; len--, uni++) + *ansi++ = (*uni & 0xff80) ? '?' : *(char *)uni; +} + +int +jfs_mount (void) +{ + struct jfs_superblock super; + + if (part_length < MINJFS >> SECTOR_BITS + || !devread (SUPER1_OFF >> SECTOR_BITS, 0, + sizeof(struct jfs_superblock), (char *)&super) + || (super.s_magic != JFS_MAGIC) + || !devread ((AITBL_OFF >> SECTOR_BITS) + FILESYSTEM_I, + 0, DISIZE, (char*)fileset)) { + return 0; + } + + jfs.bsize = super.s_bsize; + jfs.l2bsize = super.s_l2bsize; + jfs.bdlog = jfs.l2bsize - SECTOR_BITS; + + return 1; +} + +int +jfs_read (char *buf, int len) +{ + xad_t *xad; + s64 endofprev, endofcur; + s64 offset, xadlen; + int toread, startpos, endpos; + + startpos = filepos; + endpos = filepos + len; + endofprev = (1ULL << 62) - 1; + xad = first_extent (inode); + do { + offset = offsetXAD (xad); + xadlen = lengthXAD (xad); + if (isinxt (filepos >> jfs.l2bsize, offset, xadlen)) { + endofcur = (offset + xadlen) << jfs.l2bsize; + toread = (endofcur >= endpos) + ? len : (endofcur - filepos); + + disk_read_func = disk_read_hook; + devread (addressXAD (xad) << jfs.bdlog, + filepos - (offset << jfs.l2bsize), toread, buf); + disk_read_func = NULL; + + buf += toread; + len -= toread; + filepos += toread; + } else if (offset > endofprev) { + toread = ((offset << jfs.l2bsize) >= endpos) + ? len : ((offset - endofprev) << jfs.l2bsize); + len -= toread; + filepos += toread; + for (; toread; toread--) { + *buf++ = 0; + } + continue; + } + endofprev = offset + xadlen; + xad = next_extent (); + } while (len > 0 && xad); + + return filepos - startpos; +} + +int +jfs_dir (char *dirname) +{ + char *ptr, *rest, ch; + ldtentry_t *de; + dtslot_t *ds; + u32 inum, parent_inum; + s64 di_size; + u32 di_mode; + int namlen, cmp, n, link_count; + char namebuf[JFS_NAME_MAX + 1], linkbuf[JFS_PATH_MAX]; + + parent_inum = inum = ROOT_I; + link_count = 0; + for (;;) { + di_read (inum, inode); + di_size = inode->di_size; + di_mode = inode->di_mode; + + if ((di_mode & IFMT) == IFLNK) { + if (++link_count > MAX_LINK_COUNT) { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + if (di_size < (di_mode & INLINEEA ? 256 : 128)) { + grub_memmove (linkbuf, inode->di_fastsymlink, di_size); + n = di_size; + } else if (di_size < JFS_PATH_MAX - 1) { + filepos = 0; + filemax = di_size; + n = jfs_read (linkbuf, filemax); + } else { + errnum = ERR_FILELENGTH; + return 0; + } + + inum = (linkbuf[0] == '/') ? ROOT_I : parent_inum; + while (n < (JFS_PATH_MAX - 1) && (linkbuf[n++] = *dirname++)); + linkbuf[n] = 0; + dirname = linkbuf; + continue; + } + + if (!*dirname || isspace (*dirname)) { + if ((di_mode & IFMT) != IFREG) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + filepos = 0; + filemax = di_size; + return 1; + } + + if ((di_mode & IFMT) != IFDIR) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + for (; *dirname == '/'; dirname++); + + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + *rest = 0; + + de = first_dentry (); + for (;;) { + namlen = de->namlen; + if (de->next == -1) { + uni2ansi (de->name, namebuf, namlen); + namebuf[namlen] = 0; + } else { + uni2ansi (de->name, namebuf, DTLHDRDATALEN); + ptr = namebuf; + ptr += DTLHDRDATALEN; + namlen -= DTLHDRDATALEN; + ds = next_dslot (de->next); + while (ds->next != -1) { + uni2ansi (ds->name, ptr, DTSLOTDATALEN); + ptr += DTSLOTDATALEN; + namlen -= DTSLOTDATALEN; + ds = next_dslot (ds->next); + } + uni2ansi (ds->name, ptr, namlen); + ptr += namlen; + *ptr = 0; + } + + cmp = (!*dirname) ? -1 : substring (dirname, namebuf); +#ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && cmp <= 0) { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (namebuf); + } else +#endif + if (cmp == 0) { + parent_inum = inum; + inum = de->inumber; + *(dirname = rest) = ch; + break; + } + de = next_dentry (); + if (de == NULL) { + if (print_possibilities < 0) + return 1; + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + } + } + } +} + +int +jfs_embed (int *start_sector, int needed_sectors) +{ + struct jfs_superblock super; + + if (needed_sectors > 63 + || !devread (SUPER1_OFF >> SECTOR_BITS, 0, + sizeof (struct jfs_superblock), + (char *)&super) + || (super.s_magic != JFS_MAGIC)) { + return 0; + } + + *start_sector = 1; + return 1; +} + +#endif /* FSYS_JFS */ diff --git a/src/filo/fs/fsys_minix.c b/src/filo/fs/fsys_minix.c new file mode 100644 index 000000000..5c76796a2 --- /dev/null +++ b/src/filo/fs/fsys_minix.c @@ -0,0 +1,534 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Restrictions: + This is MINIX V1 only (yet) + Disk creation is like: + mkfs.minix -c DEVICE +*/ + +#ifdef FSYS_MINIX + +#include "shared.h" +#include "filesys.h" + +/* #define DEBUG_MINIX */ + +/* indirect blocks */ +static int mapblock1, mapblock2, namelen; + +/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ +#define DEV_BSIZE 512 + +/* include/linux/fs.h */ +#define BLOCK_SIZE_BITS 10 +#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS) + +/* made up, defaults to 1 but can be passed via mount_opts */ +#define WHICH_SUPER 1 +/* kind of from fs/ext2/super.c (is OK for minix) */ +#define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE) /* = 2 */ + +/* include/asm-i386/type.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +/* include/linux/minix_fs.h */ +#define MINIX_ROOT_INO 1 + +/* Not the same as the bogus LINK_MAX in <linux/limits.h>. Oh well. */ +#define MINIX_LINK_MAX 250 +#define MINIX2_LINK_MAX 65530 + +#define MINIX_I_MAP_SLOTS 8 +#define MINIX_Z_MAP_SLOTS 64 +#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */ +#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */ +#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */ +#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */ +#define MINIX_VALID_FS 0x0001 /* Clean fs. */ +#define MINIX_ERROR_FS 0x0002 /* fs has errors. */ + +#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode))) +#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode))) + +#define MINIX_V1 0x0001 /* original minix fs */ +#define MINIX_V2 0x0002 /* minix V2 fs */ + +/* originally this is : +#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version + here we have */ +#define INODE_VERSION(inode) (SUPERBLOCK->s_version) + +/* + * This is the original minix inode layout on disk. + * Note the 8-bit gid and atime and ctime. + */ +struct minix_inode { + __u16 i_mode; + __u16 i_uid; + __u32 i_size; + __u32 i_time; + __u8 i_gid; + __u8 i_nlinks; + __u16 i_zone[9]; +}; + +/* + * The new minix inode has all the time entries, as well as + * long block numbers and a third indirect block (7+1+1+1 + * instead of 7+1+1). Also, some previously 8-bit values are + * now 16-bit. The inode is now 64 bytes instead of 32. + */ +struct minix2_inode { + __u16 i_mode; + __u16 i_nlinks; + __u16 i_uid; + __u16 i_gid; + __u32 i_size; + __u32 i_atime; + __u32 i_mtime; + __u32 i_ctime; + __u32 i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + __u16 s_ninodes; + __u16 s_nzones; + __u16 s_imap_blocks; + __u16 s_zmap_blocks; + __u16 s_firstdatazone; + __u16 s_log_zone_size; + __u32 s_max_size; + __u16 s_magic; + __u16 s_state; + __u32 s_zones; +}; + +struct minix_dir_entry { + __u16 inode; + char name[0]; +}; + +/* made up, these are pointers into FSYS_BUF */ +/* read once, always stays there: */ +#define SUPERBLOCK \ + ((struct minix_super_block *)(FSYS_BUF)) +#define INODE \ + ((struct minix_inode *)((int) SUPERBLOCK + BLOCK_SIZE)) +#define DATABLOCK1 \ + ((int)((int)INODE + sizeof(struct minix_inode))) +#define DATABLOCK2 \ + ((int)((int)DATABLOCK1 + BLOCK_SIZE)) + +/* linux/stat.h */ +#define S_IFMT 00170000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFDIR 0040000 +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* check filesystem types and read superblock into memory buffer */ +int +minix_mount (void) +{ + if (((current_drive & 0x80) || current_slice != 0) + && ! IS_PC_SLICE_TYPE_MINIX (current_slice) + && ! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER)) + return 0; /* The partition is not of MINIX type */ + + if (part_length < (SBLOCK + + (sizeof (struct minix_super_block) / DEV_BSIZE))) + return 0; /* The partition is too short */ + + if (!devread (SBLOCK, 0, sizeof (struct minix_super_block), + (char *) SUPERBLOCK)) + return 0; /* Cannot read superblock */ + + switch (SUPERBLOCK->s_magic) + { + case MINIX_SUPER_MAGIC: + namelen = 14; + break; + case MINIX_SUPER_MAGIC2: + namelen = 30; + break; + default: + return 0; /* Unsupported type */ + } + + return 1; +} + +/* Takes a file system block number and reads it into BUFFER. */ +static int +minix_rdfsb (int fsblock, int buffer) +{ + return devread (fsblock * (BLOCK_SIZE / DEV_BSIZE), 0, + BLOCK_SIZE, (char *) buffer); +} + +/* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into + a physical block (the location in the file system) via an inode. */ +static int +minix_block_map (int logical_block) +{ + int i; + + if (logical_block < 7) + return INODE->i_zone[logical_block]; + + logical_block -= 7; + if (logical_block < 512) + { + i = INODE->i_zone[7]; + + if (!i || ((mapblock1 != 1) + && !minix_rdfsb (i, DATABLOCK1))) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 1; + return ((__u16 *) DATABLOCK1) [logical_block]; + } + + logical_block -= 512; + i = INODE->i_zone[8]; + if (!i || ((mapblock1 != 2) + && !minix_rdfsb (i, DATABLOCK1))) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 2; + i = ((__u16 *) DATABLOCK1)[logical_block >> 9]; + if (!i || ((mapblock2 != i) + && !minix_rdfsb (i, DATABLOCK2))) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock2 = i; + return ((__u16 *) DATABLOCK2)[logical_block & 511]; +} + +/* read from INODE into BUF */ +int +minix_read (char *buf, int len) +{ + int logical_block; + int offset; + int map; + int ret = 0; + int size = 0; + + while (len > 0) + { + /* find the (logical) block component of our location */ + logical_block = filepos >> BLOCK_SIZE_BITS; + offset = filepos & (BLOCK_SIZE - 1); + map = minix_block_map (logical_block); +#ifdef DEBUG_MINIX + printf ("map=%d\n", map); +#endif + if (map < 0) + break; + + size = BLOCK_SIZE; + size -= offset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + devread (map * (BLOCK_SIZE / DEV_BSIZE), + offset, size, buf); + + disk_read_func = NULL; + + buf += size; + len -= size; + filepos += size; + ret += size; + } + + if (errnum) + ret = 0; + + return ret; +} + +/* preconditions: minix_mount already executed, therefore supblk in buffer + known as SUPERBLOCK + returns: 0 if error, nonzero iff we were able to find the file successfully + postconditions: on a nonzero return, buffer known as INODE contains the + inode of the file we were trying to look up + side effects: none yet */ +int +minix_dir (char *dirname) +{ + int current_ino = MINIX_ROOT_INO; /* start at the root */ + int updir_ino = current_ino; /* the parent of the current directory */ + int ino_blk; /* fs pointer of the inode's info */ + + int str_chk = 0; /* used ot hold the results of a string + compare */ + + struct minix_inode * raw_inode; /* inode info for current_ino */ + + char linkbuf[PATH_MAX]; /* buffer for following sym-links */ + int link_count = 0; + + char * rest; + char ch; + + int off; /* offset within block of directory + entry */ + int loc; /* location within a directory */ + int blk; /* which data blk within dir entry */ + long map; /* fs pointer of a particular block from + dir entry */ + struct minix_dir_entry * dp; /* pointer to directory entry */ + + /* loop invariants: + current_ino = inode to lookup + dirname = pointer to filename component we are cur looking up within + the directory known pointed to by current_ino (if any) */ + +#ifdef DEBUG_MINIX + printf ("\n"); +#endif + + while (1) + { +#ifdef DEBUG_MINIX + printf ("inode %d, dirname %s\n", current_ino, dirname); +#endif + + ino_blk = (2 + SUPERBLOCK->s_imap_blocks + SUPERBLOCK->s_zmap_blocks + + (current_ino - 1) / MINIX_INODES_PER_BLOCK); + if (! minix_rdfsb (ino_blk, (int) INODE)) + return 0; + + /* reset indirect blocks! */ + mapblock2 = mapblock1 = -1; + + raw_inode = INODE + ((current_ino - 1) % MINIX_INODES_PER_BLOCK); + + /* copy inode to fixed location */ + memmove ((void *) INODE, (void *) raw_inode, + sizeof (struct minix_inode)); + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (INODE->i_mode)) + { + int len; + + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } +#ifdef DEBUG_MINIX + printf ("S_ISLNK (%s)\n", dirname); +#endif + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + /* Get the symlink size. */ + filemax = (INODE->i_size); + if (filemax + len > sizeof (linkbuf) - 2) + { + errnum = ERR_FILELENGTH; + return 0; + } + + if (len) + { + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + memmove (linkbuf + filemax, dirname, len); + } + linkbuf[filemax + len] = '\0'; + + /* Read the necessary blocks, and reset the file pointer. */ + len = grub_read (linkbuf, filemax); + filepos = 0; + if (!len) + return 0; + +#ifdef DEBUG_MINIX + printf ("symlink=%s\n", linkbuf); +#endif + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + current_ino = MINIX_ROOT_INO; + updir_ino = current_ino; + } + else + { + /* Relative, so look it up in our parent directory. */ + current_ino = updir_ino; + } + + /* Try again using the new name. */ + continue; + } + + /* If end of filename, INODE points to the file's inode */ + if (!*dirname || isspace (*dirname)) + { + if (!S_ISREG (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filemax = (INODE->i_size); + return 1; + } + + /* else we have to traverse a directory */ + updir_ino = current_ino; + + /* skip over slashes */ + while (*dirname == '/') + dirname++; + + /* if this isn't a directory of sufficient size to hold our file, + abort */ + if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + /* skip to next slash or end of filename (space) */ + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; + rest++); + + /* look through this directory and find the next filename component */ + /* invariant: rest points to slash after the next filename component */ + *rest = 0; + loc = 0; + + do + { +#ifdef DEBUG_MINIX + printf ("dirname=`%s', rest=`%s', loc=%d\n", dirname, rest, loc); +#endif + + /* if our location/byte offset into the directory exceeds the size, + give up */ + if (loc >= INODE->i_size) + { + if (print_possibilities < 0) + { +#if 0 + putchar ('\n'); +#endif + } + else + { + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + return (print_possibilities < 0); + } + + /* else, find the (logical) block component of our location */ + blk = loc >> BLOCK_SIZE_BITS; + + /* we know which logical block of the directory entry we are looking + for, now we have to translate that to the physical (fs) block on + the disk */ + map = minix_block_map (blk); +#ifdef DEBUG_MINIX + printf ("fs block=%d\n", map); +#endif + mapblock2 = -1; + if ((map < 0) || !minix_rdfsb (map, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + *rest = ch; + return 0; + } + off = loc & (BLOCK_SIZE - 1); + dp = (struct minix_dir_entry *) (DATABLOCK2 + off); + /* advance loc prematurely to next on-disk directory entry */ + loc += sizeof (dp->inode) + namelen; + + /* NOTE: minix filenames are NULL terminated if < NAMELEN + else exact */ + +#ifdef DEBUG_MINIX + printf ("directory entry ino=%d\n", dp->inode); + if (dp->inode) + printf ("entry=%s\n", dp->name); +#endif + + if (dp->inode) + { + int saved_c = dp->name[namelen]; + + dp->name[namelen] = 0; + str_chk = substring (dirname, dp->name); + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && (!*dirname || str_chk <= 0)) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (dp->name); + } +# endif + + dp->name[namelen] = saved_c; + } + + } + while (!dp->inode || (str_chk || (print_possibilities && ch != '/'))); + + current_ino = dp->inode; + *(dirname = rest) = ch; + } + /* never get here */ +} + +#endif /* FSYS_MINIX */ diff --git a/src/filo/fs/fsys_reiserfs.c b/src/filo/fs/fsys_reiserfs.c new file mode 100644 index 000000000..fbc2d74af --- /dev/null +++ b/src/filo/fs/fsys_reiserfs.c @@ -0,0 +1,1239 @@ +/* fsys_reiserfs.c - an implementation for the ReiserFS filesystem */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef FSYS_REISERFS +#include "shared.h" +#include "filesys.h" +#include <lib.h> +#include "string.h" + +#undef REISERDEBUG + +/* Some parts of this code (mainly the structures and defines) are + * from the original reiser fs code, as found in the linux kernel. + */ + +/* include/asm-i386/types.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; +typedef unsigned long long __u64; + +/* linux/posix_type.h */ +typedef long linux_off_t; + +/* linux/little_endian.h */ +#define __cpu_to_le64(x) ((__u64) (x)) +#define __le64_to_cpu(x) ((__u64) (x)) +#define __cpu_to_le32(x) ((__u32) (x)) +#define __le32_to_cpu(x) ((__u32) (x)) +#define __cpu_to_le16(x) ((__u16) (x)) +#define __le16_to_cpu(x) ((__u16) (x)) + +/* include/linux/reiser_fs.h */ +/* This is the new super block of a journaling reiserfs system */ +struct reiserfs_super_block +{ + __u32 s_block_count; /* blocks count */ + __u32 s_free_blocks; /* free blocks count */ + __u32 s_root_block; /* root block number */ + __u32 s_journal_block; /* journal block number */ + __u32 s_journal_dev; /* journal device number */ + __u32 s_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */ + __u32 s_journal_trans_max; /* max number of blocks in a transaction. */ + __u32 s_journal_magic; /* random value made on fs creation */ + __u32 s_journal_max_batch; /* max number of blocks to batch into a trans */ + __u32 s_journal_max_commit_age; /* in seconds, how old can an async commit be */ + __u32 s_journal_max_trans_age; /* in seconds, how old can a transaction be */ + __u16 s_blocksize; /* block size */ + __u16 s_oid_maxsize; /* max size of object id array */ + __u16 s_oid_cursize; /* current size of object id array */ + __u16 s_state; /* valid or error */ + char s_magic[16]; /* reiserfs magic string indicates that file system is reiserfs */ + __u16 s_tree_height; /* height of disk tree */ + __u16 s_bmap_nr; /* amount of bitmap blocks needed to address each block of file system */ + __u16 s_version; + char s_unused[128]; /* zero filled by mkreiserfs */ +}; + +#define REISERFS_MAX_SUPPORTED_VERSION 2 +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" + +#define MAX_HEIGHT 7 + +/* must be correct to keep the desc and commit structs at 4k */ +#define JOURNAL_TRANS_HALF 1018 + +/* first block written in a commit. */ +struct reiserfs_journal_desc { + __u32 j_trans_id; /* id of commit */ + __u32 j_len; /* length of commit. len +1 is the commit block */ + __u32 j_mount_id; /* mount id of this trans*/ + __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the first blocks */ + char j_magic[12]; +}; + +/* last block written in a commit */ +struct reiserfs_journal_commit { + __u32 j_trans_id; /* must match j_trans_id from the desc block */ + __u32 j_len; /* ditto */ + __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the last blocks */ + char j_digest[16]; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */ +}; + +/* this header block gets written whenever a transaction is considered + fully flushed, and is more recent than the last fully flushed + transaction. + fully flushed means all the log blocks and all the real blocks are + on disk, and this transaction does not need to be replayed. +*/ +struct reiserfs_journal_header { + /* id of last fully flushed transaction */ + __u32 j_last_flush_trans_id; + /* offset in the log of where to start replay after a crash */ + __u32 j_first_unflushed_offset; + /* mount id to detect very old transactions */ + __u32 j_mount_id; +}; + +/* magic string to find desc blocks in the journal */ +#define JOURNAL_DESC_MAGIC "ReIsErLB" + + +/* + * directories use this key as well as old files + */ +struct offset_v1 +{ + /* + * for regular files this is the offset to the first byte of the + * body, contained in the object-item, as measured from the start of + * the entire body of the object. + * + * for directory entries, k_offset consists of hash derived from + * hashing the name and using few bits (23 or more) of the resulting + * hash, and generation number that allows distinguishing names with + * hash collisions. If number of collisions overflows generation + * number, we return EEXIST. High order bit is 0 always + */ + __u32 k_offset; + __u32 k_uniqueness; +}; + +struct offset_v2 +{ + /* + * for regular files this is the offset to the first byte of the + * body, contained in the object-item, as measured from the start of + * the entire body of the object. + * + * for directory entries, k_offset consists of hash derived from + * hashing the name and using few bits (23 or more) of the resulting + * hash, and generation number that allows distinguishing names with + * hash collisions. If number of collisions overflows generation + * number, we return EEXIST. High order bit is 0 always + */ + __u64 k_offset:60; + __u64 k_type: 4; +}; + + +struct key +{ + /* packing locality: by default parent directory object id */ + __u32 k_dir_id; + /* object identifier */ + __u32 k_objectid; + /* the offset and node type (old and new form) */ + union + { + struct offset_v1 v1; + struct offset_v2 v2; + } + u; +}; + +#define KEY_SIZE (sizeof (struct key)) + +/* Header of a disk block. More precisely, header of a formatted leaf + or internal node, and not the header of an unformatted node. */ +struct block_head +{ + __u16 blk_level; /* Level of a block in the tree. */ + __u16 blk_nr_item; /* Number of keys/items in a block. */ + __u16 blk_free_space; /* Block free space in bytes. */ + struct key blk_right_delim_key; /* Right delimiting key for this block (supported for leaf level nodes + only) */ +}; +#define BLKH_SIZE (sizeof (struct block_head)) +#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */ + +struct item_head +{ + struct key ih_key; /* Everything in the tree is found by searching for it based on its key.*/ + + union + { + __u16 ih_free_space; /* The free space in the last unformatted node of an indirect item if this + is an indirect item. This equals 0xFFFF iff this is a direct item or + stat data item. Note that the key, not this field, is used to determine + the item type, and thus which field this union contains. */ + __u16 ih_entry_count; /* Iff this is a directory item, this field equals the number of directory + entries in the directory item. */ + } + u; + __u16 ih_item_len; /* total size of the item body */ + __u16 ih_item_location; /* an offset to the item body within the block */ + __u16 ih_version; /* ITEM_VERSION_1 for all old items, + ITEM_VERSION_2 for new ones. + Highest bit is set by fsck + temporary, cleaned after all done */ +}; +/* size of item header */ +#define IH_SIZE (sizeof (struct item_head)) + +#define ITEM_VERSION_1 0 +#define ITEM_VERSION_2 1 +#define IH_KEY_OFFSET(ih) ((ih)->ih_version == ITEM_VERSION_1 \ + ? (ih)->ih_key.u.v1.k_offset \ + : (ih)->ih_key.u.v2.k_offset) + +#define IH_KEY_ISTYPE(ih, type) ((ih)->ih_version == ITEM_VERSION_1 \ + ? (ih)->ih_key.u.v1.k_uniqueness == V1_##type \ + : (ih)->ih_key.u.v2.k_type == V2_##type) + +struct disk_child +{ + unsigned long dc_block_number; /* Disk child's block number. */ + unsigned short dc_size; /* Disk child's used space. */ +}; + +#define DC_SIZE (sizeof (struct disk_child)) + +/* Stat Data on disk. + * + * Note that reiserfs has two different forms of stat data. Luckily + * the fields needed by grub are at the same position. + */ +struct stat_data +{ + __u16 sd_mode; /* file type, permissions */ + __u16 sd_notused1[3]; /* fields not needed by reiserfs */ + __u32 sd_size; /* file size */ + __u32 sd_size_hi; /* file size high 32 bits (since version 2) */ +}; + +struct reiserfs_de_head +{ + __u32 deh_offset; /* third component of the directory entry key */ + __u32 deh_dir_id; /* objectid of the parent directory of the + object, that is referenced by directory entry */ + __u32 deh_objectid;/* objectid of the object, that is referenced by + directory entry */ + __u16 deh_location;/* offset of name in the whole item */ + __u16 deh_state; /* whether 1) entry contains stat data (for + future), and 2) whether entry is hidden + (unlinked) */ +}; + +#define DEH_SIZE (sizeof (struct reiserfs_de_head)) + +#define DEH_Statdata (1 << 0) /* not used now */ +#define DEH_Visible (1 << 2) + +#define SD_OFFSET 0 +#define SD_UNIQUENESS 0 +#define DOT_OFFSET 1 +#define DOT_DOT_OFFSET 2 +#define DIRENTRY_UNIQUENESS 500 + +#define V1_TYPE_STAT_DATA 0x0 +#define V1_TYPE_DIRECT 0xffffffff +#define V1_TYPE_INDIRECT 0xfffffffe +#define V1_TYPE_DIRECTORY_MAX 0xfffffffd +#define V2_TYPE_STAT_DATA 0 +#define V2_TYPE_INDIRECT 1 +#define V2_TYPE_DIRECT 2 +#define V2_TYPE_DIRENTRY 3 + +#define REISERFS_ROOT_OBJECTID 2 +#define REISERFS_ROOT_PARENT_OBJECTID 1 +#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) +/* the spot for the super in versions 3.5 - 3.5.11 (inclusive) */ +#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024) +#define REISERFS_OLD_BLOCKSIZE 4096 + +#define S_ISREG(mode) (((mode) & 0170000) == 0100000) +#define S_ISDIR(mode) (((mode) & 0170000) == 0040000) +#define S_ISLNK(mode) (((mode) & 0170000) == 0120000) + +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* The size of the node cache */ +#define FSYSREISER_CACHE_SIZE 24*1024 +#define FSYSREISER_MIN_BLOCKSIZE SECTOR_SIZE +#define FSYSREISER_MAX_BLOCKSIZE FSYSREISER_CACHE_SIZE / 3 + +/* Info about currently opened file */ +struct fsys_reiser_fileinfo +{ + __u32 k_dir_id; + __u32 k_objectid; +}; + +/* In memory info about the currently mounted filesystem */ +struct fsys_reiser_info +{ + /* The last read item head */ + struct item_head *current_ih; + /* The last read item */ + char *current_item; + /* The information for the currently opened file */ + struct fsys_reiser_fileinfo fileinfo; + /* The start of the journal */ + __u32 journal_block; + /* The size of the journal */ + __u32 journal_block_count; + /* The first valid descriptor block in journal + (relative to journal_block) */ + __u32 journal_first_desc; + + /* The ReiserFS version. */ + __u16 version; + /* The current depth of the reiser tree. */ + __u16 tree_depth; + /* SECTOR_SIZE << blocksize_shift == blocksize. */ + __u8 blocksize_shift; + /* 1 << full_blocksize_shift == blocksize. */ + __u8 fullblocksize_shift; + /* The reiserfs block size (must be a power of 2) */ + __u16 blocksize; + /* The number of cached tree nodes */ + __u16 cached_slots; + /* The number of valid transactions in journal */ + __u16 journal_transactions; + + unsigned int blocks[MAX_HEIGHT]; + unsigned int next_key_nr[MAX_HEIGHT]; +}; + +/* The cached s+tree blocks in FSYS_BUF, see below + * for a more detailed description. + */ +#define ROOT ((char *) ((int) FSYS_BUF)) +#define CACHE(i) (ROOT + ((i) << INFO->fullblocksize_shift)) +#define LEAF CACHE (DISK_LEAF_NODE_LEVEL) + +#define BLOCKHEAD(cache) ((struct block_head *) cache) +#define ITEMHEAD ((struct item_head *) ((int) LEAF + BLKH_SIZE)) +#define KEY(cache) ((struct key *) ((int) cache + BLKH_SIZE)) +#define DC(cache) ((struct disk_child *) \ + ((int) cache + BLKH_SIZE + KEY_SIZE * nr_item)) +/* The fsys_reiser_info block. + */ +#define INFO \ + ((struct fsys_reiser_info *) ((int) FSYS_BUF + FSYSREISER_CACHE_SIZE)) +/* + * The journal cache. For each transaction it contains the number of + * blocks followed by the real block numbers of this transaction. + * + * If the block numbers of some transaction won't fit in this space, + * this list is stopped with a 0xffffffff marker and the remaining + * uncommitted transactions aren't cached. + */ +#define JOURNAL_START ((__u32 *) (INFO + 1)) +#define JOURNAL_END ((__u32 *) (FSYS_BUF + FSYS_BUFLEN)) + + +static __inline__ unsigned long +log2 (unsigned long word) +{ + __asm__ ("bsfl %1,%0" + : "=r" (word) + : "r" (word)); + return word; +} + +static __inline__ int +is_power_of_two (unsigned long word) +{ + return (word & -word) == word; +} + +static int +journal_read (int block, int len, char *buffer) +{ + return devread ((INFO->journal_block + block) << INFO->blocksize_shift, + 0, len, buffer); +} + +/* Read a block from ReiserFS file system, taking the journal into + * account. If the block nr is in the journal, the block from the + * journal taken. + */ +static int +block_read (int blockNr, int start, int len, char *buffer) +{ + int transactions = INFO->journal_transactions; + int desc_block = INFO->journal_first_desc; + int journal_mask = INFO->journal_block_count - 1; + int translatedNr = blockNr; + __u32 *journal_table = JOURNAL_START; + while (transactions-- > 0) + { + int i = 0; + int j_len; + if (*journal_table != 0xffffffff) + { + /* Search for the blockNr in cached journal */ + j_len = *journal_table++; + while (i++ < j_len) + { + if (*journal_table++ == blockNr) + { + journal_table += j_len - i; + goto found; + } + } + } + else + { + /* This is the end of cached journal marker. The remaining + * transactions are still on disk. + */ + struct reiserfs_journal_desc desc; + struct reiserfs_journal_commit commit; + + if (! journal_read (desc_block, sizeof (desc), (char *) &desc)) + return 0; + + j_len = desc.j_len; + while (i < j_len && i < JOURNAL_TRANS_HALF) + if (desc.j_realblock[i++] == blockNr) + goto found; + + if (j_len >= JOURNAL_TRANS_HALF) + { + int commit_block = (desc_block + 1 + j_len) & journal_mask; + if (! journal_read (commit_block, + sizeof (commit), (char *) &commit)) + return 0; + while (i < j_len) + if (commit.j_realblock[i++ - JOURNAL_TRANS_HALF] == blockNr) + goto found; + } + } + goto not_found; + + found: + translatedNr = INFO->journal_block + ((desc_block + i) & journal_mask); +#ifdef REISERDEBUG + printf ("block_read: block %d is mapped to journal block %d.\n", + blockNr, translatedNr - INFO->journal_block); +#endif + /* We must continue the search, as this block may be overwritten + * in later transactions. + */ + not_found: + desc_block = (desc_block + 2 + j_len) & journal_mask; + } + return devread (translatedNr << INFO->blocksize_shift, start, len, buffer); +} + +/* Init the journal data structure. We try to cache as much as + * possible in the JOURNAL_START-JOURNAL_END space, but if it is full + * we can still read the rest from the disk on demand. + * + * The first number of valid transactions and the descriptor block of the + * first valid transaction are held in INFO. The transactions are all + * adjacent, but we must take care of the journal wrap around. + */ +static int +journal_init (void) +{ + unsigned int block_count = INFO->journal_block_count; + unsigned int desc_block; + unsigned int commit_block; + unsigned int next_trans_id; + struct reiserfs_journal_header header; + struct reiserfs_journal_desc desc; + struct reiserfs_journal_commit commit; + __u32 *journal_table = JOURNAL_START; + + journal_read (block_count, sizeof (header), (char *) &header); + desc_block = header.j_first_unflushed_offset; + if (desc_block >= block_count) + return 0; + + INFO->journal_first_desc = desc_block; + next_trans_id = header.j_last_flush_trans_id + 1; + +#ifdef REISERDEBUG + printf ("journal_init: last flushed %d\n", + header.j_last_flush_trans_id); +#endif + + while (1) + { + journal_read (desc_block, sizeof (desc), (char *) &desc); + if (substring (JOURNAL_DESC_MAGIC, desc.j_magic) > 0 + || desc.j_trans_id != next_trans_id + || desc.j_mount_id != header.j_mount_id) + /* no more valid transactions */ + break; + + commit_block = (desc_block + desc.j_len + 1) & (block_count - 1); + journal_read (commit_block, sizeof (commit), (char *) &commit); + if (desc.j_trans_id != commit.j_trans_id + || desc.j_len != commit.j_len) + /* no more valid transactions */ + break; + +#ifdef REISERDEBUG + printf ("Found valid transaction %d/%d at %d.\n", + desc.j_trans_id, desc.j_mount_id, desc_block); +#endif + + next_trans_id++; + if (journal_table < JOURNAL_END) + { + if ((journal_table + 1 + desc.j_len) >= JOURNAL_END) + { + /* The table is almost full; mark the end of the cached + * journal.*/ + *journal_table = 0xffffffff; + journal_table = JOURNAL_END; + } + else + { + int i; + /* Cache the length and the realblock numbers in the table. + * The block number of descriptor can easily be computed. + * and need not to be stored here. + */ + *journal_table++ = desc.j_len; + for (i = 0; i < desc.j_len && i < JOURNAL_TRANS_HALF; i++) + { + *journal_table++ = desc.j_realblock[i]; +#ifdef REISERDEBUG + printf ("block %d is in journal %d.\n", + desc.j_realblock[i], desc_block); +#endif + } + for ( ; i < desc.j_len; i++) + { + *journal_table++ = commit.j_realblock[i-JOURNAL_TRANS_HALF]; +#ifdef REISERDEBUG + printf ("block %d is in journal %d.\n", + commit.j_realblock[i-JOURNAL_TRANS_HALF], + desc_block); +#endif + } + } + } + desc_block = (commit_block + 1) & (block_count - 1); + } +#ifdef REISERDEBUG + printf ("Transaction %d/%d at %d isn't valid.\n", + desc.j_trans_id, desc.j_mount_id, desc_block); +#endif + + INFO->journal_transactions + = next_trans_id - header.j_last_flush_trans_id - 1; + return errnum == 0; +} + +/* check filesystem types and read superblock into memory buffer */ +int +reiserfs_mount (void) +{ + struct reiserfs_super_block super; + int superblock = REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; + + if (part_length < superblock + (sizeof (super) >> SECTOR_BITS) + || ! devread (superblock, 0, sizeof (struct reiserfs_super_block), + (char *) &super) + || (substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) + || (/* check that this is not a copy inside the journal log */ + super.s_journal_block * super.s_blocksize + <= REISERFS_DISK_OFFSET_IN_BYTES)) + { + /* Try old super block position */ + superblock = REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; + if (part_length < superblock + (sizeof (super) >> SECTOR_BITS) + || ! devread (superblock, 0, sizeof (struct reiserfs_super_block), + (char *) &super)) + return 0; + + if (substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) + { + /* pre journaling super block ? */ + if (substring (REISERFS_SUPER_MAGIC_STRING, + (char*) ((int) &super + 20)) > 0) + return 0; + + super.s_blocksize = REISERFS_OLD_BLOCKSIZE; + super.s_journal_block = 0; + super.s_version = 0; + } + } + + /* check the version number. */ + if (super.s_version > REISERFS_MAX_SUPPORTED_VERSION) + return 0; + + INFO->version = super.s_version; + INFO->blocksize = super.s_blocksize; + INFO->fullblocksize_shift = log2 (super.s_blocksize); + INFO->blocksize_shift = INFO->fullblocksize_shift - SECTOR_BITS; + INFO->cached_slots = + (FSYSREISER_CACHE_SIZE >> INFO->fullblocksize_shift) - 1; + +#ifdef REISERDEBUG + printf ("reiserfs_mount: version=%d, blocksize=%d\n", + INFO->version, INFO->blocksize); +#endif /* REISERDEBUG */ + + /* Clear node cache. */ + memset (INFO->blocks, 0, sizeof (INFO->blocks)); + + if (super.s_blocksize < FSYSREISER_MIN_BLOCKSIZE + || super.s_blocksize > FSYSREISER_MAX_BLOCKSIZE + || (SECTOR_SIZE << INFO->blocksize_shift) != super.s_blocksize) + return 0; + + /* Initialize journal code. If something fails we end with zero + * journal_transactions, so we don't access the journal at all. + */ + INFO->journal_transactions = 0; + if (super.s_journal_block != 0 && super.s_journal_dev == 0) + { + INFO->journal_block = super.s_journal_block; + INFO->journal_block_count = super.s_journal_size; + if (is_power_of_two (INFO->journal_block_count)) + journal_init (); + + /* Read in super block again, maybe it is in the journal */ + block_read (superblock >> INFO->blocksize_shift, + 0, sizeof (struct reiserfs_super_block), (char *) &super); + } + + if (! block_read (super.s_root_block, 0, INFO->blocksize, (char*) ROOT)) + return 0; + + INFO->tree_depth = BLOCKHEAD (ROOT)->blk_level; + +#ifdef REISERDEBUG + printf ("root read_in: block=%d, depth=%d\n", + super.s_root_block, INFO->tree_depth); +#endif /* REISERDEBUG */ + + if (INFO->tree_depth >= MAX_HEIGHT) + return 0; + if (INFO->tree_depth == DISK_LEAF_NODE_LEVEL) + { + /* There is only one node in the whole filesystem, + * which is simultanously leaf and root */ + memcpy (LEAF, ROOT, INFO->blocksize); + } + return 1; +} + +/***************** TREE ACCESSING METHODS *****************************/ + +/* I assume you are familiar with the ReiserFS tree, if not go to + * http://www.namesys.com/content_table.html + * + * My tree node cache is organized as following + * 0 ROOT node + * 1 LEAF node (if the ROOT is also a LEAF it is copied here + * 2-n other nodes on current path from bottom to top. + * if there is not enough space in the cache, the top most are + * omitted. + * + * I have only two methods to find a key in the tree: + * search_stat(dir_id, objectid) searches for the stat entry (always + * the first entry) of an object. + * next_key() gets the next key in tree order. + * + * This means, that I can only sequential reads of files are + * efficient, but this really doesn't hurt for grub. + */ + +/* Read in the node at the current path and depth into the node cache. + * You must set INFO->blocks[depth] before. + */ +static char * +read_tree_node (unsigned int blockNr, int depth) +{ + char* cache = CACHE(depth); + int num_cached = INFO->cached_slots; + if (depth < num_cached) + { + /* This is the cached part of the path. Check if same block is + * needed. + */ + if (blockNr == INFO->blocks[depth]) + return cache; + } + else + cache = CACHE(num_cached); + +#ifdef REISERDEBUG + printf (" next read_in: block=%d (depth=%d)\n", + blockNr, depth); +#endif /* REISERDEBUG */ + if (! block_read (blockNr, 0, INFO->blocksize, cache)) + return 0; + /* Make sure it has the right node level */ + if (BLOCKHEAD (cache)->blk_level != depth) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + + INFO->blocks[depth] = blockNr; + return cache; +} + +/* Get the next key, i.e. the key following the last retrieved key in + * tree order. INFO->current_ih and + * INFO->current_info are adapted accordingly. */ +static int +next_key (void) +{ + int depth; + struct item_head *ih = INFO->current_ih + 1; + char *cache; + +#ifdef REISERDEBUG + printf ("next_key:\n old ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + + if (ih == &ITEMHEAD[BLOCKHEAD (LEAF)->blk_nr_item]) + { + depth = DISK_LEAF_NODE_LEVEL; + /* The last item, was the last in the leaf node. + * Read in the next block + */ + do + { + if (depth == INFO->tree_depth) + { + /* There are no more keys at all. + * Return a dummy item with MAX_KEY */ + ih = (struct item_head *) &BLOCKHEAD (LEAF)->blk_right_delim_key; + goto found; + } + depth++; +#ifdef REISERDEBUG + printf (" depth=%d, i=%d\n", depth, INFO->next_key_nr[depth]); +#endif /* REISERDEBUG */ + } + while (INFO->next_key_nr[depth] == 0); + + if (depth == INFO->tree_depth) + cache = ROOT; + else if (depth <= INFO->cached_slots) + cache = CACHE (depth); + else + { + cache = read_tree_node (INFO->blocks[depth], depth); + if (! cache) + return 0; + } + + do + { + int nr_item = BLOCKHEAD (cache)->blk_nr_item; + int key_nr = INFO->next_key_nr[depth]++; +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, key_nr, nr_item); +#endif /* REISERDEBUG */ + if (key_nr == nr_item) + /* This is the last item in this block, set the next_key_nr to 0 */ + INFO->next_key_nr[depth] = 0; + + cache = read_tree_node (DC (cache)[key_nr].dc_block_number, --depth); + if (! cache) + return 0; + } + while (depth > DISK_LEAF_NODE_LEVEL); + + ih = ITEMHEAD; + } + found: + INFO->current_ih = ih; + INFO->current_item = &LEAF[ih->ih_item_location]; +#ifdef REISERDEBUG + printf (" new ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + return 1; +} + +/* preconditions: reiserfs_mount already executed, therefore + * INFO block is valid + * returns: 0 if error (errnum is set), + * nonzero iff we were able to find the key successfully. + * postconditions: on a nonzero return, the current_ih and + * current_item fields describe the key that equals the + * searched key. INFO->next_key contains the next key after + * the searched key. + * side effects: messes around with the cache. + */ +static int +search_stat (__u32 dir_id, __u32 objectid) +{ + char *cache; + int depth; + int nr_item; + int i; + struct item_head *ih; +#ifdef REISERDEBUG + printf ("search_stat:\n key %d:%d:0:0\n", dir_id, objectid); +#endif /* REISERDEBUG */ + + depth = INFO->tree_depth; + cache = ROOT; + + while (depth > DISK_LEAF_NODE_LEVEL) + { + struct key *key; + nr_item = BLOCKHEAD (cache)->blk_nr_item; + + key = KEY (cache); + + for (i = 0; i < nr_item; i++) + { + if (key->k_dir_id > dir_id + || (key->k_dir_id == dir_id + && (key->k_objectid > objectid + || (key->k_objectid == objectid + && (key->u.v1.k_offset + | key->u.v1.k_uniqueness) > 0)))) + break; + key++; + } + +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); +#endif /* REISERDEBUG */ + INFO->next_key_nr[depth] = (i == nr_item) ? 0 : i+1; + cache = read_tree_node (DC (cache)[i].dc_block_number, --depth); + if (! cache) + return 0; + } + + /* cache == LEAF */ + nr_item = BLOCKHEAD (LEAF)->blk_nr_item; + ih = ITEMHEAD; + for (i = 0; i < nr_item; i++) + { + if (ih->ih_key.k_dir_id == dir_id + && ih->ih_key.k_objectid == objectid + && ih->ih_key.u.v1.k_offset == 0 + && ih->ih_key.u.v1.k_uniqueness == 0) + { +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); +#endif /* REISERDEBUG */ + INFO->current_ih = ih; + INFO->current_item = &LEAF[ih->ih_item_location]; + return 1; + } + ih++; + } + errnum = ERR_FSYS_CORRUPT; + return 0; +} + +int +reiserfs_read (char *buf, int len) +{ + unsigned int blocksize; + unsigned int offset; + unsigned int to_read; + char *prev_buf = buf; + +#ifdef REISERDEBUG + printf ("reiserfs_read: filepos=%d len=%d, offset=%x:%x\n", + filepos, len, (__u64) IH_KEY_OFFSET (INFO->current_ih) - 1); +#endif /* REISERDEBUG */ + + if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid + || IH_KEY_OFFSET (INFO->current_ih) > filepos + 1) + { + search_stat (INFO->fileinfo.k_dir_id, INFO->fileinfo.k_objectid); + goto get_next_key; + } + + while (! errnum) + { + if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid) + break; + + offset = filepos - IH_KEY_OFFSET (INFO->current_ih) + 1; + blocksize = INFO->current_ih->ih_item_len; + +#ifdef REISERDEBUG + printf (" loop: filepos=%d len=%d, offset=%d blocksize=%d\n", + filepos, len, offset, blocksize); +#endif /* REISERDEBUG */ + + if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_DIRECT) + && offset < blocksize) + { +#ifdef REISERDEBUG + printf ("direct_read: offset=%d, blocksize=%d\n", + offset, blocksize); +#endif /* REISERDEBUG */ + to_read = blocksize - offset; + if (to_read > len) + to_read = len; + + if (disk_read_hook != NULL) + { + disk_read_func = disk_read_hook; + + block_read (INFO->blocks[DISK_LEAF_NODE_LEVEL], + (INFO->current_item - LEAF + offset), to_read, buf); + + disk_read_func = NULL; + } + else + memcpy (buf, INFO->current_item + offset, to_read); + goto update_buf_len; + } + else if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_INDIRECT)) + { + blocksize = (blocksize >> 2) << INFO->fullblocksize_shift; +#ifdef REISERDEBUG + printf ("indirect_read: offset=%d, blocksize=%d\n", + offset, blocksize); +#endif /* REISERDEBUG */ + + while (offset < blocksize) + { + __u32 blocknr = ((__u32 *) INFO->current_item) + [offset >> INFO->fullblocksize_shift]; + int blk_offset = offset & (INFO->blocksize-1); + + to_read = INFO->blocksize - blk_offset; + if (to_read > len) + to_read = len; + + disk_read_func = disk_read_hook; + + /* Journal is only for meta data. Data blocks can be read + * directly without using block_read + */ + devread (blocknr << INFO->blocksize_shift, + blk_offset, to_read, buf); + + disk_read_func = NULL; + update_buf_len: + len -= to_read; + buf += to_read; + offset += to_read; + filepos += to_read; + if (len == 0) + goto done; + } + } + get_next_key: + next_key (); + } + done: + return errnum ? 0 : buf - prev_buf; +} + + +/* preconditions: reiserfs_mount already executed, therefore + * INFO block is valid + * returns: 0 if error, nonzero iff we were able to find the file successfully + * postconditions: on a nonzero return, INFO->fileinfo contains the info + * of the file we were trying to look up, filepos is 0 and filemax is + * the size of the file. + */ +int +reiserfs_dir (char *dirname) +{ + struct reiserfs_de_head *de_head; + char *rest, ch; + __u32 dir_id, objectid, parent_dir_id = 0, parent_objectid = 0; +#ifndef STAGE1_5 + int do_possibilities = 0; +#endif /* ! STAGE1_5 */ + char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ + int link_count = 0; + int mode; + + dir_id = REISERFS_ROOT_PARENT_OBJECTID; + objectid = REISERFS_ROOT_OBJECTID; + + while (1) + { +#ifdef REISERDEBUG + printf ("dirname=%s\n", dirname); +#endif /* REISERDEBUG */ + + /* Search for the stat info first. */ + if (! search_stat (dir_id, objectid)) + return 0; + +#ifdef REISERDEBUG + printf ("sd_mode=%x sd_size=%d\n", + ((struct stat_data *) INFO->current_item)->sd_mode, + ((struct stat_data *) INFO->current_item)->sd_size); +#endif /* REISERDEBUG */ + + mode = ((struct stat_data *) INFO->current_item)->sd_mode; + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (mode)) + { + int len; + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + + /* Get the symlink size. */ + filemax = ((struct stat_data *) INFO->current_item)->sd_size; + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + if (filemax + len > sizeof (linkbuf) - 1) + { + errnum = ERR_FILELENGTH; + return 0; + } + + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + grub_memmove (linkbuf + filemax, dirname, len+1); + + INFO->fileinfo.k_dir_id = dir_id; + INFO->fileinfo.k_objectid = objectid; + filepos = 0; + if (! next_key () + || reiserfs_read (linkbuf, filemax) != filemax) + { + if (! errnum) + errnum = ERR_FSYS_CORRUPT; + return 0; + } + +#ifdef REISERDEBUG + printf ("symlink=%s\n", linkbuf); +#endif /* REISERDEBUG */ + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + dir_id = REISERFS_ROOT_PARENT_OBJECTID; + objectid = REISERFS_ROOT_OBJECTID; + } + else + { + /* Relative, so look it up in our parent directory. */ + dir_id = parent_dir_id; + objectid = parent_objectid; + } + + /* Now lookup the new name. */ + continue; + } + + /* if we have a real file (and we're not just printing possibilities), + then this is where we want to exit */ + + if (! *dirname || isspace (*dirname)) + { + if (! S_ISREG (mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filepos = 0; + filemax = ((struct stat_data *) INFO->current_item)->sd_size; + + /* If this is a new stat data and size is > 4GB set filemax to + * maximum + */ + if (INFO->current_ih->ih_version == ITEM_VERSION_2 + && ((struct stat_data *) INFO->current_item)->sd_size_hi > 0) + filemax = 0xffffffff; + + INFO->fileinfo.k_dir_id = dir_id; + INFO->fileinfo.k_objectid = objectid; + return next_key (); + } + + /* continue with the file/directory name interpretation */ + while (*dirname == '/') + dirname++; + if (! S_ISDIR (mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + for (rest = dirname; (ch = *rest) && ! isspace (ch) && ch != '/'; rest++); + *rest = 0; + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/') + do_possibilities = 1; +# endif /* ! STAGE1_5 */ + + while (1) + { + char *name_end; + int num_entries; + + if (! next_key ()) + return 0; +#ifdef REISERDEBUG + printf ("ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + + if (INFO->current_ih->ih_key.k_objectid != objectid) + break; + + name_end = INFO->current_item + INFO->current_ih->ih_item_len; + de_head = (struct reiserfs_de_head *) INFO->current_item; + num_entries = INFO->current_ih->u.ih_entry_count; + while (num_entries > 0) + { + char *filename = INFO->current_item + de_head->deh_location; + char tmp = *name_end; + if ((de_head->deh_state & DEH_Visible)) + { + int cmp; + /* Directory names in ReiserFS are not null + * terminated. We write a temporary 0 behind it. + * NOTE: that this may overwrite the first block in + * the tree cache. That doesn't hurt as long as we + * don't call next_key () in between. + */ + *name_end = 0; + cmp = substring (dirname, filename); + *name_end = tmp; +# ifndef STAGE1_5 + if (do_possibilities) + { + if (cmp <= 0) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + *name_end = 0; + print_a_completion (filename); + *name_end = tmp; + } + } + else +# endif /* ! STAGE1_5 */ + if (cmp == 0) + goto found; + } + /* The beginning of this name marks the end of the next name. + */ + name_end = filename; + de_head++; + num_entries--; + } + } + +# ifndef STAGE1_5 + if (print_possibilities < 0) + return 1; +# endif /* ! STAGE1_5 */ + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + + found: + + *rest = ch; + dirname = rest; + + parent_dir_id = dir_id; + parent_objectid = objectid; + dir_id = de_head->deh_dir_id; + objectid = de_head->deh_objectid; + + } + + return 1; +} + +int +reiserfs_embed (int *start_sector, int needed_sectors) +{ + struct reiserfs_super_block super; + int num_sectors; + + if (! devread (REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS, 0, + sizeof (struct reiserfs_super_block), (char *) &super)) + return 0; + + *start_sector = 1; /* reserve first sector for stage1 */ + if ((substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) <= 0 + || substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) <= 0) + && (/* check that this is not a super block copy inside + * the journal log */ + super.s_journal_block * super.s_blocksize + > REISERFS_DISK_OFFSET_IN_BYTES)) + num_sectors = (REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; + else + num_sectors = (REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; + + return (needed_sectors <= num_sectors); +} +#endif /* FSYS_REISERFS */ diff --git a/src/filo/fs/fsys_xfs.c b/src/filo/fs/fsys_xfs.c new file mode 100644 index 000000000..b551c07a2 --- /dev/null +++ b/src/filo/fs/fsys_xfs.c @@ -0,0 +1,624 @@ +/* fsys_xfs.c - an implementation for the SGI XFS file system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2001,2002 Free Software Foundation, Inc. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef FSYS_XFS + +#include "shared.h" +#include "filesys.h" +#include "xfs.h" + +#define MAX_LINK_COUNT 8 + +typedef struct xad { + xfs_fileoff_t offset; + xfs_fsblock_t start; + xfs_filblks_t len; +} xad_t; + +struct xfs_info { + int bsize; + int dirbsize; + int isize; + unsigned int agblocks; + int bdlog; + int blklog; + int inopblog; + int agblklog; + int agnolog; + unsigned int nextents; + xfs_daddr_t next; + xfs_daddr_t daddr; + xfs_dablk_t forw; + xfs_dablk_t dablk; + xfs_bmbt_rec_32_t *xt; + xfs_bmbt_ptr_t ptr0; + int btnode_ptr0_off; + int i8param; + int dirpos; + int dirmax; + int blkoff; + int fpos; + xfs_ino_t rootino; +}; + +static struct xfs_info xfs; + +#define dirbuf ((char *)FSYS_BUF) +#define filebuf ((char *)FSYS_BUF + 4096) +#define inode ((xfs_dinode_t *)((char *)FSYS_BUF + 8192)) +#define icore (inode->di_core) + +#define mask32lo(n) (((__uint32_t)1 << (n)) - 1) + +#define XFS_INO_MASK(k) ((__uint32_t)((1ULL << (k)) - 1)) +#define XFS_INO_OFFSET_BITS xfs.inopblog +#define XFS_INO_AGBNO_BITS xfs.agblklog +#define XFS_INO_AGINO_BITS (xfs.agblklog + xfs.inopblog) +#define XFS_INO_AGNO_BITS xfs.agnolog + +static inline xfs_agblock_t +agino2agbno (xfs_agino_t agino) +{ + return agino >> XFS_INO_OFFSET_BITS; +} + +static inline xfs_agnumber_t +ino2agno (xfs_ino_t ino) +{ + return ino >> XFS_INO_AGINO_BITS; +} + +static inline xfs_agino_t +ino2agino (xfs_ino_t ino) +{ + return ino & XFS_INO_MASK(XFS_INO_AGINO_BITS); +} + +static inline int +ino2offset (xfs_ino_t ino) +{ + return ino & XFS_INO_MASK(XFS_INO_OFFSET_BITS); +} + +static inline __const__ __uint16_t +le16 (__uint16_t x) +{ + __asm__("xchgb %b0,%h0" \ + : "=q" (x) \ + : "0" (x)); \ + return x; +} + +static inline __const__ __uint32_t +le32 (__uint32_t x) +{ +#if 0 + /* 386 doesn't have bswap. */ + __asm__("bswap %0" : "=r" (x) : "0" (x)); +#else + /* This is slower but this works on all x86 architectures. */ + __asm__("xchgb %b0, %h0" \ + "\n\troll $16, %0" \ + "\n\txchgb %b0, %h0" \ + : "=q" (x) : "0" (x)); +#endif + return x; +} + +static inline __const__ __uint64_t +le64 (__uint64_t x) +{ + __uint32_t h = x >> 32; + __uint32_t l = x & ((1ULL<<32)-1); + return (((__uint64_t)le32(l)) << 32) | ((__uint64_t)(le32(h))); +} + + +static xfs_fsblock_t +xt_start (xfs_bmbt_rec_32_t *r) +{ + return (((xfs_fsblock_t)(le32 (r->l1) & mask32lo(9))) << 43) | + (((xfs_fsblock_t)le32 (r->l2)) << 11) | + (((xfs_fsblock_t)le32 (r->l3)) >> 21); +} + +static xfs_fileoff_t +xt_offset (xfs_bmbt_rec_32_t *r) +{ + return (((xfs_fileoff_t)le32 (r->l0) & + mask32lo(31)) << 23) | + (((xfs_fileoff_t)le32 (r->l1)) >> 9); +} + +static xfs_filblks_t +xt_len (xfs_bmbt_rec_32_t *r) +{ + return le32(r->l3) & mask32lo(21); +} + +static inline int +xfs_highbit32(__uint32_t v) +{ + int i; + + if (--v) { + for (i = 0; i < 31; i++, v >>= 1) { + if (v == 0) + return i; + } + } + return 0; +} + +static int +isinxt (xfs_fileoff_t key, xfs_fileoff_t offset, xfs_filblks_t len) +{ + return (key >= offset) ? (key < offset + len ? 1 : 0) : 0; +} + +static xfs_daddr_t +agb2daddr (xfs_agnumber_t agno, xfs_agblock_t agbno) +{ + return ((xfs_fsblock_t)agno*xfs.agblocks + agbno) << xfs.bdlog; +} + +static xfs_daddr_t +fsb2daddr (xfs_fsblock_t fsbno) +{ + return agb2daddr ((xfs_agnumber_t)(fsbno >> xfs.agblklog), + (xfs_agblock_t)(fsbno & mask32lo(xfs.agblklog))); +} + +#undef offsetof +#define offsetof(t,m) ((int)&(((t *)0)->m)) + +static inline int +btroot_maxrecs (void) +{ + int tmp = icore.di_forkoff ? (icore.di_forkoff << 3) : xfs.isize; + + return (tmp - sizeof(xfs_bmdr_block_t) - offsetof(xfs_dinode_t, di_u)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t)); +} + +static int +di_read (xfs_ino_t ino) +{ + xfs_agino_t agino; + xfs_agnumber_t agno; + xfs_agblock_t agbno; + xfs_daddr_t daddr; + int offset; + + agno = ino2agno (ino); + agino = ino2agino (ino); + agbno = agino2agbno (agino); + offset = ino2offset (ino); + daddr = agb2daddr (agno, agbno); + + devread (daddr, offset*xfs.isize, xfs.isize, (char *)inode); + + xfs.ptr0 = *(xfs_bmbt_ptr_t *) + (inode->di_u.di_c + sizeof(xfs_bmdr_block_t) + + btroot_maxrecs ()*sizeof(xfs_bmbt_key_t)); + + return 1; +} + +static void +init_extents (void) +{ + xfs_bmbt_ptr_t ptr0; + xfs_btree_lblock_t h; + + switch (icore.di_format) { + case XFS_DINODE_FMT_EXTENTS: + xfs.xt = inode->di_u.di_bmx; + xfs.nextents = le32 (icore.di_nextents); + break; + case XFS_DINODE_FMT_BTREE: + ptr0 = xfs.ptr0; + for (;;) { + xfs.daddr = fsb2daddr (le64(ptr0)); + devread (xfs.daddr, 0, + sizeof(xfs_btree_lblock_t), (char *)&h); + if (!h.bb_level) { + xfs.nextents = le16(h.bb_numrecs); + xfs.next = fsb2daddr (le64(h.bb_rightsib)); + xfs.fpos = sizeof(xfs_btree_block_t); + return; + } + devread (xfs.daddr, xfs.btnode_ptr0_off, + sizeof(xfs_bmbt_ptr_t), (char *)&ptr0); + } + } +} + +static xad_t * +next_extent (void) +{ + static xad_t xad; + + switch (icore.di_format) { + case XFS_DINODE_FMT_EXTENTS: + if (xfs.nextents == 0) + return NULL; + break; + case XFS_DINODE_FMT_BTREE: + if (xfs.nextents == 0) { + xfs_btree_lblock_t h; + if (xfs.next == 0) + return NULL; + xfs.daddr = xfs.next; + devread (xfs.daddr, 0, sizeof(xfs_btree_lblock_t), (char *)&h); + xfs.nextents = le16(h.bb_numrecs); + xfs.next = fsb2daddr (le64(h.bb_rightsib)); + xfs.fpos = sizeof(xfs_btree_block_t); + } + /* Yeah, I know that's slow, but I really don't care */ + devread (xfs.daddr, xfs.fpos, sizeof(xfs_bmbt_rec_t), filebuf); + xfs.xt = (xfs_bmbt_rec_32_t *)filebuf; + xfs.fpos += sizeof(xfs_bmbt_rec_32_t); + } + xad.offset = xt_offset (xfs.xt); + xad.start = xt_start (xfs.xt); + xad.len = xt_len (xfs.xt); + ++xfs.xt; + --xfs.nextents; + + return &xad; +} + +/* + * Name lies - the function reads only first 100 bytes + */ +static void +xfs_dabread (void) +{ + xad_t *xad; + xfs_fileoff_t offset;; + + init_extents (); + while ((xad = next_extent ())) { + offset = xad->offset; + if (isinxt (xfs.dablk, offset, xad->len)) { + devread (fsb2daddr (xad->start + xfs.dablk - offset), + 0, 100, dirbuf); + break; + } + } +} + +static inline xfs_ino_t +sf_ino (char *sfe, int namelen) +{ + void *p = sfe + namelen + 3; + + return (xfs.i8param == 0) + ? le64(*(xfs_ino_t *)p) : le32(*(__uint32_t *)p); +} + +static inline xfs_ino_t +sf_parent_ino (void) +{ + return (xfs.i8param == 0) + ? le64(*(xfs_ino_t *)(&inode->di_u.di_dir2sf.hdr.parent)) + : le32(*(__uint32_t *)(&inode->di_u.di_dir2sf.hdr.parent)); +} + +static inline int +roundup8 (int n) +{ + return ((n+7)&~7); +} + +static char * +next_dentry (xfs_ino_t *ino) +{ + int namelen = 1; + int toread; + static char *usual[2] = {".", ".."}; + static xfs_dir2_sf_entry_t *sfe; + char *name = usual[0]; + + if (xfs.dirpos >= xfs.dirmax) { + if (xfs.forw == 0) + return NULL; + xfs.dablk = xfs.forw; + xfs_dabread (); +#define h ((xfs_dir2_leaf_hdr_t *)dirbuf) + xfs.dirmax = le16 (h->count) - le16 (h->stale); + xfs.forw = le32 (h->info.forw); +#undef h + xfs.dirpos = 0; + } + + switch (icore.di_format) { + case XFS_DINODE_FMT_LOCAL: + switch (xfs.dirpos) { + case -2: + *ino = 0; + break; + case -1: + *ino = sf_parent_ino (); + ++name; + ++namelen; + sfe = (xfs_dir2_sf_entry_t *) + (inode->di_u.di_c + + sizeof(xfs_dir2_sf_hdr_t) + - xfs.i8param); + break; + default: + namelen = sfe->namelen; + *ino = sf_ino ((char *)sfe, namelen); + name = sfe->name; + sfe = (xfs_dir2_sf_entry_t *) + ((char *)sfe + namelen + 11 - xfs.i8param); + } + break; + case XFS_DINODE_FMT_BTREE: + case XFS_DINODE_FMT_EXTENTS: +#define dau ((xfs_dir2_data_union_t *)dirbuf) + for (;;) { + if (xfs.blkoff >= xfs.dirbsize) { + xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); + filepos &= ~(xfs.dirbsize - 1); + filepos |= xfs.blkoff; + } + xfs_read (dirbuf, 4); + xfs.blkoff += 4; + if (dau->unused.freetag == XFS_DIR2_DATA_FREE_TAG) { + toread = roundup8 (le16(dau->unused.length)) - 4; + xfs.blkoff += toread; + filepos += toread; + continue; + } + break; + } + xfs_read ((char *)dirbuf + 4, 5); + *ino = le64 (dau->entry.inumber); + namelen = dau->entry.namelen; +#undef dau + toread = roundup8 (namelen + 11) - 9; + xfs_read (dirbuf, toread); + name = (char *)dirbuf; + xfs.blkoff += toread + 5; + } + ++xfs.dirpos; + name[namelen] = 0; + + return name; +} + +static char * +first_dentry (xfs_ino_t *ino) +{ + xfs.forw = 0; + switch (icore.di_format) { + case XFS_DINODE_FMT_LOCAL: + xfs.dirmax = inode->di_u.di_dir2sf.hdr.count; + xfs.i8param = inode->di_u.di_dir2sf.hdr.i8count ? 0 : 4; + xfs.dirpos = -2; + break; + case XFS_DINODE_FMT_EXTENTS: + case XFS_DINODE_FMT_BTREE: + filepos = 0; + xfs_read (dirbuf, sizeof(xfs_dir2_data_hdr_t)); + if (((xfs_dir2_data_hdr_t *)dirbuf)->magic == le32(XFS_DIR2_BLOCK_MAGIC)) { +#define tail ((xfs_dir2_block_tail_t *)dirbuf) + filepos = xfs.dirbsize - sizeof(*tail); + xfs_read (dirbuf, sizeof(*tail)); + xfs.dirmax = le32 (tail->count) - le32 (tail->stale); +#undef tail + } else { + xfs.dablk = (1ULL << 35) >> xfs.blklog; +#define h ((xfs_dir2_leaf_hdr_t *)dirbuf) +#define n ((xfs_da_intnode_t *)dirbuf) + for (;;) { + xfs_dabread (); + if ((n->hdr.info.magic == le16(XFS_DIR2_LEAFN_MAGIC)) + || (n->hdr.info.magic == le16(XFS_DIR2_LEAF1_MAGIC))) { + xfs.dirmax = le16 (h->count) - le16 (h->stale); + xfs.forw = le32 (h->info.forw); + break; + } + xfs.dablk = le32 (n->btree[0].before); + } +#undef n +#undef h + } + xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); + filepos = xfs.blkoff; + xfs.dirpos = 0; + } + return next_dentry (ino); +} + +int +xfs_mount (void) +{ + xfs_sb_t super; + + if (!devread (0, 0, sizeof(super), (char *)&super) + || (le32(super.sb_magicnum) != XFS_SB_MAGIC) + || ((le16(super.sb_versionnum) + & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4) ) { + return 0; + } + + xfs.bsize = le32 (super.sb_blocksize); + xfs.blklog = super.sb_blocklog; + xfs.bdlog = xfs.blklog - SECTOR_BITS; + xfs.rootino = le64 (super.sb_rootino); + xfs.isize = le16 (super.sb_inodesize); + xfs.agblocks = le32 (super.sb_agblocks); + xfs.dirbsize = xfs.bsize << super.sb_dirblklog; + + xfs.inopblog = super.sb_inopblog; + xfs.agblklog = super.sb_agblklog; + xfs.agnolog = xfs_highbit32 (le32(super.sb_agcount)); + + xfs.btnode_ptr0_off = + ((xfs.bsize - sizeof(xfs_btree_block_t)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t))) + * sizeof(xfs_bmbt_key_t) + sizeof(xfs_btree_block_t); + + return 1; +} + +int +xfs_read (char *buf, int len) +{ + xad_t *xad; + xfs_fileoff_t endofprev, endofcur, offset; + xfs_filblks_t xadlen; + int toread, startpos, endpos; + + if (icore.di_format == XFS_DINODE_FMT_LOCAL) { + grub_memmove (buf, inode->di_u.di_c + filepos, len); + filepos += len; + return len; + } + + startpos = filepos; + endpos = filepos + len; + endofprev = (xfs_fileoff_t)-1; + init_extents (); + while (len > 0 && (xad = next_extent ())) { + offset = xad->offset; + xadlen = xad->len; + if (isinxt (filepos >> xfs.blklog, offset, xadlen)) { + endofcur = (offset + xadlen) << xfs.blklog; + toread = (endofcur >= endpos) + ? len : (endofcur - filepos); + + disk_read_func = disk_read_hook; + devread (fsb2daddr (xad->start), + filepos - (offset << xfs.blklog), toread, buf); + disk_read_func = NULL; + + buf += toread; + len -= toread; + filepos += toread; + } else if (offset > endofprev) { + toread = ((offset << xfs.blklog) >= endpos) + ? len : ((offset - endofprev) << xfs.blklog); + len -= toread; + filepos += toread; + for (; toread; toread--) { + *buf++ = 0; + } + continue; + } + endofprev = offset + xadlen; + } + + return filepos - startpos; +} + +int +xfs_dir (char *dirname) +{ + xfs_ino_t ino, parent_ino, new_ino; + xfs_fsize_t di_size; + int di_mode; + int cmp, n, link_count; + char linkbuf[xfs.bsize]; + char *rest, *name, ch; + + parent_ino = ino = xfs.rootino; + link_count = 0; + for (;;) { + di_read (ino); + di_size = le64 (icore.di_size); + di_mode = le16 (icore.di_mode); + + if ((di_mode & IFMT) == IFLNK) { + if (++link_count > MAX_LINK_COUNT) { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + if (di_size < xfs.bsize - 1) { + filepos = 0; + filemax = di_size; + n = xfs_read (linkbuf, filemax); + } else { + errnum = ERR_FILELENGTH; + return 0; + } + + ino = (linkbuf[0] == '/') ? xfs.rootino : parent_ino; + while (n < (xfs.bsize - 1) && (linkbuf[n++] = *dirname++)); + linkbuf[n] = 0; + dirname = linkbuf; + continue; + } + + if (!*dirname || isspace (*dirname)) { + if ((di_mode & IFMT) != IFREG) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + filepos = 0; + filemax = di_size; + return 1; + } + + if ((di_mode & IFMT) != IFDIR) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + for (; *dirname == '/'; dirname++); + + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + *rest = 0; + + name = first_dentry (&new_ino); + for (;;) { + cmp = (!*dirname) ? -1 : substring (dirname, name); +#ifndef STAGE1_5 + if (print_possibilities && ch != '/' && cmp <= 0) { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (name); + } else +#endif + if (cmp == 0) { + parent_ino = ino; + if (new_ino) + ino = new_ino; + *(dirname = rest) = ch; + break; + } + name = next_dentry (&new_ino); + if (name == NULL) { + if (print_possibilities < 0) + return 1; + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + } + } + } +} + +#endif /* FSYS_XFS */ diff --git a/src/filo/fs/iso9660.h b/src/filo/fs/iso9660.h new file mode 100644 index 000000000..06a79062f --- /dev/null +++ b/src/filo/fs/iso9660.h @@ -0,0 +1,168 @@ +/* + * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) + * including Rock Ridge Extensions support + * + * Copyright (C) 1998, 1999 Kousuke Takai <tak@kmc.kyoto-u.ac.jp> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * References: + * linux/fs/isofs/rock.[ch] + * mkisofs-1.11.1/diag/isoinfo.c + * mkisofs-1.11.1/iso9660.h + * (all are written by Eric Youngdale) + */ + +/* + * Modified by SONE Takeshi to work with FILO + */ + +#ifndef _ISO9660_H_ +#define _ISO9660_H_ + +#define ISO_SECTOR_BITS (11) +#define ISO_SECTOR_SIZE (1<<ISO_SECTOR_BITS) + +#define ISO_REGULAR 1 /* regular file */ +#define ISO_DIRECTORY 2 /* directory */ +#define ISO_OTHER 0 /* other file (with Rock Ridge) */ + +#define RR_FLAG_PX 0x01 /* have POSIX file attributes */ +#define RR_FLAG_NM 0x08 /* have alternate file name */ + +/* POSIX file attributes for Rock Ridge extensions */ +#define POSIX_S_IFMT 0xF000 +#define POSIX_S_IFREG 0x8000 +#define POSIX_S_IFDIR 0x4000 + +/* volume descriptor types */ +#define ISO_VD_PRIMARY 1 +#define ISO_VD_END 255 + +#define ISO_STANDARD_ID "CD001" + +#ifndef ASM_FILE + +typedef uint8_t u_int8_t; +typedef uint16_t u_int16_t; +typedef uint32_t u_int32_t; + +typedef union { + u_int8_t l,b; +} iso_8bit_t; + +typedef struct __iso_16bit { + u_int16_t l, b; +} iso_16bit_t __attribute__ ((packed)); + +typedef struct __iso_32bit { + u_int32_t l, b; +} iso_32bit_t __attribute__ ((packed)); + +typedef u_int8_t iso_date_t[7]; + +struct iso_directory_record { + iso_8bit_t length; + iso_8bit_t ext_attr_length; + iso_32bit_t extent; + iso_32bit_t size; + iso_date_t date; + iso_8bit_t flags; + iso_8bit_t file_unit_size; + iso_8bit_t interleave; + iso_16bit_t volume_seq_number; + iso_8bit_t name_len; + u_int8_t name[1]; +} __attribute__ ((packed)); + +struct iso_primary_descriptor { + iso_8bit_t type; + u_int8_t id[5]; + iso_8bit_t version; + u_int8_t _unused1[1]; + u_int8_t system_id[32]; + u_int8_t volume_id[32]; + u_int8_t _unused2[8]; + iso_32bit_t volume_space_size; + u_int8_t _unused3[32]; + iso_16bit_t volume_set_size; + iso_16bit_t volume_seq_number; + iso_16bit_t logical_block_size; + iso_32bit_t path_table_size; + u_int8_t type_l_path_table[4]; + u_int8_t opt_type_l_path_table[4]; + u_int8_t type_m_path_table[4]; + u_int8_t opt_type_m_path_table[4]; + struct iso_directory_record root_directory_record; + u_int8_t volume_set_id[128]; + u_int8_t publisher_id[128]; + u_int8_t preparer_id[128]; + u_int8_t application_id[128]; + u_int8_t copyright_file_id[37]; + u_int8_t abstract_file_id[37]; + u_int8_t bibliographic_file_id[37]; + u_int8_t creation_date[17]; + u_int8_t modification_date[17]; + u_int8_t expiration_date[17]; + u_int8_t effective_date[17]; + iso_8bit_t file_structure_version; + u_int8_t _unused4[1]; + u_int8_t application_data[512]; + u_int8_t _unused5[653]; +} __attribute__ ((packed)); + +struct rock_ridge { + u_int16_t signature; + u_int8_t len; + u_int8_t version; + union { + struct CE { + iso_32bit_t extent; + iso_32bit_t offset; + iso_32bit_t size; + } ce; + struct NM { + iso_8bit_t flags; + u_int8_t name[0]; + } nm; + struct PX { + iso_32bit_t mode; + iso_32bit_t nlink; + iso_32bit_t uid; + iso_32bit_t gid; + } px; + struct RR { + iso_8bit_t flags; + } rr; + } u; +} __attribute__ ((packed)); + +typedef union RR_ptr { + struct rock_ridge *rr; + char *ptr; + int i; +} RR_ptr_t; + +#define RRMAGIC(c1, c2) ((c1)|(c2) << 8) + +#define CHECK2(ptr, c1, c2) \ + (*(unsigned short *)(ptr) == (((c1) | (c2) << 8) & 0xFFFF)) +#define CHECK4(ptr, c1, c2, c3, c4) \ + (*(unsigned long *)(ptr) == ((c1) | (c2)<<8 | (c3)<<16 | (c4)<<24)) + +#endif /* !ASM_FILE */ + +#endif /* _ISO9660_H_ */ diff --git a/src/filo/fs/jfs.h b/src/filo/fs/jfs.h new file mode 100644 index 000000000..e596a1b8b --- /dev/null +++ b/src/filo/fs/jfs.h @@ -0,0 +1,601 @@ +/* jfs.h - an extractions from linux/include/linux/jfs/jfs* into one file */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 International Business Machines Corp. + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _JFS_H_ +#define _JFS_H_ + +/* those are from jfs_filsys.h */ + +/* + * file system option (superblock flag) + */ +/* platform option (conditional compilation) */ +#define JFS_AIX 0x80000000 /* AIX support */ +/* POSIX name/directory support */ + +#define JFS_OS2 0x40000000 /* OS/2 support */ +/* case-insensitive name/directory support */ + +#define JFS_LINUX 0x10000000 /* Linux support */ +/* case-sensitive name/directory support */ + +/* directory option */ +#define JFS_UNICODE 0x00000001 /* unicode name */ + +/* bba */ +#define JFS_SWAP_BYTES 0x00100000 /* running on big endian computer */ + + +/* + * buffer cache configuration + */ +/* page size */ +#ifdef PSIZE +#undef PSIZE +#endif +#define PSIZE 4096 /* page size (in byte) */ + +/* + * fs fundamental size + * + * PSIZE >= file system block size >= PBSIZE >= DISIZE + */ +#define PBSIZE 512 /* physical block size (in byte) */ +#define DISIZE 512 /* on-disk inode size (in byte) */ +#define L2DISIZE 9 +#define INOSPERIAG 4096 /* number of disk inodes per iag */ +#define L2INOSPERIAG 12 +#define INOSPEREXT 32 /* number of disk inode per extent */ +#define L2INOSPEREXT 5 + +/* Minimum number of bytes supported for a JFS partition */ +#define MINJFS (0x1000000) + +/* + * fixed byte offset address + */ +#define SUPER1_OFF 0x8000 /* primary superblock */ + +#define AITBL_OFF (SUPER1_OFF + PSIZE + (PSIZE << 1)) + +/* + * fixed reserved inode number + */ +/* aggregate inode */ +#define AGGREGATE_I 1 /* aggregate inode map inode */ +#define FILESYSTEM_I 16 /* 1st/only fileset inode in ait: + * fileset inode map inode + */ + +/* per fileset inode */ +#define ROOT_I 2 /* fileset root inode */ + +/* + * directory configuration + */ +#define JFS_NAME_MAX 255 +#define JFS_PATH_MAX PSIZE + +typedef unsigned char u8; +typedef char s8; +typedef unsigned short u16; +typedef short s16; +typedef unsigned int u32; +typedef int s32; +typedef unsigned long long u64; +typedef long long s64; + +typedef u16 UniChar; + +/* these from jfs_btree.h */ + +/* btpaget_t flag */ +#define BT_TYPE 0x07 /* B+-tree index */ +#define BT_ROOT 0x01 /* root page */ +#define BT_LEAF 0x02 /* leaf page */ +#define BT_INTERNAL 0x04 /* internal page */ +#define BT_RIGHTMOST 0x10 /* rightmost page */ +#define BT_LEFTMOST 0x20 /* leftmost page */ + +/* those are from jfs_types.h */ + +struct timestruc_t { + u32 tv_sec; + u32 tv_nsec; +}; + +/* + * physical xd (pxd) + */ +typedef struct { + unsigned len:24; + unsigned addr1:8; + u32 addr2; +} pxd_t; + +/* xd_t field extraction */ +#define lengthPXD(pxd) ((pxd)->len) +#define addressPXD(pxd) (((s64)((pxd)->addr1)) << 32 | ((pxd)->addr2)) + +/* + * data extent descriptor (dxd) + */ +typedef struct { + unsigned flag:8; /* 1: flags */ + unsigned rsrvd:24; /* 3: */ + u32 size; /* 4: size in byte */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} dxd_t; /* - 16 - */ + +/* + * DASD limit information - stored in directory inode + */ +typedef struct dasd { + u8 thresh; /* Alert Threshold (in percent) */ + u8 delta; /* Alert Threshold delta (in percent) */ + u8 rsrvd1; + u8 limit_hi; /* DASD limit (in logical blocks) */ + u32 limit_lo; /* DASD limit (in logical blocks) */ + u8 rsrvd2[3]; + u8 used_hi; /* DASD usage (in logical blocks) */ + u32 used_lo; /* DASD usage (in logical blocks) */ +} dasd_t; + + +/* from jfs_superblock.h */ + +#define JFS_MAGIC 0x3153464A /* "JFS1" */ + +struct jfs_superblock +{ + u32 s_magic; /* 4: magic number */ + u32 s_version; /* 4: version number */ + + s64 s_size; /* 8: aggregate size in hardware/LVM blocks; + * VFS: number of blocks + */ + s32 s_bsize; /* 4: aggregate block size in bytes; + * VFS: fragment size + */ + s16 s_l2bsize; /* 2: log2 of s_bsize */ + s16 s_l2bfactor; /* 2: log2(s_bsize/hardware block size) */ + s32 s_pbsize; /* 4: hardware/LVM block size in bytes */ + s16 s_l2pbsize; /* 2: log2 of s_pbsize */ + s16 pad; /* 2: padding necessary for alignment */ + + u32 s_agsize; /* 4: allocation group size in aggr. blocks */ + + u32 s_flag; /* 4: aggregate attributes: + * see jfs_filsys.h + */ + u32 s_state; /* 4: mount/unmount/recovery state: + * see jfs_filsys.h + */ + s32 s_compress; /* 4: > 0 if data compression */ + + pxd_t s_ait2; /* 8: first extent of secondary + * aggregate inode table + */ + + pxd_t s_aim2; /* 8: first extent of secondary + * aggregate inode map + */ + u32 s_logdev; /* 4: device address of log */ + s32 s_logserial; /* 4: log serial number at aggregate mount */ + pxd_t s_logpxd; /* 8: inline log extent */ + + pxd_t s_fsckpxd; /* 8: inline fsck work space extent */ + + struct timestruc_t s_time; /* 8: time last updated */ + + s32 s_fsckloglen; /* 4: Number of filesystem blocks reserved for + * the fsck service log. + * N.B. These blocks are divided among the + * versions kept. This is not a per + * version size. + * N.B. These blocks are included in the + * length field of s_fsckpxd. + */ + s8 s_fscklog; /* 1: which fsck service log is most recent + * 0 => no service log data yet + * 1 => the first one + * 2 => the 2nd one + */ + char s_fpack[11]; /* 11: file system volume name + * N.B. This must be 11 bytes to + * conform with the OS/2 BootSector + * requirements + */ + + /* extendfs() parameter under s_state & FM_EXTENDFS */ + s64 s_xsize; /* 8: extendfs s_size */ + pxd_t s_xfsckpxd; /* 8: extendfs fsckpxd */ + pxd_t s_xlogpxd; /* 8: extendfs logpxd */ + /* - 128 byte boundary - */ + + /* + * DFS VFS support (preliminary) + */ + char s_attach; /* 1: VFS: flag: set when aggregate is attached + */ + u8 rsrvd4[7]; /* 7: reserved - set to 0 */ + + u64 totalUsable; /* 8: VFS: total of 1K blocks which are + * available to "normal" (non-root) users. + */ + u64 minFree; /* 8: VFS: # of 1K blocks held in reserve for + * exclusive use of root. This value can be 0, + * and if it is then totalUsable will be equal + * to # of blocks in aggregate. I believe this + * means that minFree + totalUsable = # blocks. + * In that case, we don't need to store both + * totalUsable and minFree since we can compute + * one from the other. I would guess minFree + * would be the one we should store, and + * totalUsable would be the one we should + * compute. (Just a guess...) + */ + + u64 realFree; /* 8: VFS: # of free 1K blocks can be used by + * "normal" users. It may be this is something + * we should compute when asked for instead of + * storing in the superblock. I don't know how + * often this information is needed. + */ + /* + * graffiti area + */ +}; + +/* from jfs_dtree.h */ + +/* + * entry segment/slot + * + * an entry consists of type dependent head/only segment/slot and + * additional segments/slots linked vi next field; + * N.B. last/only segment of entry is terminated by next = -1; + */ +/* + * directory page slot + */ +typedef struct { + s8 next; /* 1: */ + s8 cnt; /* 1: */ + UniChar name[15]; /* 30: */ +} dtslot_t; /* (32) */ + +#define DTSLOTDATALEN 15 + +/* + * internal node entry head/only segment + */ +typedef struct { + pxd_t xd; /* 8: child extent descriptor */ + + s8 next; /* 1: */ + u8 namlen; /* 1: */ + UniChar name[11]; /* 22: 2-byte aligned */ +} idtentry_t; /* (32) */ + +/* + * leaf node entry head/only segment + * + * For legacy filesystems, name contains 13 unichars -- no index field + */ +typedef struct { + u32 inumber; /* 4: 4-byte aligned */ + s8 next; /* 1: */ + u8 namlen; /* 1: */ + UniChar name[11]; /* 22: 2-byte aligned */ + u32 index; /* 4: index into dir_table */ +} ldtentry_t; /* (32) */ + +#define DTLHDRDATALEN 11 + +/* + * dir_table used for directory traversal during readdir +*/ + +/* + * Maximum entry in inline directory table + */ + +typedef struct dir_table_slot { + u8 rsrvd; /* 1: */ + u8 flag; /* 1: 0 if free */ + u8 slot; /* 1: slot within leaf page of entry */ + u8 addr1; /* 1: upper 8 bits of leaf page address */ + u32 addr2; /* 4: lower 32 bits of leaf page address -OR- + index of next entry when this entry was deleted */ +} dir_table_slot_t; /* (8) */ + +/* + * directory root page (in-line in on-disk inode): + * + * cf. dtpage_t below. + */ +typedef union { + struct { + dasd_t DASD; /* 16: DASD limit/usage info F226941 */ + + u8 flag; /* 1: */ + s8 nextindex; /* 1: next free entry in stbl */ + s8 freecnt; /* 1: free count */ + s8 freelist; /* 1: freelist header */ + + u32 idotdot; /* 4: parent inode number */ + + s8 stbl[8]; /* 8: sorted entry index table */ + } header; /* (32) */ + + dtslot_t slot[9]; +} dtroot_t; + +/* + * directory regular page: + * + * entry slot array of 32 byte slot + * + * sorted entry slot index table (stbl): + * contiguous slots at slot specified by stblindex, + * 1-byte per entry + * 512 byte block: 16 entry tbl (1 slot) + * 1024 byte block: 32 entry tbl (1 slot) + * 2048 byte block: 64 entry tbl (2 slot) + * 4096 byte block: 128 entry tbl (4 slot) + * + * data area: + * 512 byte block: 16 - 2 = 14 slot + * 1024 byte block: 32 - 2 = 30 slot + * 2048 byte block: 64 - 3 = 61 slot + * 4096 byte block: 128 - 5 = 123 slot + * + * N.B. index is 0-based; index fields refer to slot index + * except nextindex which refers to entry index in stbl; + * end of entry stot list or freelist is marked with -1. + */ +typedef union { + struct { + s64 next; /* 8: next sibling */ + s64 prev; /* 8: previous sibling */ + + u8 flag; /* 1: */ + s8 nextindex; /* 1: next entry index in stbl */ + s8 freecnt; /* 1: */ + s8 freelist; /* 1: slot index of head of freelist */ + + u8 maxslot; /* 1: number of slots in page slot[] */ + s8 stblindex; /* 1: slot index of start of stbl */ + u8 rsrvd[2]; /* 2: */ + + pxd_t self; /* 8: self pxd */ + } header; /* (32) */ + + dtslot_t slot[128]; +} dtpage_t; + +/* from jfs_xtree.h */ + +/* + * extent allocation descriptor (xad) + */ +typedef struct xad { + unsigned flag:8; /* 1: flag */ + unsigned rsvrd:16; /* 2: reserved */ + unsigned off1:8; /* 1: offset in unit of fsblksize */ + u32 off2; /* 4: offset in unit of fsblksize */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} xad_t; /* (16) */ + +/* xad_t field extraction */ +#define offsetXAD(xad) (((s64)((xad)->off1)) << 32 | ((xad)->off2)) +#define addressXAD(xad) (((s64)((xad)->addr1)) << 32 | ((xad)->addr2)) +#define lengthXAD(xad) ((xad)->len) + +/* possible values for maxentry */ +#define XTPAGEMAXSLOT 256 +#define XTENTRYSTART 2 + +/* + * xtree page: + */ +typedef union { + struct xtheader { + s64 next; /* 8: */ + s64 prev; /* 8: */ + + u8 flag; /* 1: */ + u8 rsrvd1; /* 1: */ + s16 nextindex; /* 2: next index = number of entries */ + s16 maxentry; /* 2: max number of entries */ + s16 rsrvd2; /* 2: */ + + pxd_t self; /* 8: self */ + } header; /* (32) */ + + xad_t xad[XTPAGEMAXSLOT]; /* 16 * maxentry: xad array */ +} xtpage_t; + +/* from jfs_dinode.h */ + +struct dinode { + /* + * I. base area (128 bytes) + * ------------------------ + * + * define generic/POSIX attributes + */ + u32 di_inostamp; /* 4: stamp to show inode belongs to fileset */ + s32 di_fileset; /* 4: fileset number */ + u32 di_number; /* 4: inode number, aka file serial number */ + u32 di_gen; /* 4: inode generation number */ + + pxd_t di_ixpxd; /* 8: inode extent descriptor */ + + s64 di_size; /* 8: size */ + s64 di_nblocks; /* 8: number of blocks allocated */ + + u32 di_nlink; /* 4: number of links to the object */ + + u32 di_uid; /* 4: user id of owner */ + u32 di_gid; /* 4: group id of owner */ + + u32 di_mode; /* 4: attribute, format and permission */ + + struct timestruc_t di_atime; /* 8: time last data accessed */ + struct timestruc_t di_ctime; /* 8: time last status changed */ + struct timestruc_t di_mtime; /* 8: time last data modified */ + struct timestruc_t di_otime; /* 8: time created */ + + dxd_t di_acl; /* 16: acl descriptor */ + + dxd_t di_ea; /* 16: ea descriptor */ + + s32 di_next_index; /* 4: Next available dir_table index */ + + s32 di_acltype; /* 4: Type of ACL */ + + /* + * Extension Areas. + * + * Historically, the inode was partitioned into 4 128-byte areas, + * the last 3 being defined as unions which could have multiple + * uses. The first 96 bytes had been completely unused until + * an index table was added to the directory. It is now more + * useful to describe the last 3/4 of the inode as a single + * union. We would probably be better off redesigning the + * entire structure from scratch, but we don't want to break + * commonality with OS/2's JFS at this time. + */ + union { + struct { + /* + * This table contains the information needed to + * find a directory entry from a 32-bit index. + * If the index is small enough, the table is inline, + * otherwise, an x-tree root overlays this table + */ + dir_table_slot_t _table[12]; /* 96: inline */ + + dtroot_t _dtroot; /* 288: dtree root */ + } _dir; /* (384) */ +#define di_dirtable u._dir._table +#define di_dtroot u._dir._dtroot +#define di_parent di_dtroot.header.idotdot +#define di_DASD di_dtroot.header.DASD + + struct { + union { + u8 _data[96]; /* 96: unused */ + struct { + void *_imap; /* 4: unused */ + u32 _gengen; /* 4: generator */ + } _imap; + } _u1; /* 96: */ +#define di_gengen u._file._u1._imap._gengen + + union { + xtpage_t _xtroot; + struct { + u8 unused[16]; /* 16: */ + dxd_t _dxd; /* 16: */ + union { + u32 _rdev; /* 4: */ + u8 _fastsymlink[128]; + } _u; + u8 _inlineea[128]; + } _special; + } _u2; + } _file; +#define di_xtroot u._file._u2._xtroot +#define di_dxd u._file._u2._special._dxd +#define di_btroot di_xtroot +#define di_inlinedata u._file._u2._special._u +#define di_rdev u._file._u2._special._u._rdev +#define di_fastsymlink u._file._u2._special._u._fastsymlink +#define di_inlineea u._file._u2._special._inlineea + } u; +}; + +typedef struct dinode dinode_t; + +/* di_mode */ +#define IFMT 0xF000 /* S_IFMT - mask of file type */ +#define IFDIR 0x4000 /* S_IFDIR - directory */ +#define IFREG 0x8000 /* S_IFREG - regular file */ +#define IFLNK 0xA000 /* S_IFLNK - symbolic link */ + +/* extended mode bits (on-disk inode di_mode) */ +#define INLINEEA 0x00040000 /* inline EA area free */ + +/* from jfs_imap.h */ + +#define EXTSPERIAG 128 /* number of disk inode extent per iag */ +#define SMAPSZ 4 /* number of words per summary map */ +#define MAXAG 128 /* maximum number of allocation groups */ + +/* + * inode allocation map: + * + * inode allocation map consists of + * . the inode map control page and + * . inode allocation group pages (per 4096 inodes) + * which are addressed by standard JFS xtree. + */ +/* + * inode allocation group page (per 4096 inodes of an AG) + */ +typedef struct { + s64 agstart; /* 8: starting block of ag */ + s32 iagnum; /* 4: inode allocation group number */ + s32 inofreefwd; /* 4: ag inode free list forward */ + s32 inofreeback; /* 4: ag inode free list back */ + s32 extfreefwd; /* 4: ag inode extent free list forward */ + s32 extfreeback; /* 4: ag inode extent free list back */ + s32 iagfree; /* 4: iag free list */ + + /* summary map: 1 bit per inode extent */ + s32 inosmap[SMAPSZ]; /* 16: sum map of mapwords w/ free inodes; + * note: this indicates free and backed + * inodes, if the extent is not backed the + * value will be 1. if the extent is + * backed but all inodes are being used the + * value will be 1. if the extent is + * backed but at least one of the inodes is + * free the value will be 0. + */ + s32 extsmap[SMAPSZ]; /* 16: sum map of mapwords w/ free extents */ + s32 nfreeinos; /* 4: number of free inodes */ + s32 nfreeexts; /* 4: number of free extents */ + /* (72) */ + u8 pad[1976]; /* 1976: pad to 2048 bytes */ + /* allocation bit map: 1 bit per inode (0 - free, 1 - allocated) */ + u32 wmap[EXTSPERIAG]; /* 512: working allocation map */ + u32 pmap[EXTSPERIAG]; /* 512: persistent allocation map */ + pxd_t inoext[EXTSPERIAG]; /* 1024: inode extent addresses */ +} iag_t; /* (4096) */ + +#endif /* _JFS_H_ */ diff --git a/src/filo/fs/shared.h b/src/filo/fs/shared.h new file mode 100644 index 000000000..9f2090b3f --- /dev/null +++ b/src/filo/fs/shared.h @@ -0,0 +1 @@ +/* Sorry, nothing is shared here ;) Just for GRUB compatibility. */ diff --git a/src/filo/fs/vfs.c b/src/filo/fs/vfs.c new file mode 100644 index 000000000..9131bf210 --- /dev/null +++ b/src/filo/fs/vfs.c @@ -0,0 +1,193 @@ +/* Interface between GRUB's fs drivers and application code */ +#include <lib.h> + +#include "filesys.h" +#include <fs.h> + +#define DEBUG_THIS DEBUG_VFS +#include <debug.h> + +int filepos; +int filemax; +grub_error_t errnum; +void (*disk_read_hook) (int, int, int); +void (*disk_read_func) (int, int, int); +char FSYS_BUF[FSYS_BUFLEN]; +int fsmax; + +struct fsys_entry { + char *name; + int (*mount_func) (void); + int (*read_func) (char *buf, int len); + int (*dir_func) (char *dirname); + void (*close_func) (void); + int (*embed_func) (int *start_sector, int needed_sectors); +}; + +struct fsys_entry fsys_table[] = { +# ifdef FSYS_FAT + {"fat", fat_mount, fat_read, fat_dir, 0, 0}, +# endif +# ifdef FSYS_EXT2FS + {"ext2fs", ext2fs_mount, ext2fs_read, ext2fs_dir, 0, 0}, +# endif +# ifdef FSYS_MINIX + {"minix", minix_mount, minix_read, minix_dir, 0, 0}, +# endif +# ifdef FSYS_REISERFS + {"reiserfs", reiserfs_mount, reiserfs_read, reiserfs_dir, 0, + reiserfs_embed}, +# endif +# ifdef FSYS_JFS + {"jfs", jfs_mount, jfs_read, jfs_dir, 0, jfs_embed}, +# endif +# ifdef FSYS_XFS + {"xfs", xfs_mount, xfs_read, xfs_dir, 0, 0}, +# endif +# ifdef FSYS_ISO9660 + {"iso9660", iso9660_mount, iso9660_read, iso9660_dir, 0, 0}, +# endif +}; + +/* NULLFS is used to read images from raw device */ +static int nullfs_dir(char *name) +{ + uint64_t dev_size; + + if (name) { + debug("can't have a named file\n"); + return 0; + } + + dev_size = (uint64_t) part_length << 9; + /* GRUB code doesn't like 2GB or bigger files */ + if (dev_size > 0x7fffffff) + dev_size = 0x7fffffff; + filemax = dev_size; + return 1; +} + +static int nullfs_read(char *buf, int len) +{ + if (devread(filepos>>9, filepos&0x1ff, len, buf)) { + filepos += len; + return len; + } else + return 0; +} + +static struct fsys_entry nullfs = + {"nullfs", 0, nullfs_read, nullfs_dir, 0, 0}; + +static struct fsys_entry *fsys; + +int mount_fs(void) +{ + int i; + + for (i = 0; i < sizeof(fsys_table)/sizeof(fsys_table[0]); i++) { + if (fsys_table[i].mount_func()) { + fsys = &fsys_table[i]; + printf("Mounted %s\n", fsys->name); + return 1; + } + } + fsys = 0; + printf("Unknown filesystem type\n"); + return 0; +} + +int file_open(const char *filename) +{ + + char dev[32]; +// char *dev=0; + const char *path; + int len; + int retval = 0; + int reopen; + + path = strchr(filename, ':'); + if (path) { + len = path - filename; + path++; + //dev = malloc(len + 1); + memcpy(dev, filename, len); + dev[len] = '\0'; + } else { + /* No colon is given. Is this device or filename? */ + if (filename[0] == '/') { + /* Anything starts with '/' must be a filename */ + // dev = 0; + dev[0]=0; + + path = filename; + } else { + memcpy(dev, filename, 32); +// dev = strdup(filename); + path = 0; + } + } + debug("dev=%s, path=%s\n", dev, path); + + if (dev && dev[0]) { + if (!devopen(dev, &reopen)) { + fsys = 0; + goto out; + } + if (!reopen) + fsys = 0; + } + + if (path) { + if (!fsys || fsys==&nullfs) { + if (!mount_fs()) + goto out; + } + using_devsize = 0; + if (!path[0]) { + printf("No filename is given\n"); + goto out; + } + } else + fsys = &nullfs; + + filepos = 0; + errnum = 0; + if (!fsys->dir_func((char *) path)) { + printf("errnum=%d\n",errnum); +// printf("File not found\n"); + goto out; + } + retval = 1; +out: +// if (dev) +// free(dev); + return retval; +} + +int file_read(void *buf, unsigned long len) +{ + if (filepos < 0 || filepos > filemax) + filepos = filemax; + if (len < 0 || len > filemax-filepos) + len = filemax - filepos; + errnum = 0; + return fsys->read_func(buf, len); +} + +int file_seek(unsigned long offset) +{ + filepos = offset; + return filepos; +} + +unsigned long file_size(void) +{ + return filemax; +} + +void file_close(void) +{ +} + diff --git a/src/filo/fs/xfs.h b/src/filo/fs/xfs.h new file mode 100644 index 000000000..6a2f8fe0e --- /dev/null +++ b/src/filo/fs/xfs.h @@ -0,0 +1,546 @@ +/* xfs.h - an extraction from xfsprogs-1.3.5/include/xfs* into one file */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#ifndef _BITS_TYPES_H +typedef signed char __int8_t; +typedef unsigned char __uint8_t; +typedef short __int16_t; +typedef unsigned short __uint16_t; +typedef int __int32_t; +typedef unsigned int __uint32_t; +typedef long long __int64_t; +typedef unsigned long long __uint64_t; +#endif + +typedef __uint64_t xfs_ino_t; +typedef __uint32_t xfs_agino_t; +typedef __int64_t xfs_daddr_t; +typedef __int64_t xfs_off_t; +typedef __uint8_t uuid_t[16]; + + +/* those are from xfs_types.h */ + +typedef __uint32_t xfs_agblock_t; /* blockno in alloc. group */ +typedef __uint32_t xfs_extlen_t; /* extent length in blocks */ +typedef __uint32_t xfs_agnumber_t; /* allocation group number */ +typedef __int32_t xfs_extnum_t; /* # of extents in a file */ +typedef __int16_t xfs_aextnum_t; /* # extents in an attribute fork */ +typedef __int64_t xfs_fsize_t; /* bytes in a file */ + +typedef __uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ +typedef __uint32_t xfs_dahash_t; /* dir/attr hash value */ + +/* + * Disk based types: + */ +typedef __uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */ +typedef __uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */ +typedef __uint64_t xfs_drtbno_t; /* extent (block) in realtime area */ +typedef __uint64_t xfs_dfiloff_t; /* block number in a file */ + +typedef __uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef __uint64_t xfs_fileoff_t; /* block number in a file */ +typedef __uint64_t xfs_filblks_t; /* number of blocks in a file */ + + +/* those are from xfs_sb.h */ + +#define XFS_SB_MAGIC 0x58465342 /* 'XFSB'*/ +#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */ +#define XFS_SB_VERSION_NUMBITS 0x000f + +typedef struct xfs_sb +{ + __uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ + __uint32_t sb_blocksize; /* logical block size, bytes */ + xfs_drfsbno_t sb_dblocks; /* number of data blocks */ + xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */ + xfs_drtbno_t sb_rextents; /* number of realtime extents */ + uuid_t sb_uuid; /* file system unique id */ + xfs_dfsbno_t sb_logstart; /* starting block of log if internal */ + xfs_ino_t sb_rootino; /* root inode number */ + xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */ + xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */ + xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */ + xfs_agblock_t sb_agblocks; /* size of an allocation group */ + xfs_agnumber_t sb_agcount; /* number of allocation groups */ + xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */ + xfs_extlen_t sb_logblocks; /* number of log blocks */ + __uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ + __uint16_t sb_sectsize; /* volume sector size, bytes */ + __uint16_t sb_inodesize; /* inode size, bytes */ + __uint16_t sb_inopblock; /* inodes per block */ + char sb_fname[12]; /* file system name */ + __uint8_t sb_blocklog; /* log2 of sb_blocksize */ + __uint8_t sb_sectlog; /* log2 of sb_sectsize */ + __uint8_t sb_inodelog; /* log2 of sb_inodesize */ + __uint8_t sb_inopblog; /* log2 of sb_inopblock */ + __uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ + __uint8_t sb_rextslog; /* log2 of sb_rextents */ + __uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ + __uint8_t sb_imax_pct; /* max % of fs for inode space */ + /* statistics */ + /* + * These fields must remain contiguous. If you really + * want to change their layout, make sure you fix the + * code in xfs_trans_apply_sb_deltas(). + */ + __uint64_t sb_icount; /* allocated inodes */ + __uint64_t sb_ifree; /* free inodes */ + __uint64_t sb_fdblocks; /* free data blocks */ + __uint64_t sb_frextents; /* free realtime extents */ + /* + * End contiguous fields. + */ + xfs_ino_t sb_uquotino; /* user quota inode */ + xfs_ino_t sb_gquotino; /* group quota inode */ + __uint16_t sb_qflags; /* quota flags */ + __uint8_t sb_flags; /* misc. flags */ + __uint8_t sb_shared_vn; /* shared version number */ + xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */ + __uint32_t sb_unit; /* stripe or raid unit */ + __uint32_t sb_width; /* stripe or raid width */ + __uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */ + __uint8_t sb_dummy[7]; /* padding */ +} xfs_sb_t; + + +/* those are from xfs_btree.h */ + +/* + * Long form header: bmap btrees. + */ +typedef struct xfs_btree_lblock +{ + __uint32_t bb_magic; /* magic number for block type */ + __uint16_t bb_level; /* 0 is a leaf */ + __uint16_t bb_numrecs; /* current # of data records */ + xfs_dfsbno_t bb_leftsib; /* left sibling block or NULLDFSBNO */ + xfs_dfsbno_t bb_rightsib; /* right sibling block or NULLDFSBNO */ +} xfs_btree_lblock_t; + +/* + * Combined header and structure, used by common code. + */ +typedef struct xfs_btree_hdr +{ + __uint32_t bb_magic; /* magic number for block type */ + __uint16_t bb_level; /* 0 is a leaf */ + __uint16_t bb_numrecs; /* current # of data records */ +} xfs_btree_hdr_t; + +typedef struct xfs_btree_block +{ + xfs_btree_hdr_t bb_h; /* header */ + union { + struct { + xfs_agblock_t bb_leftsib; + xfs_agblock_t bb_rightsib; + } s; /* short form pointers */ + struct { + xfs_dfsbno_t bb_leftsib; + xfs_dfsbno_t bb_rightsib; + } l; /* long form pointers */ + } bb_u; /* rest */ +} xfs_btree_block_t; + +/* those are from xfs_bmap_btree.h */ + +/* + * Bmap root header, on-disk form only. + */ +typedef struct xfs_bmdr_block +{ + __uint16_t bb_level; /* 0 is a leaf */ + __uint16_t bb_numrecs; /* current # of data records */ +} xfs_bmdr_block_t; + +/* + * Bmap btree record and extent descriptor. + * For 32-bit kernels, + * l0:31 is an extent flag (value 1 indicates non-normal). + * l0:0-30 and l1:9-31 are startoff. + * l1:0-8, l2:0-31, and l3:21-31 are startblock. + * l3:0-20 are blockcount. + * For 64-bit kernels, + * l0:63 is an extent flag (value 1 indicates non-normal). + * l0:9-62 are startoff. + * l0:0-8 and l1:21-63 are startblock. + * l1:0-20 are blockcount. + */ + +#define BMBT_USE_64 1 + +typedef struct xfs_bmbt_rec_32 +{ + __uint32_t l0, l1, l2, l3; +} xfs_bmbt_rec_32_t; +typedef struct xfs_bmbt_rec_64 +{ + __uint64_t l0, l1; +} xfs_bmbt_rec_64_t; + +#if BMBT_USE_64 +typedef __uint64_t xfs_bmbt_rec_base_t; /* use this for casts */ +typedef xfs_bmbt_rec_64_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; +#else /* !BMBT_USE_64 */ +typedef __uint32_t xfs_bmbt_rec_base_t; /* use this for casts */ +typedef xfs_bmbt_rec_32_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; +#endif /* BMBT_USE_64 */ + +/* + * Key structure for non-leaf levels of the tree. + */ +typedef struct xfs_bmbt_key +{ + xfs_dfiloff_t br_startoff; /* starting file offset */ +} xfs_bmbt_key_t, xfs_bmdr_key_t; + +typedef xfs_dfsbno_t xfs_bmbt_ptr_t, xfs_bmdr_ptr_t; /* btree pointer type */ + /* btree block header type */ +typedef struct xfs_btree_lblock xfs_bmbt_block_t; + + +/* those are from xfs_dir2.h */ +/* + * Directory version 2. + * There are 4 possible formats: + * shortform + * single block - data with embedded leaf at the end + * multiple data blocks, single leaf+freeindex block + * data blocks, node&leaf blocks (btree), freeindex blocks + * + * The shortform format is in xfs_dir2_sf.h. + * The single block format is in xfs_dir2_block.h. + * The data block format is in xfs_dir2_data.h. + * The leaf and freeindex block formats are in xfs_dir2_leaf.h. + * Node blocks are the same as the other version, in xfs_da_btree.h. + */ + +/* + * Byte offset in data block and shortform entry. + */ +typedef __uint16_t xfs_dir2_data_off_t; + +/* + * Byte offset in a directory. + */ +typedef xfs_off_t xfs_dir2_off_t; + +/* those are from xfs_da_btree.h */ +/*======================================================================== + * Directory Structure when greater than XFS_LBSIZE(mp) bytes. + *========================================================================*/ + +/* + * This structure is common to both leaf nodes and non-leaf nodes in the Btree. + * + * Is is used to manage a doubly linked list of all blocks at the same + * level in the Btree, and to identify which type of block this is. + */ +#define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* magic number: v2 dirlf single blks */ +#define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* magic number: v2 dirlf multi blks */ + +typedef struct xfs_da_blkinfo { + xfs_dablk_t forw; /* previous block in list */ + xfs_dablk_t back; /* following block in list */ + __uint16_t magic; /* validity check on block */ + __uint16_t pad; /* unused */ +} xfs_da_blkinfo_t; + +/* + * This is the structure of the root and intermediate nodes in the Btree. + * The leaf nodes are defined above. + * + * Entries are not packed. + * + * Since we have duplicate keys, use a binary search but always follow + * all match in the block, not just the first match found. + */ + +typedef struct xfs_da_intnode { + struct xfs_da_node_hdr { /* constant-structure header block */ + xfs_da_blkinfo_t info; /* block type, links, etc. */ + __uint16_t count; /* count of active entries */ + __uint16_t level; /* level above leaves (leaf == 0) */ + } hdr; + struct xfs_da_node_entry { + xfs_dahash_t hashval; /* hash value for this descendant */ + xfs_dablk_t before; /* Btree block before this key */ + } btree[1]; /* variable sized array of keys */ +} xfs_da_intnode_t; + + +/* those are from xfs_dir2_data.h */ +/* + * Directory format 2, data block structures. + */ + +/* + * Constants. + */ +#define XFS_DIR2_DATA_FREE_TAG 0xffff +#define XFS_DIR2_DATA_FD_COUNT 3 + +/* + * Structures. + */ + +/* + * Describe a free area in the data block. + * The freespace will be formatted as a xfs_dir2_data_unused_t. + */ +typedef struct xfs_dir2_data_free { + xfs_dir2_data_off_t offset; /* start of freespace */ + xfs_dir2_data_off_t length; /* length of freespace */ +} xfs_dir2_data_free_t; + +/* + * Header for the data blocks. + * Always at the beginning of a directory-sized block. + * The code knows that XFS_DIR2_DATA_FD_COUNT is 3. + */ +typedef struct xfs_dir2_data_hdr { + __uint32_t magic; /* XFS_DIR2_DATA_MAGIC */ + /* or XFS_DIR2_BLOCK_MAGIC */ + xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT]; +} xfs_dir2_data_hdr_t; + +/* + * Active entry in a data block. Aligned to 8 bytes. + * Tag appears as the last 2 bytes. + */ +typedef struct xfs_dir2_data_entry { + xfs_ino_t inumber; /* inode number */ + __uint8_t namelen; /* name length */ + __uint8_t name[1]; /* name bytes, no null */ + /* variable offset */ + xfs_dir2_data_off_t tag; /* starting offset of us */ +} xfs_dir2_data_entry_t; + +/* + * Unused entry in a data block. Aligned to 8 bytes. + * Tag appears as the last 2 bytes. + */ +typedef struct xfs_dir2_data_unused { + __uint16_t freetag; /* XFS_DIR2_DATA_FREE_TAG */ + xfs_dir2_data_off_t length; /* total free length */ + /* variable offset */ + xfs_dir2_data_off_t tag; /* starting offset of us */ +} xfs_dir2_data_unused_t; + +typedef union { + xfs_dir2_data_entry_t entry; + xfs_dir2_data_unused_t unused; +} xfs_dir2_data_union_t; + + +/* those are from xfs_dir2_leaf.h */ +/* + * Directory version 2, leaf block structures. + */ + +/* + * Leaf block header. + */ +typedef struct xfs_dir2_leaf_hdr { + xfs_da_blkinfo_t info; /* header for da routines */ + __uint16_t count; /* count of entries */ + __uint16_t stale; /* count of stale entries */ +} xfs_dir2_leaf_hdr_t; + + +/* those are from xfs_dir2_block.h */ +/* + * xfs_dir2_block.h + * Directory version 2, single block format structures + */ + +/* + * The single block format is as follows: + * xfs_dir2_data_hdr_t structure + * xfs_dir2_data_entry_t and xfs_dir2_data_unused_t structures + * xfs_dir2_leaf_entry_t structures + * xfs_dir2_block_tail_t structure + */ + +#define XFS_DIR2_BLOCK_MAGIC 0x58443242 /* XD2B: for one block dirs */ + +typedef struct xfs_dir2_block_tail { + __uint32_t count; /* count of leaf entries */ + __uint32_t stale; /* count of stale lf entries */ +} xfs_dir2_block_tail_t; + + +/* those are from xfs_dir2_sf.h */ + +/* + * Directory layout when stored internal to an inode. + * + * Small directories are packed as tightly as possible so as to + * fit into the literal area of the inode. + */ + +/* + * Inode number stored as 8 8-bit values. + */ +typedef struct { __uint8_t i[8]; } xfs_dir2_ino8_t; + +/* + * Inode number stored as 4 8-bit values. + * Works a lot of the time, when all the inode numbers in a directory + * fit in 32 bits. + */ +typedef struct { __uint8_t i[4]; } xfs_dir2_ino4_t; + +typedef union { + xfs_dir2_ino8_t i8; + xfs_dir2_ino4_t i4; +} xfs_dir2_inou_t; + +/* + * Normalized offset (in a data block) of the entry, really xfs_dir2_data_off_t. + * Only need 16 bits, this is the byte offset into the single block form. + */ +typedef struct { __uint8_t i[2]; } xfs_dir2_sf_off_t; + +/* + * The parent directory has a dedicated field, and the self-pointer must + * be calculated on the fly. + * + * Entries are packed toward the top as tightly as possible. The header + * and the elements must be bcopy()'d out into a work area to get correct + * alignment for the inode number fields. + */ +typedef struct xfs_dir2_sf_hdr { + __uint8_t count; /* count of entries */ + __uint8_t i8count; /* count of 8-byte inode #s */ + xfs_dir2_inou_t parent; /* parent dir inode number */ +} xfs_dir2_sf_hdr_t; + +typedef struct xfs_dir2_sf_entry { + __uint8_t namelen; /* actual name length */ + xfs_dir2_sf_off_t offset; /* saved offset */ + __uint8_t name[1]; /* name, variable size */ + xfs_dir2_inou_t inumber; /* inode number, var. offset */ +} xfs_dir2_sf_entry_t; + +typedef struct xfs_dir2_sf { + xfs_dir2_sf_hdr_t hdr; /* shortform header */ + xfs_dir2_sf_entry_t list[1]; /* shortform entries */ +} xfs_dir2_sf_t; + +/* those are from xfs_dinode.h */ + +#define XFS_DINODE_VERSION_1 1 +#define XFS_DINODE_VERSION_2 2 +#define XFS_DINODE_MAGIC 0x494e /* 'IN' */ + +/* + * Disk inode structure. + * This is just the header; the inode is expanded to fill a variable size + * with the last field expanding. It is split into the core and "other" + * because we only need the core part in the in-core inode. + */ +typedef struct xfs_timestamp { + __int32_t t_sec; /* timestamp seconds */ + __int32_t t_nsec; /* timestamp nanoseconds */ +} xfs_timestamp_t; + +/* + * Note: Coordinate changes to this structure with the XFS_DI_* #defines + * below and the offsets table in xfs_ialloc_log_di(). + */ +typedef struct xfs_dinode_core +{ + __uint16_t di_magic; /* inode magic # = XFS_DINODE_MAGIC */ + __uint16_t di_mode; /* mode and type of file */ + __int8_t di_version; /* inode version */ + __int8_t di_format; /* format of di_c data */ + __uint16_t di_onlink; /* old number of links to file */ + __uint32_t di_uid; /* owner's user id */ + __uint32_t di_gid; /* owner's group id */ + __uint32_t di_nlink; /* number of links to file */ + __uint16_t di_projid; /* owner's project id */ + __uint8_t di_pad[10]; /* unused, zeroed space */ + xfs_timestamp_t di_atime; /* time last accessed */ + xfs_timestamp_t di_mtime; /* time last modified */ + xfs_timestamp_t di_ctime; /* time created/inode modified */ + xfs_fsize_t di_size; /* number of bytes in file */ + xfs_drfsbno_t di_nblocks; /* # of direct & btree blocks used */ + xfs_extlen_t di_extsize; /* basic/minimum extent size for file */ + xfs_extnum_t di_nextents; /* number of extents in data fork */ + xfs_aextnum_t di_anextents; /* number of extents in attribute fork*/ + __uint8_t di_forkoff; /* attr fork offs, <<3 for 64b align */ + __int8_t di_aformat; /* format of attr fork's data */ + __uint32_t di_dmevmask; /* DMIG event mask */ + __uint16_t di_dmstate; /* DMIG state info */ + __uint16_t di_flags; /* random flags, XFS_DIFLAG_... */ + __uint32_t di_gen; /* generation number */ +} xfs_dinode_core_t; + +typedef struct xfs_dinode +{ + xfs_dinode_core_t di_core; + xfs_agino_t di_next_unlinked;/* agi unlinked list ptr */ + union { + xfs_bmdr_block_t di_bmbt; /* btree root block */ + xfs_bmbt_rec_32_t di_bmx[1]; /* extent list */ + xfs_dir2_sf_t di_dir2sf; /* shortform directory v2 */ + char di_c[1]; /* local contents */ + } di_u; +} xfs_dinode_t; + +/* + * Values for di_format + */ +typedef enum xfs_dinode_fmt +{ + XFS_DINODE_FMT_DEV, /* CHR, BLK: di_dev */ + XFS_DINODE_FMT_LOCAL, /* DIR, REG: di_c */ + /* LNK: di_symlink */ + XFS_DINODE_FMT_EXTENTS, /* DIR, REG, LNK: di_bmx */ + XFS_DINODE_FMT_BTREE, /* DIR, REG, LNK: di_bmbt */ + XFS_DINODE_FMT_UUID /* MNT: di_uuid */ +} xfs_dinode_fmt_t; + +/* + * File types (mode field) + */ +#define IFMT 0170000 /* type of file */ +#define IFDIR 0040000 /* directory */ +#define IFREG 0100000 /* regular */ +#define IFLNK 0120000 /* symbolic link */ diff --git a/src/filo/i386/context.c b/src/filo/i386/context.c new file mode 100644 index 000000000..54a15e354 --- /dev/null +++ b/src/filo/i386/context.c @@ -0,0 +1,125 @@ +/* + * context switching + * 2003-10 by SONE Takeshi + */ +#include <etherboot.h> + +#include "segment.h" +#include "context.h" + +#define MAIN_STACK_SIZE 16384 +#define IMAGE_STACK_SIZE 4096 + +static void start_main(void); /* forward decl. */ +void __exit_context(void); /* assembly routine */ + +/* + * Main context structure + * It is placed at the bottom of our stack, and loaded by assembly routine + * to start us up. + */ +struct context main_ctx __attribute__((section (".initctx"))) = { + .gdt_base = (uint32_t) gdt, + .gdt_limit = GDT_LIMIT, + .cs = FLAT_CS, + .ds = FLAT_DS, + .es = FLAT_DS, + .fs = FLAT_DS, + .gs = FLAT_DS, + .ss = FLAT_DS, + .esp = (uint32_t) ESP_LOC(&main_ctx), + .eip = (uint32_t) start_main, + .return_addr = (uint32_t) __exit_context, +}; + +/* This is used by assembly routine to load/store the context which + * it is to switch/switched. */ +struct context *__context = &main_ctx; + +#if 0 +/* Stack for loaded ELF image */ +static uint8_t image_stack[IMAGE_STACK_SIZE]; +#endif + +/* Pointer to startup context (physical address) */ +unsigned long __boot_ctx; + +/* + * Main starter + * This is the C function that runs first. + */ +static void start_main(void) +{ + int retval; + extern int filo(void); + + /* Save startup context, so we can refer to it later. + * We have to keep it in physical address since we will relocate. */ + __boot_ctx = virt_to_phys(__context); + + /* Start the real fun */ + retval = filo(); + + /* Pass return value to startup context. Bootloader may see it. */ + boot_ctx->eax = retval; + + /* Returning from here should jump to __exit_context */ + __context = boot_ctx; +} + +/* Setup a new context using the given stack. + */ +struct context * +init_context(uint8_t *stack, uint32_t stack_size, int num_params) +{ + struct context *ctx; + + ctx = (struct context *) + (stack + stack_size - (sizeof(*ctx) + num_params*sizeof(uint32_t))); + memset(ctx, 0, sizeof(*ctx)); + + /* Fill in reasonable default for flat memory model */ + ctx->gdt_base = virt_to_phys(gdt); + ctx->gdt_limit = GDT_LIMIT; + ctx->cs = FLAT_CS; + ctx->ds = FLAT_DS; + ctx->es = FLAT_DS; + ctx->fs = FLAT_DS; + ctx->gs = FLAT_DS; + ctx->ss = FLAT_DS; + ctx->esp = virt_to_phys(ESP_LOC(ctx)); + ctx->return_addr = virt_to_phys(__exit_context); + + return ctx; +} + +/* Switch to another context. */ +struct context *switch_to(struct context *ctx) +{ + struct context *save, *ret; + + save = __context; + __context = ctx; + asm ("pushl %cs; call __switch_context"); + ret = __context; + __context = save; + return ret; +} + +#if 0 +//We will use elf_start in Etherboot +/* Start ELF Boot image */ +uint32_t start_elf(uint32_t entry_point, uint32_t param) +{ + struct context *ctx; + + ctx = init_context(image_stack, sizeof image_stack, 1); + ctx->eip = entry_point; + ctx->param[0] = param; + ctx->eax = 0xe1fb007; + ctx->ebx = param; + + ctx = switch_to(ctx); + return ctx->eax; +} +#endif diff --git a/src/filo/i386/context.h b/src/filo/i386/context.h new file mode 100644 index 000000000..b87317950 --- /dev/null +++ b/src/filo/i386/context.h @@ -0,0 +1,50 @@ +#ifndef i386_CONTEXT_H +#define i386_CONTEXT_H + +#include <stdint.h> + +struct context { + /* Stack Segment, placed here because of the alignment issue... */ + uint16_t ss; + /* Used with sgdt/lgdt */ + uint16_t gdt_limit; + uint32_t gdt_base; + /* General registers, accessed with pushal/popal */ + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; /* points just below eax */ + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; +#define ESP_LOC(ctx) (&(ctx)->gs) + /* Segment registers */ + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + /* Flags */ + uint32_t eflags; + /* Code segment:offset */ + uint32_t eip; + uint32_t cs; + /* Optional stack contents */ + uint32_t return_addr; + uint32_t param[0]; +}; + +/* Create a new context in the given stack */ +struct context * +init_context(uint8_t *stack, uint32_t stack_size, int num_param); + +/* Switch context */ +struct context *switch_to(struct context *); + +/* Holds physical address of boot context */ +extern unsigned long __boot_ctx; + +/* This can always be safely used to refer to the boot context */ +#define boot_ctx ((struct context *) phys_to_virt(__boot_ctx)) + +#endif /* i386_CONTEXT_H */ diff --git a/src/filo/i386/linux_load.c b/src/filo/i386/linux_load.c new file mode 100644 index 000000000..42e3edfcc --- /dev/null +++ b/src/filo/i386/linux_load.c @@ -0,0 +1,629 @@ +/* + * Linux/i386 loader + * Supports bzImage, zImage and Image format. + * + * Based on work by Steve Gehlbach. + * Portions are taken from mkelfImage. + * + * 2003-09 by SONE Takeshi + */ +#include <etherboot.h> + +#include <lib.h> + +#include <fs.h> +#include <sys_info.h> + +#include "context.h" +#include "segment.h" + +#define DEBUG_THIS DEBUG_LINUXLOAD +#include <debug.h> + +#define LINUX_PARAM_LOC 0x90000 +#define COMMAND_LINE_LOC 0x91000 +#define GDT_LOC 0x92000 +#define STACK_LOC 0x93000 + +/* The header of Linux/i386 kernel */ +struct linux_header { + uint8_t reserved1[0x1f1]; /* 0x000 */ + uint8_t setup_sects; /* 0x1f1 */ + uint16_t root_flags; /* 0x1f2 */ + uint8_t reserved2[6]; /* 0x1f4 */ + uint16_t vid_mode; /* 0x1fa */ + uint16_t root_dev; /* 0x1fc */ + uint16_t boot_sector_magic; /* 0x1fe */ + /* 2.00+ */ + uint8_t reserved3[2]; /* 0x200 */ + uint8_t header_magic[4]; /* 0x202 */ + uint16_t protocol_version; /* 0x206 */ + uint32_t realmode_swtch; /* 0x208 */ + uint16_t start_sys; /* 0x20c */ + uint16_t kver_addr; /* 0x20e */ + uint8_t type_of_loader; /* 0x210 */ + uint8_t loadflags; /* 0x211 */ + uint16_t setup_move_size; /* 0x212 */ + uint32_t code32_start; /* 0x214 */ + uint32_t ramdisk_image; /* 0x218 */ + uint32_t ramdisk_size; /* 0x21c */ + uint8_t reserved4[4]; /* 0x220 */ + /* 2.01+ */ + uint16_t heap_end_ptr; /* 0x224 */ + uint8_t reserved5[2]; /* 0x226 */ + /* 2.02+ */ + uint32_t cmd_line_ptr; /* 0x228 */ + /* 2.03+ */ + uint32_t initrd_addr_max; /* 0x22c */ +} __attribute__ ((packed)); + + +/* Paramters passed to 32-bit part of Linux + * This is another view of the structure above.. */ +struct linux_params { + uint8_t orig_x; /* 0x00 */ + uint8_t orig_y; /* 0x01 */ + uint16_t ext_mem_k; /* 0x02 -- EXT_MEM_K sits here */ + uint16_t orig_video_page; /* 0x04 */ + uint8_t orig_video_mode; /* 0x06 */ + uint8_t orig_video_cols; /* 0x07 */ + uint16_t unused2; /* 0x08 */ + uint16_t orig_video_ega_bx; /* 0x0a */ + uint16_t unused3; /* 0x0c */ + uint8_t orig_video_lines; /* 0x0e */ + uint8_t orig_video_isVGA; /* 0x0f */ + uint16_t orig_video_points; /* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + uint16_t lfb_width; /* 0x12 */ + uint16_t lfb_height; /* 0x14 */ + uint16_t lfb_depth; /* 0x16 */ + uint32_t lfb_base; /* 0x18 */ + uint32_t lfb_size; /* 0x1c */ + uint16_t cl_magic; /* 0x20 */ +#define CL_MAGIC_VALUE 0xA33F + uint16_t cl_offset; /* 0x22 */ + uint16_t lfb_linelength; /* 0x24 */ + uint8_t red_size; /* 0x26 */ + uint8_t red_pos; /* 0x27 */ + uint8_t green_size; /* 0x28 */ + uint8_t green_pos; /* 0x29 */ + uint8_t blue_size; /* 0x2a */ + uint8_t blue_pos; /* 0x2b */ + uint8_t rsvd_size; /* 0x2c */ + uint8_t rsvd_pos; /* 0x2d */ + uint16_t vesapm_seg; /* 0x2e */ + uint16_t vesapm_off; /* 0x30 */ + uint16_t pages; /* 0x32 */ + uint8_t reserved4[12]; /* 0x34 -- 0x3f reserved for future expansion */ + + //struct apm_bios_info apm_bios_info; /* 0x40 */ + uint8_t apm_bios_info[0x40]; + //struct drive_info_struct drive_info; /* 0x80 */ + uint8_t drive_info[0x20]; + //struct sys_desc_table sys_desc_table; /* 0xa0 */ + uint8_t sys_desc_table[0x140]; + uint32_t alt_mem_k; /* 0x1e0 */ + uint8_t reserved5[4]; /* 0x1e4 */ + uint8_t e820_map_nr; /* 0x1e8 */ + uint8_t reserved6[9]; /* 0x1e9 */ + uint16_t mount_root_rdonly; /* 0x1f2 */ + uint8_t reserved7[4]; /* 0x1f4 */ + uint16_t ramdisk_flags; /* 0x1f8 */ +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 + uint8_t reserved8[2]; /* 0x1fa */ + uint16_t orig_root_dev; /* 0x1fc */ + uint8_t reserved9[1]; /* 0x1fe */ + uint8_t aux_device_info; /* 0x1ff */ + uint8_t reserved10[2]; /* 0x200 */ + uint8_t param_block_signature[4]; /* 0x202 */ + uint16_t param_block_version; /* 0x206 */ + uint8_t reserved11[8]; /* 0x208 */ + uint8_t loader_type; /* 0x210 */ +#define LOADER_TYPE_LOADLIN 1 +#define LOADER_TYPE_BOOTSECT_LOADER 2 +#define LOADER_TYPE_SYSLINUX 3 +#define LOADER_TYPE_ETHERBOOT 4 +#define LOADER_TYPE_KERNEL 5 + uint8_t loader_flags; /* 0x211 */ + uint8_t reserved12[2]; /* 0x212 */ + uint32_t kernel_start; /* 0x214 */ + uint32_t initrd_start; /* 0x218 */ + uint32_t initrd_size; /* 0x21c */ + uint8_t reserved12_5[8]; /* 0x220 */ + uint32_t cmd_line_ptr; /* 0x228 */ + uint8_t reserved13[164]; /* 0x22c */ + struct e820entry e820_map[E820MAX]; /* 0x2d0 */ + uint8_t reserved16[688]; /* 0x550 */ +#define COMMAND_LINE_SIZE 256 + /* Command line is copied here by 32-bit i386/kernel/head.S. + * So I will follow the boot protocol, rather than putting it + * directly here. --ts1 */ + uint8_t command_line[COMMAND_LINE_SIZE]; /* 0x800 */ + uint8_t reserved17[1792]; /* 0x900 - 0x1000 */ +}; + +uint64_t forced_memsize; + +/* Load the first part the file and check if it's Linux */ +static uint32_t load_linux_header(struct linux_header *hdr) +{ + int load_high; + uint32_t kern_addr; + + if (file_read(hdr, sizeof *hdr) != sizeof *hdr) { + debug("Can't read Linux header\n"); + return 0; + } + if (hdr->boot_sector_magic != 0xaa55) { + debug("Not a Linux kernel image\n"); + return 0; + } + + /* Linux is found. Print some information */ + if (memcmp(hdr->header_magic, "HdrS", 4) != 0) { + /* This may be floppy disk image or something. + * Perform a simple (incomplete) sanity check. */ + if (hdr->setup_sects >= 16 + || file_size() - (hdr->setup_sects<<9) >= 512<<10) { + debug("This looks like a bootdisk image but not like Linux...\n"); + return 0; + } + + debugx("Possible very old Linux"); + /* This kernel does not even have a protocol version. + * Force the value. */ + hdr->protocol_version = 0; /* pre-2.00 */ + } else + printf("Found Linux"); + if (hdr->protocol_version >= 0x200 && hdr->kver_addr) { + char kver[256]; + file_seek(hdr->kver_addr + 0x200); + if (file_read(kver, sizeof kver) != 0) { + kver[255] = 0; + printf(" version %s", kver); + } + } + debug(" (protocol %#x)", hdr->protocol_version); + load_high = 0; + if (hdr->protocol_version >= 0x200) { + debug(" (loadflags %#x)", hdr->loadflags); + load_high = hdr->loadflags & 1; + } + if (load_high) { + printf(" bzImage"); + kern_addr = 0x100000; + } else { + printf(" zImage or Image"); + kern_addr = 0x1000; + } + printf(".\n"); + + return kern_addr; +} + +/* Set up parameters for 32-bit kernel */ +static void +init_linux_params(struct linux_params *params, struct linux_header *hdr) +{ + debug("Setting up paramters at %#lx\n", virt_to_phys(params)); + memset(params, 0, sizeof *params); + + /* Copy some useful values from header */ + params->mount_root_rdonly = hdr->root_flags; + params->orig_root_dev = hdr->root_dev; + + /* Video parameters. + * This assumes we have VGA in standard 80x25 text mode, + * just like our vga.c does. + * Cursor position is filled later to allow some more printf's. */ + params->orig_video_mode = 3; + params->orig_video_cols = 80; + params->orig_video_lines = 25; + params->orig_video_isVGA = 1; + params->orig_video_points = 16; + + params->loader_type = 0xff; /* Unregistered Linux loader */ +} + +/* Memory map */ +static void +set_memory_size(struct linux_params *params, struct sys_info *info) +{ + uint32_t i; + uint32_t ramtop = 0; + struct e820entry *linux_map; + struct e820entry *filo_map; + + linux_map = params->e820_map; + + filo_map = meminfo.map; + for (i = 0; i < meminfo.map_count; i++, linux_map++, filo_map++) { + if (i < E820MAX) { + /* Convert to BIOS e820 style */ + linux_map->addr = filo_map->addr; + linux_map->size = filo_map->size; + linux_map->type = filo_map->type; +// debug("%016Lx - %016Lx\n", linux_map->addr,linux_map->addr + linux_map->size); + params->e820_map_nr = i+1; + } + + } + ramtop = meminfo.memsize; + debug("ramtop=%#xk\n", ramtop); + /* Size of memory above 1MB in KB */ + params->alt_mem_k = ramtop; + /* old style, 64MB max */ + if (ramtop >= (64<<10)) + params->ext_mem_k = (63<<10); + else + params->ext_mem_k = params->alt_mem_k; + + debug("ext_mem_k=%d, alt_mem_k=%d\n", params->ext_mem_k, params->alt_mem_k); +} + +/* + * Parse command line + * Some parameters, like initrd=<file>, are not passed to kernel, + * we are responsible to process them. + * Parameters for kernel are copied to kern_cmdline. Returns name of initrd. + */ +static char *parse_command_line(const char *orig_cmdline, char *kern_cmdline) +{ + const char *start, *sep, *end, *val; + char name[64]; + int len; + int k_len; + int to_kern; + char *initrd = 0; + int toolong = 0; + + forced_memsize = 0; + + if (!orig_cmdline) { + *kern_cmdline = 0; + return 0; + } + + k_len = 0; + debug("original command line: \"%s\"\n", orig_cmdline); + debug("kernel command line at %#lx\n", virt_to_phys(kern_cmdline)); + + start = orig_cmdline; + while (*start == ' ') + start++; + while (*start) { + end = strchr(start, ' '); + if (!end) + end = start + strlen(start); + sep = strchr(start, '='); + if (!sep || sep > end) + sep = end; + len = sep - start; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + memcpy(name, start, len); + name[len] = 0; + + if (*sep == '=') { + val = sep + 1; + len = end - val; + } else { + val = 0; + len = 0; + } + + /* Only initrd= and mem= are handled here. vga= is not, + * which I believe is a paramter to the realmode part of Linux, + * which we don't execute. */ + if (strcmp(name, "initrd") == 0) { + if (!val) + printf("Missing filename to initrd parameter\n"); + else { + initrd = allot(len + 1); + memcpy(initrd, val, len); + initrd[len] = 0; + debug("initrd=%s\n", initrd); + } + /* Don't pass this to kernel */ + to_kern = 0; + } else if (strcmp(name, "mem") == 0) { + if (!val) + printf("Missing value for mem parameter\n"); + else { + forced_memsize = strtoull_with_suffix(val, (char**)&val, 0); + if (forced_memsize == 0) + printf("Invalid mem option, ignored\n"); + if (val != end) { + printf("Garbage after mem=<size>, ignored\n"); + forced_memsize = 0; + } +// debug("mem=%Lu\n", forced_memsize); + } + /* mem= is for both loader and kernel */ + to_kern = 1; + } else + to_kern = 1; + + if (to_kern) { + /* Copy to kernel command line buffer */ + if (k_len != 0) + kern_cmdline[k_len++] = ' '; /* put separator */ + len = end - start; + if (k_len + len >= COMMAND_LINE_SIZE) { + len = COMMAND_LINE_SIZE - k_len - 1; + if (!toolong) { + printf("Kernel command line is too long; truncated to " + "%d bytes\n", COMMAND_LINE_SIZE-1); + toolong = 1; + } + } + memcpy(kern_cmdline + k_len, start, len); + k_len += len; + } + + start = end; + while (*start == ' ') + start++; + } + kern_cmdline[k_len] = 0; + debug("kernel command line (%d bytes): \"%s\"\n", k_len, kern_cmdline); + + return initrd; +} + +/* Set command line location */ +static void set_command_line_loc(struct linux_params *params, + struct linux_header *hdr) +{ + if (hdr->protocol_version >= 0x202) { + /* new style */ + params->cmd_line_ptr = COMMAND_LINE_LOC; + } else { + /* old style */ + params->cl_magic = CL_MAGIC_VALUE; + params->cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC; + } +} + +/* Load 32-bit part of kernel */ +static int load_linux_kernel(struct linux_header *hdr, uint32_t kern_addr) +{ + uint32_t kern_offset, kern_size; + + if (hdr->setup_sects == 0) + hdr->setup_sects = 4; + kern_offset = (hdr->setup_sects + 1) * 512; + file_seek(kern_offset); + kern_size = file_size() - kern_offset; + debug("offset=%#x addr=%#x size=%#x\n", kern_offset, kern_addr, kern_size); + + if (using_devsize) { + printf("Attempt to load up to end of device as kernel; " + "specify the image size\n"); + return 0; + } + + printf("Loading kernel... "); + if (file_read(phys_to_virt(kern_addr), kern_size) != kern_size) { + printf("Can't read kernel\n"); + return 0; + } + printf("ok\n"); + + return kern_size; +} + +static int load_initrd(struct linux_header *hdr, struct sys_info *info, + uint32_t kern_end, struct linux_params *params, const char *initrd_file) +{ + uint32_t max; + uint32_t start, end, size; + uint64_t forced; + extern char _virt_start[], _end[]; + + if (!file_open(initrd_file)) { + printf("Can't open initrd: %s\n", initrd_file); + return -1; + } + if (using_devsize) { + printf("Attempt to load up to end of device as initrd; " + "specify the image size\n"); + return -1; + } + size = file_size(); + + + /* Find out the kernel's restriction on how high the initrd can be + * placed */ + if (hdr->protocol_version >= 0x203) + max = hdr->initrd_addr_max; + else + max = 0x38000000; /* Hardcoded value for older kernels */ + + /* FILO itself is at the top of RAM. (relocated) + * So, try putting initrd just below us. */ + end = virt_to_phys(_virt_start); + if (end > max) + end = max; + + /* If "mem=" option is given, we have to put the initrd within + * the specified range. */ + if (forced_memsize) { + forced = forced_memsize; + if (forced > max) + forced = max; + /* If the "mem=" is lower, it's easy */ + if (forced <= end) + end = forced; + else { + /* Otherwise, see if we can put it above us */ + if (virt_to_phys(_end) + size <= forced) + end = forced; /* Ok */ + } + } + + start = end - size; + start &= ~0xfff; /* page align */ + end = start + size; + + debug("start=%#x end=%#x\n", start, end); + + if (start < kern_end) { + printf("Initrd is too big\n"); + return -1; + } + + printf("Loading initrd... "); + if (file_read(phys_to_virt(start), size) != size) { + printf("Can't read initrd\n"); + return -1; + } + printf("ok\n"); + + params->initrd_start = start; + params->initrd_size = size; + + return 0; +} + +static void hardware_setup(void) +{ + /* Disable nmi */ + outb(0x80, 0x70); + + /* Make sure any coprocessor is properly reset.. */ + outb(0, 0xf0); + outb(0, 0xf1); + + /* we're getting screwed again and again by this problem of the 8259. + * so we're going to leave this lying around for inclusion into + * crt0.S on an as-needed basis. + * + * well, that went ok, I hope. Now we have to reprogram the interrupts :-( + * we put them right after the intel-reserved hardware interrupts, at + * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really + * messed this up with the original PC, and they haven't been able to + * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, + * which is used for the internal hardware interrupts as well. We just + * have to reprogram the 8259's, and it isn't fun. + */ + + outb(0x11, 0x20); /* initialization sequence to 8259A-1 */ + outb(0x11, 0xA0); /* and to 8259A-2 */ + + outb(0x20, 0x21); /* start of hardware int's (0x20) */ + outb(0x28, 0xA1); /* start of hardware int's 2 (0x28) */ + + outb(0x04, 0x21); /* 8259-1 is master */ + outb(0x02, 0xA1); /* 8259-2 is slave */ + + outb(0x01, 0x21); /* 8086 mode for both */ + outb(0x01, 0xA1); + + outb(0xFF, 0xA1); /* mask off all interrupts for now */ + outb(0xFB, 0x21); /* mask all irq's but irq2 which is cascaded */ +} + +/* Start Linux */ +static int start_linux(uint32_t kern_addr, struct linux_params *params) +{ + struct segment_desc *linux_gdt; + struct context *ctx; +#if 0 + extern int cursor_x, cursor_y; +#endif + + ctx = init_context(phys_to_virt(STACK_LOC), 4096, 0); + + /* Linux expects GDT being in low memory */ + linux_gdt = phys_to_virt(GDT_LOC); + memset(linux_gdt, 0, 13*sizeof(struct segment_desc)); + /* Normal kernel code/data segments */ + linux_gdt[2] = gdt[FLAT_CODE]; + linux_gdt[3] = gdt[FLAT_DATA]; + /* 2.6 kernel uses 12 and 13, but head.S uses backward-compatible + * segments (2 and 3), so it SHOULD not be a problem. + * However, some distro kernels (eg. RH9) with backported threading + * patch use 12 and 13 also when booting... */ + linux_gdt[12] = gdt[FLAT_CODE]; + linux_gdt[13] = gdt[FLAT_DATA]; + ctx->gdt_base = GDT_LOC; + ctx->gdt_limit = 14*8-1; + ctx->cs = 0x10; + ctx->ds = 0x18; + ctx->es = 0x18; + ctx->fs = 0x18; + ctx->gs = 0x18; + ctx->ss = 0x18; + + /* Parameter location */ + ctx->esi = virt_to_phys(params); + + /* Entry point */ + ctx->eip = kern_addr; + + debug("eip=%#x\n", kern_addr); + printf("Jumping to entry point...\n"); + +#ifdef VGA_CONSOLE + /* Update VGA cursor position. + * This must be here because the printf changes the value! */ +#if 0 + params->orig_x = cursor_x; + params->orig_y = cursor_y; +#endif +#endif + + /* Go... */ + ctx = switch_to(ctx); + + /* It's impossible but... */ + printf("Returned with eax=%#x\n", ctx->eax); + + return ctx->eax; +} + +int linux_load(struct sys_info *info, const char *file, const char *cmdline) +{ + struct linux_header hdr; + struct linux_params *params; + uint32_t kern_addr, kern_size; + char *initrd_file = 0; + + if (!file_open(file)) + return -1; + + kern_addr = load_linux_header(&hdr); + if (kern_addr == 0) + return LOADER_NOT_SUPPORT; + + params = phys_to_virt(LINUX_PARAM_LOC); + init_linux_params(params, &hdr); + set_memory_size(params, info); + initrd_file = parse_command_line(cmdline, phys_to_virt(COMMAND_LINE_LOC)); + set_command_line_loc(params, &hdr); + + kern_size = load_linux_kernel(&hdr, kern_addr); + if (kern_size == 0) { + if (initrd_file) + forget(initrd_file); + return -1; + } + + if (initrd_file) { + if (load_initrd(&hdr, info, kern_addr+kern_size, params, initrd_file) + != 0) { + forget(initrd_file); + return -1; + } + forget(initrd_file); + } + + hardware_setup(); + + start_linux(kern_addr, params); + return 0; +} diff --git a/src/filo/i386/multiboot.c b/src/filo/i386/multiboot.c new file mode 100644 index 000000000..e4a495695 --- /dev/null +++ b/src/filo/i386/multiboot.c @@ -0,0 +1,129 @@ +/* Support for Multiboot */ +#include <etherboot.h> + +#include <lib.h> +#include <sys_info.h> +#include <arch/io.h> + +#define DEBUG_THIS DEBUG_MULTIBOOT +#include <debug.h> + +/* Multiboot header, gives information to loader */ + +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 +#define MULTIBOOT_HEADER_FLAGS 0x00000003 + +struct mbheader { + unsigned int magic, flags, checksum; +}; +const struct mbheader multiboot_header + __attribute__((section (".hdr"))) = +{ + MULTIBOOT_HEADER_MAGIC, + MULTIBOOT_HEADER_FLAGS, + -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) +}; + +/* Multiboot information structure, provided by loader to us */ + +struct multiboot_mmap { + unsigned entry_size; + unsigned base_lo, base_hi; + unsigned size_lo, size_hi; + unsigned type; +}; + +struct multiboot_info { + unsigned flags; +#define MULTIBOOT_MEM_VALID 0x01 +#define MULTIBOOT_BOOT_DEV_VALID 0x02 +#define MULTIBOOT_CMDLINE_VALID 0x04 +#define MULTIBOOT_MODS_VALID 0x08 +#define MULTIBOOT_AOUT_SYMS_VALID 0x10 +#define MULTIBOOT_ELF_SYMS_VALID 0x20 +#define MULTIBOOT_MMAP_VALID 0x40 + unsigned mem_lower; + unsigned mem_upper; + unsigned char boot_device[4]; + unsigned command_line; + unsigned mods_count; + unsigned mods_addr; + unsigned syms_num; + unsigned syms_size; + unsigned syms_addr; + unsigned syms_shndx; + unsigned mmap_length; + unsigned mmap_addr; +}; + +void collect_multiboot_info(struct sys_info *info) +{ + struct multiboot_info *mbinfo; + struct multiboot_mmap *mbmem; + unsigned mbcount, mbaddr; + int i; + struct memrange *mmap; + int mmap_count; + + if (info->boot_type != 0x2BADB002) + return; + + debug("Using Multiboot information at %#lx\n", info->boot_data); + + mbinfo = phys_to_virt(info->boot_data); + + if (mbinfo->flags & MULTIBOOT_MMAP_VALID) { + /* convert mmap records */ + mbmem = phys_to_virt(mbinfo->mmap_addr); + mbcount = mbinfo->mmap_length / (mbmem->entry_size + 4); + mmap = malloc(mbcount * sizeof *mmap); + mmap_count = 0; + mbaddr = mbinfo->mmap_addr; + for (i = 0; i < mbcount; i++) { + mbmem = phys_to_virt(mbaddr); + debug("%08x%08x %08x%08x (%d)\n", + mbmem->base_hi, + mbmem->base_lo, + mbmem->size_hi, + mbmem->size_lo, + mbmem->type); + if (mbmem->type == 1) { /* Only normal RAM */ + mmap[mmap_count].base = mbmem->base_lo + + (((unsigned long long) mbmem->base_hi) << 32); + mmap[mmap_count].size = mbmem->size_lo + + (((unsigned long long) mbmem->size_hi) << 32); + mmap_count++; + } + mbaddr += mbmem->entry_size + 4; + if (mbaddr >= mbinfo->mmap_addr + mbinfo->mmap_length) + break; + } + /* simple sanity check - there should be at least 2 RAM segments + * (base 640k and extended) */ + if (mmap_count >= 2) + goto got_it; + + printf("Multiboot mmap is broken\n"); + free(mmap); + /* fall back to mem_lower/mem_upper */ + } + + if (mbinfo->flags & MULTIBOOT_MEM_VALID) { + /* use mem_lower and mem_upper */ + mmap_count = 2; + mmap = malloc(2 * sizeof(*mmap)); + mmap[0].base = 0; + mmap[0].size = mbinfo->mem_lower << 10; + mmap[1].base = 1 << 20; /* 1MB */ + mmap[1].size = mbinfo->mem_upper << 10; + goto got_it; + } + + printf("Can't get memory information from Multiboot\n"); + return; + +got_it: + info->memrange = mmap; + info->n_memranges = mmap_count; + return; +} diff --git a/src/filo/i386/segment.c b/src/filo/i386/segment.c new file mode 100644 index 000000000..6e370d8b4 --- /dev/null +++ b/src/filo/i386/segment.c @@ -0,0 +1,48 @@ +/* Segmentation of the i386 architecture. + * + * 2003-07 by SONE Takeshi + */ +#include <etherboot.h> + +//#include <lib.h> +#include <sys_info.h> +#include "segment.h" + +#define DEBUG_THIS DEBUG_SEGMENT +#include <debug.h> + +/* i386 lgdt argument */ +struct gdtarg { + unsigned short limit; + unsigned int base; +} __attribute__((packed)); + +/* GDT, the global descriptor table */ +struct segment_desc gdt[NUM_SEG] = { + /* 0x00: null segment */ + {0, 0, 0, 0, 0, 0}, + /* 0x08: flat code segment */ + {0xffff, 0, 0, 0x9f, 0xcf, 0}, + /* 0x10: flat data segment */ + {0xffff, 0, 0, 0x93, 0xcf, 0}, + /* 0x18: code segment for relocated execution */ + {0xffff, 0, 0, 0x9f, 0xcf, 0}, + /* 0x20: data segment for relocated execution */ + {0xffff, 0, 0, 0x93, 0xcf, 0}, +}; + +/* Copy GDT to new location and reload it */ +void move_gdt(unsigned long newgdt) +{ + struct gdtarg gdtarg; + + debug("Moving GDT to %#lx...", newgdt); + memcpy(phys_to_virt(newgdt), gdt, sizeof gdt); + gdtarg.base = newgdt; + gdtarg.limit = GDT_LIMIT; + debug("reloading GDT..."); + __asm__ __volatile__ ("lgdt %0\n\t" : : "m" (gdtarg)); + debug("reloading CS for fun..."); + __asm__ __volatile__ ("ljmp %0, $1f\n1:" : : "n" (RELOC_CS)); + debug("ok\n"); +} diff --git a/src/filo/i386/segment.h b/src/filo/i386/segment.h new file mode 100644 index 000000000..97eb574e6 --- /dev/null +++ b/src/filo/i386/segment.h @@ -0,0 +1,29 @@ +/* Segment indexes. Must match the gdt definition in segment.c. */ +enum { + NULL_SEG, + FLAT_CODE, + FLAT_DATA, + RELOC_CODE, + RELOC_DATA, + NUM_SEG, +}; + +/* Values for segment selector register */ +#define FLAT_CS (FLAT_CODE << 3) +#define FLAT_DS (FLAT_DATA << 3) +#define RELOC_CS (RELOC_CODE << 3) +#define RELOC_DS (RELOC_DATA << 3) + +/* i386 segment descriptor */ +struct segment_desc { + unsigned short limit_0; + unsigned short base_0; + unsigned char base_16; + unsigned char types; + unsigned char flags; + unsigned char base_24; +}; + +extern struct segment_desc gdt[NUM_SEG]; + +#define GDT_LIMIT ((NUM_SEG << 3) - 1) diff --git a/src/filo/i386/switch.S b/src/filo/i386/switch.S new file mode 100644 index 000000000..76ac75bf1 --- /dev/null +++ b/src/filo/i386/switch.S @@ -0,0 +1,116 @@ + .globl entry, __switch_context, __exit_context, halt + + .text + .align 4 + +/* + * Entry point + * We start execution from here. + * It is assumed that CPU is in 32-bit protected mode and + * all segments are 4GB and base zero (flat model). + */ +entry: + /* Save boot context and switch to our main context. + * Main context is statically defined in C. + */ + pushl %cs + call __switch_context + + /* We get here when the main context switches back to + * the boot context. + * Return to previous bootloader. + */ + ret + +/* + * Switch execution context + * This saves registers, segments, and GDT in the stack, then + * switches the stack, and restores everything from the new stack. + * This function takes no argument. New stack pointer is + * taken from global variable __context, and old stack pointer + * is also saved to __context. This way we can just jump to + * this routine to get back to the original context. + * + * Call this routine with lcall or pushl %cs; call. + */ +__switch_context: + /* Save everything in current stack */ + pushfl /* 56 */ + pushl %ds /* 52 */ + pushl %es /* 48 */ + pushl %fs /* 44 */ + pushl %gs /* 40 */ + pushal /* 8 */ + subl $8, %esp + movw %ss, (%esp) /* 0 */ + sgdt 2(%esp) /* 2 */ + +#if 0 + /* Swap %cs and %eip on the stack, so lret will work */ + movl 60(%esp), %eax + xchgl %eax, 64(%esp) + movl %eax, 60(%esp) +#endif + + /* At this point we don't know if we are on flat segment + * or relocated. So compute the address offset from %eip. + * Assuming CS.base==DS.base==SS.base. + */ + call 1f +1: popl %ebx + subl $1b, %ebx + + /* Interrupts are not allowed... */ + cli + + /* Current context pointer is our stack pointer */ + movl %esp, %esi + + /* Normalize the ctx pointer */ + subl %ebx, %esi + + /* Swap it with new value */ + xchgl %esi, __context(%ebx) + + /* Adjust new ctx pointer for current address offset */ + addl %ebx, %esi + + /* Load new %ss and %esp to temporary */ + movzwl (%esi), %edx + movl 20(%esi), %eax + + /* Load new GDT */ + lgdt 2(%esi) + + /* Load new stack segment with new GDT */ + movl %edx, %ss + + /* Set new stack pointer, but we have to adjust it because + * pushal saves %esp value before pushal, and we want the value + * after pushal. + */ + leal -32(%eax), %esp + + /* Load the rest from new stack */ + popal + popl %gs + popl %fs + popl %es + popl %ds + popfl + + /* Finally, load new %cs and %eip */ + lret + +__exit_context: + /* Get back to the original context */ + pushl %cs + call __switch_context + + /* We get here if the other context attempt to switch to this + * dead context. This should not happen. */ + +halt: + cli + hlt + jmp halt diff --git a/src/filo/i386/sys_info.c b/src/filo/i386/sys_info.c new file mode 100644 index 000000000..fa8c190f8 --- /dev/null +++ b/src/filo/i386/sys_info.c @@ -0,0 +1,29 @@ +#include <etherboot.h> +#include <sys_info.h> +#include "context.h" +#define DEBUG_THIS DEBUG_SYS_INFO +#include <debug.h> + +void collect_multiboot_info(struct sys_info *); + +void collect_sys_info(struct sys_info *info) +{ + + /* Pick up paramters given by bootloader to us */ + info->boot_type = boot_ctx->eax; + info->boot_data = boot_ctx->ebx; + info->boot_arg = boot_ctx->param[0]; + debug("boot eax = %#lx\n", info->boot_type); + debug("boot ebx = %#lx\n", info->boot_data); + debug("boot arg = %#lx\n", info->boot_arg); + + collect_elfboot_info(info); + collect_linuxbios_info(info); +#ifdef MULTIBOOT_IMAGE + collect_multiboot_info(info); +#endif + +#if 0 + debug("RAM %Ld MB\n", (meminfo.memsize + 512*1024) >> 20); +#endif +} diff --git a/src/filo/main/console_x.c b/src/filo/main/console_x.c new file mode 100644 index 000000000..fd04a9392 --- /dev/null +++ b/src/filo/main/console_x.c @@ -0,0 +1,74 @@ + +#include "etherboot.h" + +#include <lib.h> + +int getline(char *buf, int max) +{ + int cur, ch, nonspace_seen; + + cur = 0; + while (buf[cur]) { + putchar(buf[cur]); + cur++; + } + for (;;) { + ch = getchar(); + switch (ch) { + /* end of line */ + case '\r': + case '\n': + putchar('\n'); + goto out; + /* backspace */ + case '\b': + case '\x7f': + if (cur > 0) { + cur--; + putchar('\b'); + putchar(' '); + putchar('\b'); + } + break; + /* word erase */ + case 'W' & 0x1f: /* ^W */ + nonspace_seen = 0; + while (cur) { + if (buf[cur-1] != ' ') + nonspace_seen = 1; + putchar('\b'); + putchar(' '); + putchar('\b'); + cur--; + if (nonspace_seen && cur < max-1 && cur > 0 && buf[cur-1]==' ') + break; + } + break; + /* line erase */ + case 'U' & 0x1f: /* ^U */ + while (cur) { + putchar('\b'); + putchar(' '); + putchar('\b'); + cur--; + } + cur = 0; + break; + default: + if (ch < 0x20) + break; /* ignore control char */ + if (ch >= 0x7f) + break; + if (cur + 1 < max) { + putchar(ch); /* echo back */ + buf[cur] = ch; + cur++; + } + } + } +out: + if (cur >= max) + cur = max - 1; + buf[cur] = '\0'; + return cur; +} diff --git a/src/filo/main/elfload.c b/src/filo/main/elfload.c new file mode 100644 index 000000000..53114ffb7 --- /dev/null +++ b/src/filo/main/elfload.c @@ -0,0 +1,398 @@ +/* ELF Boot loader + * As we have seek, this implementation can be straightforward. + * 2003-07 by SONE Takeshi + */ +#include <etherboot.h> +#include <elf.h> +#include <bits/elf_x.h> +#include <elf_boot.h> +#include <lib.h> +#include <sys_info.h> + +#include <fs.h> +#define DEBUG_THIS DEBUG_ELFBOOT +#include <debug.h> + +#if 1 +//Use that in Etherboot +extern int elf_start(unsigned long __unused_i386, unsigned long entry, unsigned long param); +#define start_elf(x,y) elf_start(0, x, y) +#else +// original in filo +extern unsigned int start_elf(unsigned long entry_point, unsigned long param); +#endif + +extern char _virt_start[], _end[]; + +static char *image_name, *image_version; + +static int check_mem_ranges(struct sys_info *info, + Elf_phdr *phdr, int phnum) +{ + int i, j; + unsigned long start, end; + unsigned long prog_start, prog_end; +#if 0 + struct memrange *mem; +#else + struct e820entry *mem; +#endif + + prog_start = virt_to_phys(&_virt_start); + prog_end = virt_to_phys(&_end); + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_LOAD) + continue; + start = phdr[i].p_paddr; + end = start + phdr[i].p_memsz; + if (start < prog_start && end > prog_start) + goto conflict; + if (start < prog_end && end > prog_end) + goto conflict; +#if 0 + for (j = 0; j < info->n_memranges; j++) { + mem = &info->memrange[j]; + if (mem->base <= start && mem->base + mem->size >= end) + break; + } + if (j >= info->n_memranges) + goto badseg; +#else +#define LB_MEM_RAM 1 + for (j = 0; j < meminfo.map_count; j++) { + mem = &meminfo.map[j]; + if (mem->type!=LB_MEM_RAM) continue; + if (mem->addr <= start && mem->addr + mem->size >= end) + break; + } + if (j >= meminfo.map_count) + goto badseg; +#endif + } + return 1; + +conflict: + printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end); + +badseg: + printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1); + return 0; +} + +static unsigned long process_image_notes(Elf_phdr *phdr, int phnum, + unsigned short *sum_ptr) +{ + int i; + char *buf = NULL; + int retval = 0; + unsigned long addr, end; + Elf_Nhdr *nhdr; + const char *name; + void *desc; + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_NOTE) + continue; + buf = allot(phdr[i].p_filesz); + file_seek(phdr[i].p_offset); + if (file_read(buf, phdr[i].p_filesz) != phdr[i].p_filesz) { + printf("Can't read note segment\n"); + goto out; + } + addr = (unsigned long) buf; + end = addr + phdr[i].p_filesz; + while (addr < end) { + nhdr = (Elf_Nhdr *) addr; + addr += sizeof(Elf_Nhdr); + name = (const char *) addr; + addr += (nhdr->n_namesz+3) & ~3; + desc = (void *) addr; + addr += (nhdr->n_descsz+3) & ~3; + + if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT) + && memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) { + if (nhdr->n_type == EIN_PROGRAM_NAME) { + image_name = calloc(1, nhdr->n_descsz + 1); + memcpy(image_name, desc, nhdr->n_descsz); + } + if (nhdr->n_type == EIN_PROGRAM_VERSION) { + image_version = calloc(1, nhdr->n_descsz + 1); + memcpy(image_version, desc, nhdr->n_descsz); + } + if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) { + *sum_ptr = *(unsigned short *) desc; + debug("Image checksum: %04x\n", *sum_ptr); + /* Where in the file */ + retval = phdr[i].p_offset + + (unsigned long) desc - (unsigned long) buf; + } + } + } + } +out: + if (buf) + forget(buf); + return retval; +} + +static int load_segments(Elf_phdr *phdr, int phnum, + unsigned long checksum_offset) +{ + unsigned long bytes; + unsigned int start_time, time; + int i; + int j; + + bytes = 0; + start_time = currticks(); +#if 0 + for (j = 0; j < phnum; j++) { + if (phdr[j].p_type != PT_LOAD) + continue; + debug("0 segment %d addr:%#x file:%#x mem:%#x, phdr%#x\n", + j, phdr[j].p_paddr, phdr[j].p_filesz, phdr[j].p_memsz, virt_to_phys(&phdr[j])); + } +#endif + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_LOAD) + continue; + debug("segment %d addr:%#x file:%#x mem:%#x phdr:%#x ", + i, phdr[i].p_paddr, phdr[i].p_filesz, phdr[i].p_memsz, virt_to_phys(&phdr[i])); + file_seek(phdr[i].p_offset); + debug("loading... "); + if (file_read(phys_to_virt(phdr[i].p_paddr), phdr[i].p_filesz) + != phdr[i].p_filesz) { + printf("Can't read program segment %d\n", i); + return 0; + } + bytes += phdr[i].p_filesz; + debug("clearing... "); + memset(phys_to_virt(phdr[i].p_paddr + phdr[i].p_filesz), 0, + phdr[i].p_memsz - phdr[i].p_filesz); + if (phdr[i].p_offset <= checksum_offset + && phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) { + debug("clearing checksum... "); + memset(phys_to_virt(phdr[i].p_paddr + checksum_offset + - phdr[i].p_offset), 0, 2); + } + debug("ok\n"); + + } + time = (currticks() - start_time)*1000/18; + printf("Loaded %d bytes in %dms (%dKB/s)\n", bytes, time, + time? bytes/time : 0); + return 1; +} + +static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum, + unsigned short image_sum) +{ + unsigned short sum, part_sum; + unsigned long offset; + int i; + + sum = 0; + offset = 0; + + part_sum = ipchksum(ehdr, sizeof *ehdr); + sum = add_ipchksums(offset, sum, part_sum); + offset += sizeof *ehdr; + + part_sum = ipchksum(phdr, phnum * sizeof(*phdr)); + sum = add_ipchksums(offset, sum, part_sum); + offset += phnum * sizeof(*phdr); + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_LOAD) + continue; + part_sum = ipchksum(phys_to_virt(phdr[i].p_paddr), phdr[i].p_memsz); + sum = add_ipchksums(offset, sum, part_sum); + offset += phdr[i].p_memsz; + } + + if (sum != image_sum) { + printf("Verify FAILED (image:%04x vs computed:%04x)\n", + image_sum, sum); + return 0; + } + return 1; +} + +static inline unsigned const padded(unsigned s) +{ + return (s + 3) & ~3; +} + +static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name, + unsigned type, const char *desc, unsigned descsz) +{ + Elf_Nhdr nhdr; + unsigned ent_size, new_size, pad; + char *addr; + + if (!bhdr) + return NULL; + + nhdr.n_namesz = name? strlen(name)+1 : 0; + nhdr.n_descsz = descsz; + nhdr.n_type = type; + ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz); + if (bhdr->b_size + ent_size > 0xffff) { + printf("Boot notes too big\n"); + forget(bhdr); + return NULL; + } + if (bhdr->b_size + ent_size > bhdr->b_checksum) { + do { + new_size = bhdr->b_checksum * 2; + } while (new_size < bhdr->b_size + ent_size); + if (new_size > 0xffff) + new_size = 0xffff; + debug("expanding boot note size to %u\n", new_size); + bhdr = realloc(bhdr, new_size); + bhdr->b_checksum = new_size; + } + + addr = (char *) bhdr; + addr += bhdr->b_size; + memcpy(addr, &nhdr, sizeof(nhdr)); + addr += sizeof(nhdr); + + memcpy(addr, name, nhdr.n_namesz); + addr += nhdr.n_namesz; + pad = padded(nhdr.n_namesz) - nhdr.n_namesz; + memset(addr, 0, pad); + addr += pad; + + memcpy(addr, desc, nhdr.n_descsz); + addr += nhdr.n_descsz; + pad = padded(nhdr.n_descsz) - nhdr.n_descsz; + memset(addr, 0, pad); + addr += pad; + + bhdr->b_size += ent_size; + bhdr->b_records++; + return bhdr; +} + +static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name, + unsigned type, const char *desc) +{ + return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1); +} + +static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline) +{ + Elf_Bhdr *bhdr; + + bhdr = allot(256); + bhdr->b_signature = ELF_BHDR_MAGIC; + bhdr->b_size = sizeof *bhdr; + bhdr->b_checksum = 256; /* XXX cache the current buffer size here */ + bhdr->b_records = 0; + + if (info->firmware) + bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware); + bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name); + bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version); + if (cmdline) + bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline); + if (!bhdr) + return bhdr; + bhdr->b_checksum = 0; + bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size); + return bhdr; +} + +int elf_load(struct sys_info *info, const char *filename, const char *cmdline) +{ + Elf_ehdr ehdr; + Elf_phdr *phdr = NULL; + unsigned long phdr_size; + unsigned long checksum_offset; + unsigned short checksum; + Elf_Bhdr *boot_notes = NULL; + int retval = -1; + int image_retval; + + image_name = image_version = 0; + + if (!file_open(filename)) + goto out; + + if (file_read(&ehdr, sizeof ehdr) != sizeof ehdr) { + debug("Can't read ELF header\n"); + retval = LOADER_NOT_SUPPORT; + goto out; + } + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_CLASS] != ARCH_ELF_CLASS + || ehdr.e_ident[EI_DATA] != ARCH_ELF_DATA + || ehdr.e_ident[EI_VERSION] != EV_CURRENT + || ehdr.e_type != ET_EXEC + || !ARCH_ELF_MACHINE_OK(ehdr.e_machine) + || ehdr.e_version != EV_CURRENT + || ehdr.e_phentsize != sizeof(Elf_phdr)) { + debug("Not a bootable ELF image\n"); + retval = LOADER_NOT_SUPPORT; + goto out; + } + + phdr_size = ehdr.e_phnum * sizeof *phdr; + phdr = allot(phdr_size);//hack LYH otherwise some one clear the last entry + file_seek(ehdr.e_phoff); + if (file_read(phdr, phdr_size) != phdr_size) { + printf("Can't read program header\n"); + goto out; + } + + if (!check_mem_ranges(info, phdr, ehdr.e_phnum)) + goto out; + + checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum); + + printf("Loading %s", image_name ? image_name : "image"); + if (image_version) + printf(" version %s", image_version); + printf("...\n"); + + if (!load_segments(phdr, ehdr.e_phnum, checksum_offset)) + goto out; + + if (checksum_offset) { + if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum)) + goto out; + } + + boot_notes = build_boot_notes(info, cmdline); + + debug("current time: %x\n", currticks()); + + debug("entry point is %#x\n", ehdr.e_entry); + printf("Jumping to entry point...\n"); + + image_retval = start_elf(ehdr.e_entry, virt_to_phys(boot_notes)); +#if 0 + console_init(); +#endif + + printf("Image returned with return value %#x\n", image_retval); + retval = 0; + +out: + if (phdr) + forget(phdr); + if (boot_notes) + forget(boot_notes); + if (image_name) + forget(image_name); + if (image_version) + forget(image_version); + return retval; +} diff --git a/src/filo/main/elfnote.c b/src/filo/main/elfnote.c new file mode 100644 index 000000000..2100ec956 --- /dev/null +++ b/src/filo/main/elfnote.c @@ -0,0 +1,153 @@ +/* Support for ELF Boot Proposal as a boot image */ + +#include <etherboot.h> +#include <elf_boot.h> +#include <sys_info.h> +#include <lib.h> + +#include "version.h" +#define DEBUG_THIS DEBUG_ELFNOTE +#include <debug.h> + +/* ELF image notes provide information to the loader who boots us */ + +/* This compiles and generates correct PT_NOTE segment for me. + * If it doesn't, use assembly version below. */ + +struct elf_image_note { + Elf_Nhdr hdr0; + char name0[sizeof(ELF_NOTE_BOOT)]; + char prog_name[sizeof(PROGRAM_NAME)]; + + Elf_Nhdr hdr1; + char name1[sizeof(ELF_NOTE_BOOT)]; + char version[sizeof(PROGRAM_VERSION)]; + + Elf_Nhdr hdr2; + char name2[sizeof(ELF_NOTE_BOOT)]; + unsigned short checksum; +}; + +const struct elf_image_note elf_image_notes + __attribute__ ((section (".note.ELFBoot"))) = +{ + .hdr0 = { + .n_namesz = sizeof(ELF_NOTE_BOOT), + .n_descsz = sizeof(PROGRAM_NAME), + .n_type = EIN_PROGRAM_NAME, + }, + .name0 = ELF_NOTE_BOOT, + .prog_name = PROGRAM_NAME, + + .hdr1 = { + .n_namesz = sizeof(ELF_NOTE_BOOT), + .n_descsz = sizeof(PROGRAM_VERSION), + .n_type = EIN_PROGRAM_VERSION, + }, + .name1 = ELF_NOTE_BOOT, + .version = PROGRAM_VERSION, + + .hdr2 = { + .n_namesz = sizeof(ELF_NOTE_BOOT), + .n_descsz = sizeof(unsigned short), + .n_type = EIN_PROGRAM_CHECKSUM, + }, + .name2 = ELF_NOTE_BOOT, + .checksum = 0, /* to be computed by external tool */ +}; + +/* This is refered by other files */ +const char *program_name = elf_image_notes.prog_name; +const char *program_version = elf_image_notes.version; + +#if 0 + + /* This tells the linker to make a PT_NOTE segment. + * If the section is named just ".note", it will be + * mixed up with useless .version notes generated by GCC. + */ + .section ".note.ELFBoot", "a" + + .align 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: .align 4 +3: .asciz PROGRAM_NAME +4: + + .align 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: .align 4 +3: .asciz PROGRAM_VERSION +4: + + .align 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: .align 4 +3: .short 0 +4: +#endif + +/* Collect information from the ELF bootloader + * Note that we have to copy them to our own memory, + * otherwise they might be overwritten afterward. */ +void collect_elfboot_info(struct sys_info *info) +{ + Elf_Bhdr *hdr = 0; + char *addr, *end; + Elf_Nhdr *nhdr; + char *name, *desc; + + if (info->boot_type == ELF_BHDR_MAGIC) + hdr = phys_to_virt(info->boot_data); + else + hdr = phys_to_virt(info->boot_arg); + + if (hdr->b_signature != ELF_BHDR_MAGIC) + return; + + if (ipchksum(hdr, hdr->b_size) != 0) { + printf("Broken ELF boot notes\n"); + return; + } + + addr = (char *) (hdr + 1); + end = addr + hdr->b_size; + while (addr < end) { + nhdr = (Elf_Nhdr *) addr; + addr += sizeof(Elf_Nhdr); + name = addr; + addr += (nhdr->n_namesz + 3) & ~3; + desc = addr; + addr += (nhdr->n_descsz + 3) & ~3; + + if (nhdr->n_namesz == 0) { + /* Standard notes */ + switch (nhdr->n_type) { + case EBN_FIRMWARE_TYPE: + info->firmware = strdup(desc); + break; + case EBN_BOOTLOADER_NAME: + debug("Bootloader: %s\n", desc); + break; + case EBN_BOOTLOADER_VERSION: + debug("Version: %s\n", desc); + break; + case EBN_COMMAND_LINE: + info->command_line = strdup(desc); + break; + case EBN_LOADED_IMAGE: + debug("Image name: %s\n", desc); + break; + } + } + } +} diff --git a/src/filo/main/filo_x.c b/src/filo/main/filo_x.c new file mode 100644 index 000000000..6569c4136 --- /dev/null +++ b/src/filo/main/filo_x.c @@ -0,0 +1,129 @@ +#include <etherboot.h> + +#include <fs.h> +#include <lib.h> +#include <sys_info.h> + +#define ENTER '\r' +#define ESCAPE '\x1b' + +#ifndef AUTOBOOT_FILE +#define autoboot() ((void) 0) /* nop */ +#endif + +#ifndef AUTOBOOT_DELAY +#define autoboot_delay() 0 /* success */ +#endif + +struct sys_info sys_info; + +static void init(void) +{ + collect_sys_info(&sys_info); + + printf("%s version %s\n", program_name, program_version); + +} + +static void boot(const char *line) +{ + char file[256], *param; + + /* Split filename and parameter */ + memcpy(file, line,256); +// file = strdup(line); + param = strchr(file, ' '); + if (param) { + *param = '\0'; + param++; + } + if (elf_load(&sys_info, file, param) == LOADER_NOT_SUPPORT){ + if (linux_load(&sys_info, file, param) == LOADER_NOT_SUPPORT) + printf("Unsupported image format\n"); + } +// free(file); +} + +#ifdef AUTOBOOT_FILE +#if AUTOBOOT_DELAY + +static inline int autoboot_delay(void) +{ + unsigned int timeout; + int sec, tmp; + int key; + + key = 0; + + printf("Press <Enter> for default boot, or <Esc> for boot prompt... "); + for (sec = AUTOBOOT_DELAY; sec>0 && key==0; sec--) { + printf("%d", sec); + timeout = currticks() + TICKS_PER_SEC; + while (currticks() < timeout) { + if (iskey()) { + key = getchar(); + if (key==ENTER || key==ESCAPE) + break; + } + } + for (tmp = sec; tmp; tmp /= 10) + printf("\b \b"); + } + if (key == 0) { + printf("timed out\n"); + return 0; /* success */ + } else { + putchar('\n'); + if (key == ESCAPE) + return -1; /* canceled */ + else + return 0; /* default accepted */ + } +} +#endif /* AUTOBOOT_DELAY */ + +static void autoboot(void) +{ + /* If Escape key is pressed already, skip autoboot */ + if (iskey() && getchar()==ESCAPE) + return; + + if (autoboot_delay()==0) { + printf("boot: %s\n", AUTOBOOT_FILE); + boot(AUTOBOOT_FILE); + } +} +#endif /* AUTOBOOT_FILE */ + +/* The main routine */ +int filo(void) +{ + char line[256]; + + /* Initialize */ + + init(); + + /* Try default image */ + autoboot(); + + /* The above didn't work, ask user */ + while (iskey()) + getchar(); +#ifdef AUTOBOOT_FILE + strncpy(line, AUTOBOOT_FILE, sizeof(line)-1); + line[sizeof(line)-1] = '\0'; +#else + line[0] = '\0'; +#endif + for (;;) { + printf("boot: "); + getline(line, sizeof line); +// BY LYH add "quit" to exit filo + if (strcmp(line,"quit")==0) break; +// if (memcmp(line,"quit",4)==0) break; + if (line[0]) + boot(line); + } + return 0; +} diff --git a/src/filo/main/lib.c b/src/filo/main/lib.c new file mode 100644 index 000000000..6e5020c7e --- /dev/null +++ b/src/filo/main/lib.c @@ -0,0 +1,56 @@ + +#include <etherboot.h> +#include <lib.h> + +char *strdup(const char *s) +{ + size_t sz = strlen(s) + 1; + char *d = allot(sz); + memcpy(d, s, sz); + return d; +} + +int isspace(int c) +{ + switch (c) { + case ' ': case '\f': case '\n': + case '\r': case '\t': case '\v': + return 1; + default: + return 0; + } +} + +unsigned int get_le32(const unsigned char *p) +{ + return ((unsigned int) p[0] << 0) + | ((unsigned int) p[1] << 8) + | ((unsigned int) p[2] << 16) + | ((unsigned int) p[3] << 24); +} + +unsigned int get_le16(const unsigned char *p) +{ + return ((unsigned int) p[0] << 0) + | ((unsigned int) p[1] << 8); +} +#if (DEBUG_ALL || DEBUG_ELFBOOT || DEBUG_ELFNOTE || DEBUG_LINUXBIOS || \ + DEBUG_MALLOC || DEBUG_MULTIBOOT || DEBUG_SEGMENT || DEBUG_SYS_INFO ||\ + DEBUG_TIMER || DEBUG_BLOCKDEV || DEBUG_PCI || DEBUG_LINUXLOAD ||\ + DEBUG_IDE || DEBUG_ELTORITO) + +// It is needed by debug for filo +void hexdump(const void *p, unsigned int len) +{ + int i; + const unsigned char *q = p; + + for (i = 0; i < len; i++) { + if (i%16==0) + printf("%04x: ", i); + printf("%02x%c", q[i], i%16==15 ? '\n' : i%8==7 ? '-' : ' '); + } + if (i%16 != 0) + putchar('\n'); +} +#endif diff --git a/src/filo/main/linuxbios_x.c b/src/filo/main/linuxbios_x.c new file mode 100644 index 000000000..37f9e3f13 --- /dev/null +++ b/src/filo/main/linuxbios_x.c @@ -0,0 +1,124 @@ +/* Adapted from Etherboot 5.1.8 */ +#include <sys_info.h> +#define DEBUG_THIS DEBUG_LINUXBIOS +#include <debug.h> + +#if 0 + +#define for_each_lbrec(head, rec) \ + for(rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \ + (((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes)) && \ + (rec->size >= 1) && \ + ((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \ + rec = (struct lb_record *)(((char *)rec) + rec->size)) + +static void convert_memmap(struct lb_memory *lbmem, struct sys_info *info) +{ + int lbcount; + int i; + + lbcount = lbmem->size / sizeof(struct lb_memory_range); + info->memrange = malloc(lbcount * sizeof(struct memrange)); + info->n_memranges = 0; + for (i = 0; i < lbcount; i++) { + debug("%#016Lx %#016Lx %d\n", + lbmem->map[i].start, lbmem->map[i].size, + (int) lbmem->map[i].type); + if (lbmem->map[i].type != LB_MEM_RAM) + continue; + info->memrange[info->n_memranges].base = lbmem->map[i].start; + info->memrange[info->n_memranges].size = lbmem->map[i].size; + info->n_memranges++; + } +} +static int read_lbtable(struct lb_header *head, struct sys_info *info) +{ + int retval = 0; + + /* Read linuxbios tables... */ + struct lb_record *rec; + + for_each_lbrec(head, rec) { + switch(rec->tag) { + case LB_TAG_MEMORY: + convert_memmap((struct lb_memory *) rec, info); + retval = 1; + break; + }; + } + return retval; +} +#endif +#if 0 +//Use func in Etherboot +static unsigned long count_lb_records(void *start, unsigned long length) +{ + struct lb_record *rec; + void *end; + unsigned long count; + count = 0; + end = ((char *)start) + length; + for(rec = start; ((void *)rec < end) && + ((signed long)rec->size <= (end - (void *)rec)); + rec = (void *)(((char *)rec) + rec->size)) { + count++; + } + return count; +} +static int find_lb_table(void *start, void *end, struct lb_header **result) +{ + unsigned char *ptr; + /* For now be stupid.... */ + for(ptr = start; (void *)ptr < end; ptr += 16) { + struct lb_header *head = (struct lb_header *)ptr; + if ( (head->signature[0] != 'L') || + (head->signature[1] != 'B') || + (head->signature[2] != 'I') || + (head->signature[3] != 'O')) { + continue; + } + if (head->header_bytes != sizeof(*head)) + continue; + debug("Found canidate at: %p\n", head); + if (ipchksum((uint16_t *)head, sizeof(*head)) != 0) + continue; + debug("header checksum o.k.\n"); + if (ipchksum((uint16_t *)(ptr + sizeof(*head)), head->table_bytes) != + head->table_checksum) { + continue; + } + debug("table checksum o.k.\n"); + if (count_lb_records(ptr + sizeof(*head), head->table_bytes) != + head->table_entries) { + continue; + } + debug("record count o.k.\n"); + *result = head; + return 1; + }; + return 0; +} +#endif +void collect_linuxbios_info(struct sys_info *info) +{ +#if 0 + struct lb_header *lb_table; + int found; + debug("Searching for LinuxBIOS tables...\n"); + found = 0; + if (!found) { + found = find_lb_table(phys_to_virt(0x00000), phys_to_virt(0x01000), &lb_table); + } + if (!found) { + found = find_lb_table(phys_to_virt(0xf0000), phys_to_virt(0x100000), &lb_table); + } + if (!found) + return; + + debug("Found LinuxBIOS table at: %p\n", lb_table); +#endif + info->firmware = "LinuxBIOS"; +#if 0 + read_lbtable(lb_table, info); +#endif +} diff --git a/src/filo/main/malloc_x.c b/src/filo/main/malloc_x.c new file mode 100644 index 000000000..99b309aa7 --- /dev/null +++ b/src/filo/main/malloc_x.c @@ -0,0 +1,45 @@ +#include <etherboot.h> + +#include <lib.h> + +void *calloc(size_t nmemb, size_t size) +{ + size_t alloc_size = nmemb * size; + void *mem; + + if (alloc_size < nmemb || alloc_size < size) { + printf("calloc overflow: %u, %u\n", nmemb, size); + return 0; + } + + mem = allot(alloc_size); + memset(mem, 0, alloc_size); + + return mem; +} + +void *realloc(void *mem, size_t size) +{ + size_t copy_size; + void *new_mem; + size_t *mark, addr; + + if (mem == 0) + return allot(size); + if (size == 0) { + forget(mem); + return 0; + } + + addr = virt_to_phys(mem); + mark = phys_to_virt(addr - sizeof(size_t)); + copy_size = *mark; + + if (size < copy_size) + copy_size = size; + /* XXX should optimze this */ + new_mem = allot(size); + memcpy(new_mem, mem, copy_size); + forget(mem); + return new_mem; +} diff --git a/src/filo/main/pci_x.c b/src/filo/main/pci_x.c new file mode 100644 index 000000000..0c88dff70 --- /dev/null +++ b/src/filo/main/pci_x.c @@ -0,0 +1,124 @@ +#ifdef CONFIG_PCI + +/* + * This program 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 2, or (at + * your option) any later version. + */ + +#include <etherboot.h> +#include <pci.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_PCI + +#include <debug.h> + +struct pci_device *dev_list; +int n_devs; + +static void pci_scan_bus(void) +{ + + unsigned int first_bus, first_devfn; + unsigned int devfn, bus, buses; + + uint32_t class; + uint16_t vendor, dev_id; + uint8_t hdr_type; + + + first_bus = 0; + first_devfn = 0; + + buses=256; + for (bus = first_bus; bus < buses; ++bus) { + for (devfn = first_devfn; devfn < 0xff; ++devfn) { + +#if 1 + if (PCI_FUNC(devfn) == 0) + pcibios_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type); + else if (!(hdr_type & 0x80)) /* not a multi-function device */ + continue; +#endif + + pcibios_read_config_word(bus,devfn, PCI_VENDOR_ID, &vendor); + if (vendor==0xffff || vendor==0) + continue; + + if (dev_list) { + dev_list[n_devs].bus = bus; + dev_list[n_devs].devfn = devfn; + dev_list[n_devs].vendor = vendor; + + pcibios_read_config_word(bus,devfn, PCI_DEVICE_ID, &dev_id); + dev_list[n_devs].dev_id = dev_id; + + pcibios_read_config_dword(bus,devfn, PCI_CLASS_REVISION, &class); + dev_list[n_devs].class = class; + + } + n_devs++; + } + first_devfn = 0; + } + first_bus = 0; + +} +#define DEBUG 0 + +void pci_init(void) +{ + /* Count devices */ + dev_list = 0; + n_devs = 0; + debug("Scanning PCI: "); + pci_scan_bus(); + debug("found %d devices\n", n_devs); + + /* Make the list */ + dev_list = allot(n_devs * sizeof(struct pci_device)); + n_devs = 0; + pci_scan_bus(); +#if DEBUG + { + int i; + for (i = 0; i < n_devs; i++) { + printf("%02x:%02x.%x %04x:%04x %04x %02x\n", + dev_list[i].bus, + PCI_SLOT(dev_list[i].devfn), + PCI_FUNC(dev_list[i].devfn), + dev_list[i].vendor, + dev_list[i].dev_id,((dev_list[i].class)>>16), + ((dev_list[i].class)>>8 & 0xff)); + } + } +#endif +} + +struct pci_device *pci_find_device_2(int vendor, int device, int devclass, int devclass2, int prog_if, int index) +{ + int i; + + for (i=0; i<n_devs; i++) { + if (vendor < 0 || vendor==dev_list[i].vendor) + if (device < 0 || device==dev_list[i].dev_id) + if (devclass < 0 || devclass==((dev_list[i].class)>>16) || devclass2==((dev_list[i].class)>>16)) + if (prog_if < 0 || prog_if==((dev_list[i].class)>>8 & 0xff)) { + if (index == 0) + return &dev_list[i]; + index--; + } + } + + return NULL; +} + + +struct pci_device *pci_find_device(int vendor, int device, int devclass, int prog_if, int index) +{ + return pci_find_device_2(vendor, device, devclass, devclass, prog_if, index); +} + +#endif diff --git a/src/filo/main/printf_x.c b/src/filo/main/printf_x.c new file mode 100644 index 000000000..517e2dd25 --- /dev/null +++ b/src/filo/main/printf_x.c @@ -0,0 +1,400 @@ +/* Adapted from LinuxBIOS */ + +/* + * + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include "etherboot.h" +#include <stdarg.h> +#include <lib.h> + +/* haha, don't need ctype.c */ +#define isdigit(c) ((c) >= '0' && (c) <= '9') +#define is_digit isdigit +#define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) +#define islower(c) ((c) >= 'a' && (c) <= 'z') +#define toupper(c) __toupper(c) + +static inline unsigned char __toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + + +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((*cp == 'x') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +long long simple_strtoll(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoull(cp+1,endp,base); + return simple_strtoull(cp,endp,base); +} + +unsigned long long strtoull_with_suffix(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result; + + if (!endp) { + printf("%s must be called with endp\n", __FUNCTION__); + return 0; + } + result = simple_strtoull(cp, endp, base); + switch (toupper(**endp)) { + case 'K': + result <<= 10; + ++*endp; + break; + case 'M': + result <<= 20; + ++*endp; + break; + case 'G': + result <<= 30; + ++*endp; + break; + } + return result; +} + +#if 0 +// it can be used to substitute the vsprintf.c in etherboot. And it has better +// support in numeric output suppot +//When you want to debug filo, you need to enable it and disable vsprintf.c +// to get output from filo +// BY LYH + +static int skip_atoi(const char **s) +{ + int i=0; + + while (is_digit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +static int number(int (*tx_byte)(int byte), long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + int count = 0; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + tx_byte(' '), count++; + if (sign) + tx_byte(sign), count++; + if (type & SPECIAL) { + if (base==8) + tx_byte('0'), count++; + else if (base==16) { + tx_byte('0'), count++; + tx_byte(digits[33]), count++; + } + } + if (!(type & LEFT)) + while (size-- > 0) + tx_byte(c), count++; + while (i < precision--) + tx_byte('0'), count++; + while (i-- > 0) + tx_byte(tmp[i]), count++; + while (size-- > 0) + tx_byte(' '), count++; + return count; +} + + +int vtxprintf(int (*tx_byte)(int byte), const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + int count; + + for (count=0; *fmt ; ++fmt) { + if (*fmt != '%') { + tx_byte(*fmt), count++; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (is_digit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (is_digit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + tx_byte(' '), count++; + tx_byte((unsigned char) va_arg(args, int)), count++; + while (--field_width > 0) + tx_byte(' '), count++; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = "<NULL>"; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + tx_byte(' '), count++; + for (i = 0; i < len; ++i) + tx_byte(*s++), count++; + while (len < field_width--) + tx_byte(' '), count++; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + count += number(tx_byte, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = count; + } else { + int * ip = va_arg(args, int *); + *ip = count; + } + continue; + + case '%': + tx_byte('%'), count++; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + tx_byte('%'), count++; + if (*fmt) + tx_byte(*fmt), count++; + else + --fmt; + continue; + } + if (qualifier == 'L') + num = va_arg(args, unsigned long long); + else if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (short) num; + } else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + count += number(tx_byte, num, base, field_width, precision, flags); + } + return count; +} + +/* FIXME this global makes vsprintf non-reentrant + */ +static char *str_buf; +static int str_tx_byte(int byte) +{ + *str_buf = byte; + str_buf++; + return byte; +} + +int vsprintf(char * buf, const char *fmt, va_list args) +{ + int i; + str_buf = buf; + i = vtxprintf(str_tx_byte, fmt, args); + /* maeder/Ispiri -- The null termination was missing a deference */ + /* and was just zeroing out the pointer instead */ + *str_buf = '\0'; + return i; +} + +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + va_end(args); + return i; +} + +void printf(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vtxprintf(putchar, fmt, args); + va_end(args); + return i; +} +#endif diff --git a/src/filo/usb/debug_x.c b/src/filo/usb/debug_x.c new file mode 100644 index 000000000..95a80b0a9 --- /dev/null +++ b/src/filo/usb/debug_x.c @@ -0,0 +1,433 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#include "usb.h" +#include "uhci.h" +#include "debug_x.h" + +//#include <string.h> + + +void dump_link( link_pointer_t *link, char *prefix) +{ + DPRINTF("%saddr: %08x\n", prefix, MEM_ADDR(link->link) ); + DPRINTF("%s raw addr: %04x\n", prefix, (link->link) <<4 ); + DPRINTF("%sterminate: %x\n", prefix, link->terminate); + DPRINTF("%squeue: %x\n", prefix, link->queue); + DPRINTF("%sdepth: %x\n", prefix, link->depth); +} + +void dump_frame_list( link_pointer_t *addr, char *prefix) +{ + int i; + + DPRINTF("%sFRAMELIST:\n",prefix); + + for(i=0;i<10; i++) { + dump_link(addr+i, prefix); + if(addr[i].queue) + dump_queue_head( MEM_ADDR(addr[i].link), ""); + else + dump_td( MEM_ADDR(addr[i].link), ""); + } +} + +void dump_hex(uchar *data, int len, char *prefix) +{ + int i=0; + + while(i<len) { + if(!i%16) { + DPRINTF("\n%s %04x: ", prefix, i); + } + else { + DPRINTF(": "); + } + + DPRINTF("%02x", data[i++]); + } + + DPRINTF("\n"); +} + +void dump_uhci(uint32_t port) +{ +#if 0 + unsigned long value; +#endif + + DPRINTF("HCI at %08x\n", port); +#if 0 + value = inw(port); + DPRINTF("Command: %04x\n", value); + + value = inw(port+2); + DPRINTF("USBSTS: %04x\n", value); + + value = inw(port+4); + DPRINTF("USBINTR: %04x\n", value); + + value = inw(port+6); + DPRINTF("FRNUM: %04x\n", value); + + value = inl(port+8); + DPRINTF("FLBASEADD: %08x\n", value); + + value = inb(port+0x0c); + DPRINTF("SOFMOD: %02x\n", value); + + value = inw(port+0x10); + DPRINTF("PORTSTS1: %04x\n", value); + + value = inw(port+0x12); + DPRINTF("PORTSTS2: %04x\n", value); + +#endif + +} + +void dump_td( td_t *td, char *prefix) +{ + char newpre[64]; + + newpre[0]='\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%sTD(%08x):\n", prefix, td); + + switch(td->packet_type) { + case SETUP_TOKEN: + DPRINTF("%stype: SETUP\n", prefix); + break; + case OUT_TOKEN: + DPRINTF("%stype: OUT\n", prefix); + break; + case IN_TOKEN: + DPRINTF("%stype: IN\n", prefix); + break; + default: + DPRINTF("%stype: INVALID (%02x)\n", prefix, td->packet_type); + break; + } + + DPRINTF("%sretries: %x\n", prefix, td->retrys); + + if(td->isochronous) { + DPRINTF("%sisochronous\n", prefix); + } + + if(td->interrupt) { + DPRINTF("%sIOC\n", prefix); + } + + if(td->lowspeed) { + DPRINTF("%slowspeed\n", prefix); + } + + if(td->detect_short) { + DPRINTF("%sDETECT_SHORT\n", prefix); + } + + DPRINTF("%sactive: %04x\n", prefix, td->active); + DPRINTF("%sdevice_addr: %02x\n", prefix, td->device_addr); + DPRINTF("%sendpoint: %1x\n", prefix, td->endpoint); + DPRINTF("%sdata_toggle: %1x\n", prefix, td->data_toggle); + DPRINTF("%smax_transfer: %x\n", prefix, td->max_transfer); + DPRINTF("%sactual: %x\n", prefix, td->actual); + DPRINTF("%slink:\n", prefix); + + if(td->stall) { + DPRINTF("%sSTALL\n", prefix); + } + + if(td->bitstuff) { + DPRINTF("%sBITSTUFF ERROR\n", prefix); + } + + if(td->crc) { + DPRINTF("%sCRC ERROR\n", prefix); + } + + if(td->nak) { + DPRINTF("%sNAK ERROR\n", prefix); + } + + if(td->babble) { + DPRINTF("%sBABBLE ERROR\n", prefix); + } + + if(td->buffer_error) { + DPRINTF("%sBUFFER ERROR\n", prefix); + } + + if(MEM_ADDR(td->link.link) == td) { + DPRINTF("link loops back to me!\n"); + return; + } + + dump_link(&(td->link), newpre); + if(!td->link.terminate) { + if(td->link.queue) + dump_queue_head(MEM_ADDR(td->link.link), prefix ); + else + dump_td(MEM_ADDR(td->link.link), prefix); + } +} + +void dump_queue_head( queue_head_t *qh, char *prefix) +{ + char newpre[64]; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%sQUEUE HEAD(%x):\n", prefix, qh); + DPRINTF("%sdepth:\n", prefix); + dump_link( &(qh->depth), newpre); + + if(!qh->depth.terminate) { + if(qh->depth.queue) { + dump_queue_head(MEM_ADDR(qh->depth.link), newpre); + } + else { + dump_td( MEM_ADDR(qh->depth.link), newpre); + } + } + + + DPRINTF("%sbredth:\n", prefix); + dump_link( &(qh->bredth), newpre); + if(!qh->bredth.terminate) { + if(qh->bredth.queue) { + dump_queue_head(MEM_ADDR(qh->bredth.link), newpre); + } + else { + dump_td( MEM_ADDR(qh->bredth.link), newpre); + } + } +} + +void dump_transaction( transaction_t *trans, char *prefix) +{ + char newpre[64]; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + + DPRINTF("%s TRANSACTION(%x):\n", prefix, trans); + dump_queue_head( trans->qh, newpre); + + DPRINTF("%s TDs:\n", prefix); + dump_td( trans->td_list, newpre); + + DPRINTF("\n"); + if(trans->next) + dump_transaction(trans->next, prefix); +} + +void dump_usbdev( usbdev_t *dev, char *prefix) +{ + char newpre[64]; + int i; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%saddress: %x\n", prefix, dev->address); + DPRINTF("%sclass: %x\n", prefix, dev->class); + DPRINTF("%ssubclass: %x\n", prefix, dev->subclass); + DPRINTF("%sbulk_in: %x\n", prefix, dev->bulk_in); + DPRINTF("%sbulk_out: %x\n", prefix, dev->bulk_out); + DPRINTF("%sinterrupt: %x\n", prefix, dev->interrupt); + + DPRINTF("%sep toggle:\n", prefix); + for(i=0;i<MAX_EP;i++) { + DPRINTF("%s%x\n", newpre, dev->toggle[i]); + } + + DPRINTF("%sep max_packet:\n", prefix); + for(i=0;i<MAX_EP;i++) { + DPRINTF("%s%x\n", newpre, dev->max_packet[i]); + } +} + +void dump_all_usbdev(char *prefix) +{ + int i; + + for(i=0;i<MAX_USB_DEV;i++) { + if(usb_device[i].address) { + DPRINTF("%sDEVICE: %x\n", prefix, i); + dump_usbdev( usb_device +i, prefix); + } + } +} + +void dump_device_descriptor( device_descriptor_t *des, char *prefix) +{ + DPRINTF("%sbLength: %02x\n", prefix, des->bLength); + DPRINTF("%stype: %02x\n", prefix, des->type); + DPRINTF("%sbcdVersion: %1x%1x\n", prefix, des->bcdVersion[1], des->bcdVersion[0]); + DPRINTF("%sClass: %02x\n",prefix, des->Class); + DPRINTF("%sSubClass: %02x\n",prefix, des->SubClass); + DPRINTF("%sprotocol: %02x\n",prefix, des->protocol); + DPRINTF("%smax_packet: %x\n",prefix, des->max_packet); + DPRINTF("%sidVendor: %04x\n", prefix, des->idVendor); + DPRINTF("%sidProduct: %04x\n", prefix, des->idProduct); + DPRINTF("%sbcdDeviceVersion: %1x%1x\n", prefix, des->bcdDevice[1], des->bcdDevice[0]); + DPRINTF("%siManufacturor: %02x\n", prefix, des->iManufacturor); + DPRINTF("%siProduct: %02x\n", prefix, des->iProduct); + DPRINTF("%siSerial: %02x\n", prefix, des->iSerial); + DPRINTF("%sbNumConfig: %02x\n", prefix, des->bNumConfig); + +} + +void dump_interface_descriptor( interface_descriptor_t *iface, char *prefix) +{ + + DPRINTF("%sbLength: %02x\n", prefix, iface->bLength); + DPRINTF("%stype: %02x\n", prefix, iface->type); + DPRINTF("%sbInterfaceNumber: %02x\n", prefix, iface->bInterfaceNumber); + DPRINTF("%sbAlternateSetting: %02x\n", prefix, iface->bAlternateSetting); + DPRINTF("%sbNumEndpoints: %02x\n", prefix, iface->bNumEndpoints); + DPRINTF("%sbInterfaceClass: %02x\n", prefix, iface->bInterfaceClass); + DPRINTF("%sbInterfaceSubClass: %02x\n", prefix, iface->bInterfaceSubClass); + DPRINTF("%sbInterfaceProtocol: %02x\n", prefix, iface->bInterfaceProtocol); + DPRINTF("%siInterface: %02x\n", prefix, iface->iInterface); +} + +void dump_endpoint_descriptor( endpoint_descriptor_t *ep, char *prefix) +{ + + DPRINTF("%sbLength: %02x\n", prefix, ep->bLength); + DPRINTF("%stype: %02x\n", prefix, ep->type); + DPRINTF("%sbEndpointAddress: %02x\n", prefix, ep->bEndpointAddress); + DPRINTF("%sbmAttributes: %02x\n", prefix, ep->bmAttributes); + DPRINTF("%swMaxPacketSize: %02x\n", prefix, ep->wMaxPacketSize); + DPRINTF("%sbInterval: %02x\n", prefix, ep->bInterval); +} + +void dump_config_descriptor( uchar *des, char *prefix) // YES uchar * +{ + config_descriptor_t *config; + interface_descriptor_t *iface; + endpoint_descriptor_t *ep; + char newpre[64]; + int i; + + memset(newpre,0,sizeof(newpre)); + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + config = (config_descriptor_t *) des; + iface = (interface_descriptor_t *) (des + config->bLength); + ep = (endpoint_descriptor_t *) (des + config->bLength + iface->bLength); + + // now, the config itself + DPRINTF("%sbLength: %02x\n", prefix, config->bLength); + DPRINTF("%stype: %02x\n", prefix, config->type); + DPRINTF("%swTotalLength: %04x\n", prefix, config->wTotalLength); + DPRINTF("%sbNumInterfaces: %02x\n", prefix, config->bNumInterfaces); + DPRINTF("%sbConfigurationValue: %02x\n", prefix, config->bConfigurationValue); + DPRINTF("%siConfiguration: %02x\n", prefix, config->iConfiguration); + DPRINTF("%sbmAttributes: %02x\n", prefix, config->bmAttributes); + + DPRINTF("%sbMaxPower: %02x\n", prefix, config->bMaxPower); + + DPRINTF("\n%sInterface(%x):\n", prefix, iface); + dump_interface_descriptor(iface, newpre); + + newpre[1] = '\t'; + strcpy(newpre+2, prefix); + + for(i=0; i<iface->bNumEndpoints; i++) { + DPRINTF("\n%sEndpoint (%x):\n", newpre+1, ep+i); + dump_endpoint_descriptor( ep+i, newpre); + } +} + +// Some control message bmRequestType defines +#define CTRL_DEVICE 0 +#define CONTROL_INTERFACE 1 +#define CONTROL_ENDPOINT 2 +#define CONTROL_OTHER 3 +#define CONTROL_RECIPIENT_MASK 0x1f + +#define CONTROL_TYPE_STD 0 +#define CONTROL_TYPE_CLASS 0x20 +#define CONTROL_CLASS_VENDOR 0x40 +#define CONTROL_CLASS_MASK 0x60 + +#define CONTROL_OUT 0 +#define CONTROL_IN 0x80 +#define CONTROL_DIR_MASK 0x80 + +// bRequest values +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 + +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 + +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 + +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 + +#define SYNC_FRAME 12 + +// descriptor types +#define DEVICE_DESC 1 +#define CONFIGURATION_DESC 2 +#define STRING_DESC 3 +#define INTERFACE_DESC 4 +#define ENDPOINT_DESC 5 +#define OTHERSPEED_DESC 7 +#define POWER_DESC 8 + +// features +#define FEATURE_HALT 0 +void dump_ctrlmsg( ctrl_msg_t *msg, char *prefix) +{ + DPRINTF("%sbmRequestType: %02x\n", prefix, msg->bmRequestType); + DPRINTF("%sbRequest: %02x\n", prefix, msg->bRequest); + DPRINTF("%swValue: %04x\n", prefix, msg->wValue); + DPRINTF("%swIndex: %04x\n", prefix, msg->wIndex); + DPRINTF("%swLength: %04x\n", prefix, msg->wLength); +} + +#endif diff --git a/src/filo/usb/debug_x.h b/src/filo/usb/debug_x.h new file mode 100644 index 000000000..109daae0e --- /dev/null +++ b/src/filo/usb/debug_x.h @@ -0,0 +1,18 @@ +#ifndef _DEBUG_X_H +#define _DEBUG_X_H + +void dump_hex(uchar *data, int len, char *prefix); +void dump_link( link_pointer_t *link, char *prefix); +void dump_td( td_t *td, char *prefix); +void dump_queue_head( queue_head_t *qh, char *prefix); +void dump_transaction( transaction_t *trans, char *prefix); +void dump_usbdev( usbdev_t *dev, char *prefix); +void dump_uhci(uint32_t port); +//void dump_all_usbdev(char *prefix); +void dump_device_descriptor( device_descriptor_t *des, char *prefix); +void dump_interface_descriptor( interface_descriptor_t *iface, char *prefix); +void dump_endpoint_descriptor( endpoint_descriptor_t *ep, char *prefix); +void dump_config_descriptor( uchar *des, char *prefix); +void dump_ctrlmsg( ctrl_msg_t *msg, char *prefix); + +#endif diff --git a/src/filo/usb/ohci.c b/src/filo/usb/ohci.c new file mode 100644 index 000000000..5c84c6376 --- /dev/null +++ b/src/filo/usb/ohci.c @@ -0,0 +1,1437 @@ +#ifdef USB_DISK + +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#define DEBUG_TD 0 +#define DEBUG_ED 0 + + +#include "usb.h" +#include "ohci.h" + + +extern int usec_offset; + +ohci_regs_t *ohci_regs; + +// It will clear the enable bit +void ohc_clear_stat(uchar dev) +{ + uint32_t value; + ohci_regs = (ohci_regs_t *)hc_base[dev]; + + value = readl(&ohci_regs->cmdstatus); + writel(value, &ohci_regs->cmdstatus); +} + +void clear_oport_stat(uint32_t port) +{ + uint32_t value; + + value = readl(port); + writel(value, port); +} + +void oport_suspend( uint32_t port) +{ + writel( RH_PS_PSS, port); + +} +void oport_wakeup( uint32_t port) +{ + writel( RH_PS_POCI, port); + +} +#if 0 +void oport_resume( uint32_t port) +{ + uint32_t value; + + value = readl(port); + value |= 0x40; + writel(value, port); + udelay(20000+usec_offset); + value &= ~0x40; + writel(value, port); + + do { + value = readl(port); + } while(value & 0x40); +} +#endif +void oport_enable( uint32_t port) +{ + uint32_t value; + + value = readl(port); + + if((value & RH_PS_CCS)) { // if connected + writel( RH_PS_PES, port); + udelay(10); + writel( RH_PS_PESC, port); // Clear Change bit + } + +} + + + +void oport_disable( uint32_t port) +{ + writel( RH_PS_CCS, port); +} + +void oport_reset(uint32_t port) +{ + + uint32_t value; + + writel( RH_PS_PRS, port); + do { + value = readl( port ); + } while (!(value & RH_PS_PRSC) ); + writel(RH_PS_PRSC, port); //Clear Change bit + +} + +void oport_reset_long(uint32_t port) +{ + oport_reset(port); +} + +#if 0 + +int ohc_stop(uchar dev) +{ + unsigned short tmp; + uint32_t ctl; + + ohci_regs = hc_base[dev]; + + ctl = readl( &ohci_regs->control); + ctl &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); + writel (ctl, &ohci_regs->control); + + return(0); +} +#endif + + +#define MAX_OHCI_TD 32 + +ohci_td_t *_ohci_td; +uint8_t _ohci_td_tag[MAX_OHCI_TD]; //1: used, 0:unused + +void init_ohci_td(){ + _ohci_td = allot2(sizeof(ohci_td_t)*MAX_OHCI_TD, 0x1f); // 32 byte aligna + if(_ohci_td==0) { + printf("init_ohci_td: NOMEM\n"); + } + memset(_ohci_td_tag, 0, sizeof(_ohci_td_tag)); +} + +ohci_td_t *td_alloc(ohci_t *ohci, int memflag){ + int i; + ohci_td_t *td; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==1) continue; + td = &_ohci_td[i]; + memset(td, 0, sizeof(ohci_td_t)); + td->td_dma = (void *)virt_to_phys(td); + _ohci_td_tag[i] = 1; + return td; + } + printf("td_alloc: no free slot\n"); + return 0; +} + +int td_free(ohci_t *ohci, ohci_td_t *td) { + int i; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==0) continue; + if(&_ohci_td[i] == td ) { + _ohci_td_tag[i] = 0; + return 1; + } + } + return 0; +} + + +struct ohci_td * +dma_to_td (struct ohci * hc, void *td_dma) +{ + int i; + ohci_td_t *td; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==0) continue; + td = &_ohci_td[i]; + if(td->td_dma == td_dma ) { + return td; + } + } + printf("dma_to_td: can not find td\n"); + return 0; + +} + +ohci_t _ohci_x[MAX_CONTROLLERS]; + +void ohci_init(void) +{ + init_ohci_td(); +} + +static int ohci_get_current_frame_number (struct usbdev *usb_dev) +{ + ohci_t * ohci = &_ohci_x[usb_dev->controller]; + + return le16_to_cpu (ohci->hcca->frame_no); +} + + + +static u32 roothub_a (struct ohci *hc) + { return readl (&hc->regs->roothub.a); } +static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) + { return readl (&hc->regs->roothub.portstatus[i]);} + + +#if DEBUG_USB==1 + +#define OHCI_VERBOSE_DEBUG + +# define dbg(...) \ + do { printf(__VA_ARGS__); printf("\n"); } while (0) + +static void urb_print (struct urb * urb, char * str, int small) +{ + unsigned int pipe= urb->pipe; + + if (!urb->dev ) { + dbg("%s URB: no dev", str); + return; + } + +#ifndef OHCI_VERBOSE_DEBUG + if (urb->status != 0) +#endif + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", + str, + ohci_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + urb->transfer_flags, + urb->actual_length, + urb->transfer_buffer_length, + urb->status, urb->status); +#ifdef OHCI_VERBOSE_DEBUG +// if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf ("ohci.c: cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((u8 *) urb->setup_packet) [i]); + printf ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printf("ohci.c: data(%d/%d):", + urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe)? + urb->transfer_buffer_length: urb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((u8 *) urb->transfer_buffer) [i]); + printf ("%s stat:%d\n", i < len? "...": "", urb->status); + } +// } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t * ohci, char * str) { + int i, j; + u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf ("ohci.c: %s branch int %2d(%2x):", str, i, i); +#if 0 + while (*ed_p != 0 && j--) { + ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p)); + printk (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } +#endif + printf ("\n"); + } +} +static void ohci_dump_intr_mask (char *label, u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} +static void maybe_print_eds (char *label, u32 value) +{ + if (value) + dbg ("%s %08x", label, value); +} +static char *hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} +// dump control and status registers +static void ohci_dump_status (ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + // intrdisable always same as intrenable + // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + u32 temp, ndp, i; + + temp = roothub_a (controller); + if (temp == ~(u32)0) + return; + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller usb-%x state", controller->regs); + + // dumps some of the state we know about + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} +void ohci_dump_x(uchar controller) +{ + ohci_t *ohci; + ohci = &_ohci_x[controller]; + ohci_dump (ohci, 1); +} +#endif + +/* link an ed into one of the HC chains */ + +/* ED is only enqueued and dequeued by HCD + So ep_link may only to be called two times for every device (function) -- ed, one for controled and one for bulked + one ohci may have several controled and bulked. +*/ + +int ep_link (ohci_t * ohci, ed_t * edi) +{ + volatile ed_t * ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { +// debug("ep_link control 21 ed->dma = %x\n", (uint32_t)ed->dma); + writel ((uint32_t)ed->dma, &ohci->regs->ed_controlhead); + } else { +// debug("ep_link control 22 ed->dma = %x\n", (uint32_t)ed->dma); + ohci->ed_controltail->hwNextED = cpu_to_le32 ((uint32_t)ed->dma); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail) { + /* enable control ed list */ + ohci->hc_control |= OHCI_CTRL_CLE; //5 + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + // debug("ep_link control 31 ed->dma = %x\n", (uint32_t)ed->dma); + writel ((uint32_t)ed->dma, &ohci->regs->ed_bulkhead); + } else { + // debug("ep_link control 32 ed->dma = %x\n", (uint32_t)ed->dma); + ohci->ed_bulktail->hwNextED = cpu_to_le32 ((uint32_t)ed->dma); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail) { + /* enable bulk ed list */ + ohci->hc_control |= OHCI_CTRL_BLE; //5 + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +ed_t * ep_add_ed ( + usbdev_t * usb_dev, + unsigned int pipe, + int interval, + int load, + int mem_flags +) +{ + ohci_t * ohci = &_ohci_x[usb_dev->controller]; + ohci_td_t * td; + ed_t * ed; + unsigned long flags; + int i; + + /* We use preallocate ed in ohci struct + numbering rule ??? + */ + i = (usb_pipeendpoint (pipe) << 1) |(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe)); + ed = (ed_t *)&ohci->ed[i]; + +// debug("ep_add_ed: usb_dev port=%x, controller = %d ohci=%x ohci->ed=%x ed=%x ed->dma=%x\n", usb_dev->port,usb_dev->controller, ohci, ohci->ed, ed, ed->dma); + + if (ed->state == ED_NEW) { + ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (ohci, 0); + + ed->hwTailP = cpu_to_le32 ((uint32_t)td->td_dma); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci->ed_cnt++; // we will be used to calcaulate next pipe + +// debug("ep_add_ed 1 td=%x dma=%x ed->dma=%x ed->hwHeadP=%x ed->hwTailP=%x\n", td, td->td_dma, ed->dma, ed->hwHeadP, ed->hwTailP); + + } + + ohci->dev[usb_pipedevice (pipe)] = usb_dev; // marked the ed to this dev + + ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16); + +// debug("ep_add_ed: pipe=%x ed_num=%d ed->dma=%x ed->hwInfo=%x ed->hwHeadP=%x ed->hwTailP=%x\n", pipe, i, ed->dma, ed->hwINFO, ed->hwHeadP, ed->hwTailP); + + return ed; +} + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +void +td_fill (ohci_t * ohci, unsigned int info, + void *data, int len, + struct urb * urb, int index) // *data should dma address of buffer +{ + ohci_td_t * td, * td_pt; + urb_priv_t * urb_priv = urb->hcpriv; + + if (index >= urb_priv->length) { + printf("internal OHCI error: TD index > length"); + return; + } + + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = dma_to_td (ohci, + (void *)(le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf)); + +// debug("td_fill 2 td = %x, dma=%x , ed->hwHeadP=%x, ed->hwTailP=%x \n", td, td->td_dma, urb_priv->ed->hwHeadP, urb_priv->ed->hwTailP ); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->urb = urb; + td->data_dma = data; + if (!len) + data = 0; + + + td->hwINFO = cpu_to_le32 (info); + td->hwCBP = cpu_to_le32 ((uint32_t)data); + if (data) + td->hwBE = cpu_to_le32 ((uint32_t)data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = cpu_to_le32 ((uint32_t)td_pt->td_dma); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; + // debug("td_fill 4 td->td_dma=%x, td->hwINFO=%x\n", td->td_dma, td->hwINFO ); + // debug("td_fill 5 ed->dma=%x, ed->hwHeadP=%x, ed->hwTailP=%x \n", urb_priv->ed->dma, urb_priv->ed->hwHeadP, urb_priv->ed->hwTailP ); +} + +/* prepare all TDs of a transfer */ + +void td_submit_urb (struct urb * urb) +{ + urb_priv_t * urb_priv = urb->hcpriv; + ohci_t * ohci = (ohci_t *) &_ohci_x[urb->dev->controller]; + void * data; + int data_len = urb->transfer_buffer_length; + int cnt = 0; + u32 info = 0; + unsigned int toggle = 0; + void *setup_buffer; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); + } + + urb_priv->td_cnt = 0; + + if (data_len) { + data = (void *)virt_to_phys(urb->transfer_buffer); + } else + data = 0; + switch (usb_pipetype (urb->pipe)) { + case PIPE_BULK: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); + cnt++; +#if 0 + /* If the transfer size is multiple of the pipe mtu, + * we may need an extra TD to create a empty frame + * Note : another way to check this condition is + * to test if(urb_priv->length > cnt) - Jean II */ + if ((urb->transfer_flags & USB_ZERO_PACKET) && + usb_pipeout (urb->pipe) && + (urb->transfer_buffer_length != 0) && + ((urb->transfer_buffer_length % maxps) == 0)) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt); + cnt++; + } +#endif + +// debug("td_submit_urb 2 -- set OHCI_BLF\n"); + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + break; + + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + setup_buffer = (void *)virt_to_phys(urb->setup_packet); +// debug("td_sumbit_urb 11 setup_buffer = %x\n", setup_buffer); + td_fill (ohci, info, setup_buffer , 8, urb, cnt++); + if (data_len > 0) { + info = usb_pipeout (urb->pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, urb, cnt++); + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, urb, cnt++); +// debug("td_sumbit_urb 11 data = %x\n", data); + +// debug("td_submit_urb 2 -- set OHCI_CLF\n"); + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + break; + + } + if (urb_priv->length != cnt) { + debug("TD LENGTH %d != CNT %d", urb_priv->length, cnt); + } +} + +/* free HCD-private data associated with this URB */ + +void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv) +{ + int i; + int last = urb_priv->length - 1; + int len; + struct ohci_td *td; + + if (last >= 0) { +#if 0 + /* ISOC, BULK, INTR data buffer starts at td 0 + * CTRL setup starts at td 0 */ + td = urb_priv->td [0]; + + len = td->urb->transfer_buffer_length; + + /* unmap CTRL URB setup */ + if (usb_pipecontrol (td->urb->pipe)) { +// it should be freed in usb_control_msg_x +// forget2((void *)phys_to_virt((uint32_t)td->data_dma)); // 8 bytes + + /* CTRL data buffer starts at td 1 if len > 0 */ + if (len && last > 0) + td = urb_priv->td [1]; + } + + /* unmap data buffer */ + if (len && td->data_dma) { +// Don't need +// forget2((void *)phys_to_virt((uint32_t)td->data_dma)); + } +#endif + + for (i = 0; i <= last; i++) { + td = urb_priv->td [i]; + if (td) + td_free (hc, td); + } + } +#if URB_PRE_ALLOCATE!=1 + forget2((void *)urb_priv); +#endif +} + +/* get a transfer request */ + +int ohci_submit_urb (struct urb * urb) +{ + ohci_t * ohci; + ed_t * ed; + urb_priv_t * urb_priv; + unsigned int pipe = urb->pipe; + int i, size = 0; + int mem_flags = 0; + + if (!urb->dev) + return -ENODEV; + + if (urb->hcpriv) /* urb already in use */ + return -EINVAL; + + + ohci = (ohci_t *) &_ohci_x[urb->dev->controller]; +// printf("ohci_submit_urb: urb->dev port=%x, controller = %d ohci=%x ohci->ed=%x ohci->hcca=%x\n", urb->dev->port,urb->dev->controller, ohci, ohci->ed, ohci->hcca); + +#if DEBUG_USB==1 +// urb_print (urb, "SUB", usb_pipein (pipe)); +#endif + + +#if 0 + /* handle a request to the virtual root hub */ + if (usb_pipedevice (pipe) == ohci->rh.devnum) + return rh_submit_urb (urb); + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + usb_dec_dev_use (urb->dev); + return -ESHUTDOWN; + } +#endif + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { + return -ENOMEM; + } +// debug("ohci_submit_usb: ed->dma=%x\n", ed->dma); + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (urb->transfer_buffer_length - 1) / 4096 + 1; +#if 0 + /* If the transfer size is multiple of the pipe mtu, + * we may need an extra TD to create a empty frame + * Jean II */ + if ((urb->transfer_flags & USB_ZERO_PACKET) && + usb_pipeout (pipe) && + (urb->transfer_buffer_length != 0) && + ((urb->transfer_buffer_length % maxps) == 0)) + size++; +#endif + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (urb->transfer_buffer_length == 0)? 2: + (urb->transfer_buffer_length - 1) / 4096 + 3; + break; + } + + /* allocate the private part of the URB */ +#if URB_PRE_ALLOCATE!=1 + urb_priv = allot2 (sizeof (urb_priv_t) + size * sizeof (ohci_td_t *), 0xff); + if (urb_priv == 0) { + printf("ohci_submit_usb: urb_priv allocated no mem\n"); + return -ENOMEM; + } +#else + urb_priv = ohci->urb_priv; +#endif + memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (ohci_td_t *)); + + /* fill the private part of the URB */ + urb_priv->length = size; + urb_priv->ed = ed; + + /* allocate the TDs (updating hash chains) */ + for (i = 0; i < size; i++) { + urb_priv->td[i] = td_alloc (ohci, 0); + if (!urb_priv->td[i]) { + urb_priv->length = i; + urb_free_priv (ohci, urb_priv); + return -ENOMEM; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (ohci, urb_priv); + return -EINVAL; + } + + urb->actual_length = 0; + urb->hcpriv = urb_priv; + urb->status = USB_ST_URB_PENDING; + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) { + ep_link (ohci, ed); + } + + /* fill the TDs and link it to the ed */ + td_submit_urb (urb); + +#if 0 + /* drive timeouts by SF (messy, but works) */ + writel (OHCI_INTR_SF, &ohci->regs->intrenable); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +#endif + + return 0; +} +/* calculate the transfer length and update the urb */ + +void dl_transfer_length(ohci_td_t * td) +{ + u32 tdINFO, tdBE, tdCBP; + struct urb * urb = td->urb; + urb_priv_t * urb_priv = urb->hcpriv; + + tdINFO = le32_to_cpup (&td->hwINFO); + tdBE = le32_to_cpup (&td->hwBE); + tdCBP = le32_to_cpup (&td->hwCBP); + + + if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == urb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + urb->actual_length += tdBE - (uint32_t)td->data_dma + 1; + else + urb->actual_length += tdCBP - (uint32_t)td->data_dma; + } + + } + +// debug("td->td_dma=%x, urb->actual_length=%d\n", td->td_dma, urb->actual_length); +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +ohci_td_t * dl_reverse_done_list (ohci_t * ohci) +{ + u32 td_list_hc; + ohci_td_t * td_rev = NULL; + ohci_td_t * td_list = NULL; + urb_priv_t * urb_priv = NULL; + uint32_t value; + u32 td_list_hc2; + int timeout = 1000000; //1 second +// unsigned long flags; +// Here need to process across the frame tds + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + td_list_hc2 = readl(&ohci->regs->donehead); +// debug("ohci->hcca->done_head = %x ohci->hcca=%x ohci=%x ohci->regs->donehead=%x\n", td_list_hc, ohci->hcca, ohci, td_list_hc2); + + td_list = dma_to_td (ohci, (void *)td_list_hc); + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + + while(/*(td_list_hc2!=0) || */(td_list->index < urb_priv->length-1) && (timeout>0)) { // wait another update for donehead + // To handle 1. ohci->hcca->donehead !=0 and regs->donehead!=0 + // 2. ohci->hcca->donehead !=0 and regs->donehead ==0 but regs-->donehead will be filled + + ohci->hcca->done_head = 0; + + value = readl(&ohci->regs->intrstatus); + value &= readl(&ohci->regs->intrenable); + + // We need to clear that the bit, otherwise We will not get next return. + if(value & OHCI_INTR_WDH) { + writel(value, &ohci->regs->intrstatus); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +// debug("OHCI_INTR_WDH cleared intrstatus=%x value=%x \n", readl(&ohci->regs->intrstatus), value); + } + while(timeout>0) { // wait for next DONEHEAD_WRITEBACK + value = readl(&ohci->regs->intrstatus); + if(!(value & OHCI_INTR_WDH)) { + udelay(1); + timeout--; + continue; + } else { + break; + } + } + + td_list_hc2 = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + // merge td_list_hc the tail of td_list_hc2 + + if(td_list_hc2!=0) { + + while (td_list_hc2) { + td_list = dma_to_td (ohci, (void *)td_list_hc2); + td_list_hc2 = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + + td_list->hwNextTD = td_list_hc; + + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + + td_list = dma_to_td (ohci, (void *)td_list_hc); + } else { + printf("."); + } + + } + + ohci->hcca->done_head = 0; + + value = readl(&ohci->regs->intrstatus); +// debug("OHCI_INTR_WDH value=%x \n", value); + value &= readl(&ohci->regs->intrenable); + +// We need to clear that the bit, otherwise We will not get next return. +// if(value & OHCI_INTR_WDH) { + writel(value, &ohci->regs->intrstatus); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +// debug("OHCI_INTR_WDH cleared intrstatus=%x value=%x \n", readl(&ohci->regs->intrstatus), value); +// } + +#if 0 + if (value & OHCI_INTR_SO) { + debug("USB Schedule overrun"); + writel (OHCI_INTR_SO, &ohci->regs->intrenable); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + } +#endif + + + while (td_list_hc) { +// debug("td_list_hc = %x\n", td_list_hc); + td_list = dma_to_td (ohci, (void *)td_list_hc); + + if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + debug(" USB-error/status: %x : %x\n", + TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) { + if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { + td_list->ed->hwHeadP = + (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) | + (td_list->ed->hwHeadP & cpu_to_le32 (0x2)); + urb_priv->td_cnt += urb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} +/*-------------------------------------------------------------------------*/ +/* td done list */ + +void dl_done_list (ohci_t * ohci, ohci_td_t * td_list) +{ + ohci_td_t * td_list_next = NULL; + ed_t * ed; + // int cc = 0; + struct urb * urb; + urb_priv_t * urb_priv; + u32 tdINFO; //, edHeadP, edTailP; + +// unsigned long flags; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + urb = td_list->urb; + urb_priv = urb->hcpriv; + tdINFO = le32_to_cpup (&td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); +#if 0 + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (cc == TD_CC_STALL) + usb_endpoint_halt(urb->dev, + usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + + if (!(urb->transfer_flags & USB_DISABLE_SPD) + && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + if (++(urb_priv->td_cnt) == urb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK)) + && (urb_priv->state != URB_DEL)) { + urb->status = cc_to_error[cc]; + ohci_return_urb (ohci, urb); + } + else { + dl_del_urb (urb); + } + } + + if (ed->state != ED_NEW) { + edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; + edTailP = le32_to_cpup (&ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } +#endif + + td_list = td_list_next; + } +} + +void ohci_wait_urb_done(struct urb *urb, int timeout) { // timeout usually ==10000 --> 10milisecond + //here need to according the urb or ed type judge the BLF and CLF, We may need one time out in it + // Or need to check intrstatus and see if the hcca->done_head has been filled. + // We need to clear that the bit, otherwise We will get next return. + uint32_t pipe = urb->pipe; + uint32_t value; + usbdev_t *usb_dev = urb->dev; + ohci_t *ohci = &_ohci_x[usb_dev->controller]; + uint32_t type; + while(timeout>0) { +#if 1 + value = readl(&ohci->regs->intrstatus); + if(!(value & OHCI_INTR_WDH)) { + udelay(1); + timeout--; + continue; + } else { + break; + } +#endif + } +#if 1 + while (timeout>0) { + type = usb_pipetype (pipe); + if(type ==PIPE_BULK) { + if( (readl(&ohci->regs->cmdstatus) & OHCI_BLF) == 0) break; + } else if(type == PIPE_CONTROL) { + if( (readl(&ohci->regs->cmdstatus) & OHCI_CLF) == 0) break; + } + udelay(1); // + timeout--; + } +#endif + + +} +void ohci_urb_complete(struct urb *urb) { + + ohci_t *ohci = &_ohci_x[urb->dev->controller]; + // it will clear the done list. and urb's actual_length is updated + dl_done_list (ohci, dl_reverse_done_list (ohci)); + +#if DEBUG_USB==1 + urb_print (urb, "RET", usb_pipein (urb->pipe)); +#endif + + urb_free_priv(ohci, urb->hcpriv); // free the priv and td list + +} +/*-------------------------------------------------------------------*/ +// it will 1. call usb_bulk_msg_x +// 2. call dl_list and find the data return +int ohci_bulk_transfer( uchar devnum, uchar ep, unsigned int data_len, uchar *data) { + int actual_length; + uint32_t t = devnum; + uint32_t pipe = ((ep&0x80)? 0x80:0)|(t<<8)|(3<<30); + t = ep; + pipe |=(t&0xf)<<15; + + usb_bulk_msg_x(&usb_device[devnum], pipe, data, data_len, &actual_length, 10000, ohci_urb_complete); + + return actual_length; + + +} +// it will 1. Call usb_control_msg_x +// 2. call dl_done_list to get the data returned ----> should be packed in one usb_complete_t function +// and assigned that to urb + +int ohci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short +wLength, void *data){ + + uint32_t t = devnum; + uint32_t pipe = ((request_type&0x80)? 0x80:0)|(t<<8)|(2<<30); + return usb_control_msg_x(&usb_device[devnum], pipe, request, request_type, wValue, wIndex, data, wLength, 10000, ohci_urb_complete); + +} + +int ohc_reset(uchar controller) +{ + + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + debug("Resetting OHCI\n"); + ohci_regs = (ohci_regs_t *)hc_base[controller]; + ohci_t *ohci = &_ohci_x[controller]; + +#ifndef __hppa__ + /* PA-RISC doesn't have SMM, but PDC might leave IR set */ + if (readl (&ohci_regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci_regs->cmdstatus); /* request ownership */ + debug("USB HC TakeOver from SMM"); + while (readl (&ohci_regs->control) & OHCI_CTRL_IR) { + mdelay (10); + if (--smm_timeout == 0) { + printf("USB HC TakeOver failed!"); + return -1; + } + } + } +#endif + + debug("USB HC reset_hc usb-%08x: ctrl = 0x%x ;", + hc_base[controller], + readl (&ohci_regs->control)); + + /* Reset USB (needed by some controllers) */ + writel (0, &ohci_regs->control); + + /* Force a state change from USBRESET to USBOPERATIONAL for ALi */ + (void) readl (&ohci_regs->control); /* PCI posting */ + writel (ohci->hc_control = OHCI_USB_OPER, &ohci_regs->control); + + /* HC Reset requires max 10 ms delay */ + writel (OHCI_HCR, &ohci_regs->cmdstatus); + while ((readl (&ohci_regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + printf("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + + +int ohc_start(uchar controller) { + // unsigned short tmp; + u32 mask; + unsigned int fminterval; + int delaytime; + ohci_regs = (ohci_regs_t *)hc_base[controller]; + ohci_t *ohci = &_ohci_x[controller]; + + debug("Starting OHCI\n"); + + writel (0, &ohci_regs->ed_controlhead); + writel (0, &ohci_regs->ed_bulkhead); + + writel ((uint32_t)ohci->hcca_dma, &ohci_regs->hcca); /* a reset clears this */ //3 + + fminterval = 0x2edf; //6 + writel ((fminterval * 9) / 10, &ohci_regs->periodicstart); // Don't worry, we can disable periodic in contol or let the ED list null + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci_regs->fminterval); + writel (0x628, &ohci_regs->lsthresh); + + /* start controller operations */ + + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + writel (ohci->hc_control, &ohci_regs->control); // PIE and IE is disabled + // DO we need to enable that but leave all ISO ED and INT ED list null??? + + mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + writel (mask, &ohci->regs->intrstatus); + + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); + + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + + // POTPGT delay is bits 24-31, in 2 ms units. + delaytime = ((roothub_a (ohci) >> 23) & 0x1fe)*5/2; // for apacer 256 usb 2.0 + NEC 2.0 chip +// delaytime = ((roothub_a (ohci) >> 23) & 0x1fe); + + mdelay (delaytime); + +// printf("delaytime: %d\n", delaytime); + + return(0); +} + + +int ohc_init(struct pci_device *dev) +{ + uint16_t word; + uint32_t dword; + ohci_t *ohci; + ed_t * ed; + int i,j, NDP; + int size; + + pci_read_config_dword(dev, 0x10, &dword); // it will be 4k range + hc_base[num_controllers] = (uint32_t)phys_to_virt(dword); + ohci = &_ohci_x[num_controllers]; + debug("ohc_init num_controllers=%d ohci=%x\n", num_controllers, (uint32_t)ohci); + memset(ohci, 0, sizeof(ohci_t)); + ohci->regs = (ohci_regs_t *)hc_base[num_controllers]; + ohci_regs = ohci->regs; + + ohci->hcca = allot2(sizeof (struct ohci_hcca), 0xff); //1 + if (!ohci->hcca) { + printf("ohc_init: hcca allocated no MEM\n"); + return -ENOMEM; + } + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + ohci->hcca_dma = (void *)virt_to_phys(ohci->hcca); + + //init ed; + ohci->ed = allot2(sizeof(ed_t)*NUM_EDS,0xf); + if(ohci->ed==0) { + printf("ohci_init: ed allocate no MEM\n"); + } +// debug("ohci->ed = %x\n", ohci->ed); + for(i=0; i<NUM_EDS;i++) { + ed = (ed_t *)&ohci->ed[i]; + ed->dma = (void *)virt_to_phys(ed); +// debug("i=%d, ed dma = %x\n", i, (uint32_t)ed->dma); + ed->state = ED_NEW; + } + +// init urb and urb_priv + ohci->urb = (struct urb *)allot2(sizeof(struct urb),0xff); + if (!ohci->urb) { + printf("ohci_init: urb allocate failed"); + } + memset(ohci->urb, 0, sizeof(urb_t)); + + + /* allocate the private part of the URB */ + size = 4; + ohci->urb_priv = allot2 (sizeof (urb_priv_t) + size * sizeof (ohci_td_t *), 0xff); + if (ohci->urb_priv == 0) { + printf("ohci_init: urb_priv allocated no mem\n"); + } + memset (ohci->urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (ohci_td_t *)); + + // set master + pci_read_config_word(dev, 0x04, &word); + word |= 0x04; + pci_write_config_word(dev, 0x04, word); + + + DPRINTF("Found OHCI at %08x\n", hc_base[num_controllers]); + ohc_reset(num_controllers); + + /* Here should or move to ohc_start + 1. Init HCCA + 2. Init ED and TD ---> in submit_urb + 3. Assign HCCA to ohci_regs->hcca ---> in ohc_init + 4. Set Intr to ohci_regs->intrenable ---> disable that in ohc_init + 5. enable all queue in ohci_regs->control ---> in ep_link and it is called by submit_urb + 6. set peridicstart to 0.9 of frameinterval ---> ohc_start + */ + +// writel( 0, &ohci_regs->intrenable); // no interrupts! //4 +// writel( 0xffffffff, &ohci_regs->intrdisable); + + NDP = readl(&ohci->regs->roothub.a) & 0xff; + for(j=0;j<NDP;j++) { + writel(RH_PS_PSS, &ohci->regs->roothub.portstatus[j]); + } + + /* FIXME this is a second HC reset; why?? */ + writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + mdelay (10); + + ohc_start(num_controllers); + + num_controllers++; + +#if DEBUG_USB==1 +// ohci_dump (ohci, 1); +#endif + +// debug("ohci->ed = %x\n", ohci->ed); + return(0); +} +int poll_o_root_hub(uint32_t port, uchar controller) +{ + uint32_t value; + int addr=0; + int i; + static uint32_t do_over=0; + uint8_t what; + ohci_t *ohci; + + value = readl(port); + + debug("poll_o_root_hub1 v=%08x port = %x, controller = %d\n", value, port, controller); + + if(value == 0xffffffff) return addr; // stupid port + + if((value & RH_PS_CSC) || do_over == port) { + debug("poll_o_root_hub2 v=%08x\t", value); + do_over=0; + if(value & RH_PS_CCS ) { // if port connected + debug("poll_o_root_hub21 v=%08x\t", value); + DPRINTF("Connection on port %04x\n", port); + + writel(value, port); + for(i=0; i<40; i++) { + udelay(10000+usec_offset); + value = readl(port); + if(value & RH_PS_CSC) { + writel(value, port); //Clear Change bit + i=0; + DPRINTF("BOUNCE!\n"); + } + } +// debug("poll_o_root_hub211 v=%08x\t", value); + + oport_wakeup(port); +// DPRINTF("Wakup %04x\n", port); + +// debug("poll_o_root_hub212 v=%08x\t", readl(port)); + oport_reset(port); +// debug("poll_o_root_hub213 v=%08x\t", readl(port)); + mdelay(10); + oport_enable(port); +// debug("poll_o_root_hub214 v=%08x\t", readl(port)); + + if(!(value & RH_PS_CCS)) { + DPRINTF("Device went away!\n"); + return(-1); + } + + addr = configure_device( port, controller, value & RH_PS_LSDA); + +#if DEBUG_USB==1 + // some one clear enable bit??? why??? It costs me one week to find it out. + ohci = &_ohci_x[controller]; + ohci_dump (ohci, 1); +#endif + +#if 1 +// usb_control_msg(addr, 0x21, 0xff, 0, 0, 0, NULL);// reset device +// mdelay(10); + usb_control_msg(addr, 0xa1, 0xfe, 0, 0, 1, &what); // get MAX L // get MAX LUN +#endif + +// debug("poll_o_root_hub215 v=%08x addr = %d\n", readl(port), addr); + + if(addr<0) { + oport_disable(port); + udelay(20000); +// oport_reset(port); + oport_reset_long(port); + oport_suspend(port); + do_over=port; + ohc_clear_stat(controller); + + } + } else { +// debug("poll_o_root_hub22 v=%08x\t", readl(port)); + oport_suspend(port); + oport_disable(port); + DPRINTF("Port %04x disconnected\n", port); + // wave hands, deconfigure devices on this port! + } + } + + return(addr); +} + + + + +#endif diff --git a/src/filo/usb/ohci.h b/src/filo/usb/ohci.h new file mode 100644 index 000000000..0c9c6ad57 --- /dev/null +++ b/src/filo/usb/ohci.h @@ -0,0 +1,316 @@ +#ifdef USB_DISK + +#ifndef _OHCI_H +#define _OHCI_H + +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +// for OHCI + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + u32 hwINFO; + u32 hwTailP; + u32 hwHeadP; + u32 hwNextED; + + struct ed * ed_prev; + u8 int_period; // No use just for aligned + u8 int_branch; // No use just for aligned + u8 int_load; // No uae just for aligned + u8 int_interval; // No use just for aligned + u8 state; + u8 type; + u16 last_iso; // no use just for aligned + struct ed * ed_rm_list; // No use just for aligned + + void * dma; + + u32 unused[3]; +}; +// __attribute((aligned(16))); +typedef struct ed ed_t; + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct ohci_td { + u32 hwINFO; + u32 hwCBP; /* Current Buffer Pointer */ + u32 hwNextTD; /* Next TD Pointer */ + u32 hwBE; /* Memory Buffer End Pointer */ + u16 hwPSW[MAXPSW]; + u8 unused; + u8 index; + struct ed * ed; + struct ohci_td * next_dl_td; + struct urb * urb; //defined in usb.h + void * td_dma; + void * data_dma; + u32 unused2[2]; +}; +//__attribute((aligned(32))); /* normally 16, iso needs 32 */ +typedef struct ohci_td ohci_td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + u32 int_table[NUM_INTS]; /* Interrupt ED table */ + u16 frame_no; /* current frame number */ + u16 pad1; /* set to 0 on each frame_no change */ + u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + + +#define MAX_ROOT_PORTS 15 + +struct ohci_regs { + /* control and status registers */ + u32 revision; + u32 control; + u32 cmdstatus; + u32 intrstatus; + u32 intrenable; + u32 intrdisable; + /* memory pointers */ + u32 hcca; + u32 ed_periodcurrent; + u32 ed_controlhead; + u32 ed_controlcurrent; + u32 ed_bulkhead; + u32 ed_bulkcurrent; + u32 donehead; + /* frame counters */ + u32 fminterval; + u32 fmremaining; + u32 fmnumber; + u32 periodicstart; + u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + u32 a; + u32 b; + u32 status; + u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); +typedef struct ohci_regs ohci_regs_t; + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) +//| OHCI_CTRL_IE | OHCI_CTRL_PLE + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +typedef struct +{ + ed_t * ed; + u16 length; // number of tds associated with this request + u16 td_cnt; // number of tds already serviced + int state; +#if 0 + wait_queue_head_t * wait; +#endif + ohci_td_t * td[0]; // list pointer to all corresponding TDs associated with this request + +} urb_priv_t; + +#define NUM_EDS 32 /* num of preallocated endpoint descriptors */ + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + void * hcca_dma; + + ohci_regs_t * regs; /* OHCI controller's memory */ + + ed_t * ed_bulktail; /* last endpoint of bulk list */ + ed_t * ed_controltail; /* last endpoint of control list */ + + int intrstatus; + u32 hc_control; /* copy of the hc control reg */ + + uint32_t ed_cnt; + ed_t *ed; // Allocate that from ed_buffer in ohc_init + usbdev_t *dev[NUM_EDS]; + urb_t *urb; // one ohci one urb + urb_priv_t *urb_priv; + struct usb_ctrlrequest *dr; +} ohci_t; + + +extern ohci_t _ohci_x[MAX_CONTROLLERS]; + +#define usb_to_ohci(usb_dev) (&_ohci_x[(usb_dev)->controller]) + +extern ohci_regs_t *ohci_regs; + +void clear_oport_stat(uint32_t port); +int ohc_init(struct pci_device *dev); +int poll_o_root_hub(uint32_t port, uchar controller); + +int ohci_bulk_transfer( uchar devnum, uchar ep, unsigned int data_len, uchar *data); +int ohci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short + wLength, void *data); +void ohci_wait_urb_done(struct urb *urb, int timeout); + +void ohci_init(void); +int ohc_init(struct pci_device *dev); +int ohci_submit_urb (struct urb * urb); +#endif + +#endif diff --git a/src/filo/usb/scsi.h b/src/filo/usb/scsi.h new file mode 100644 index 000000000..7c119a48a --- /dev/null +++ b/src/filo/usb/scsi.h @@ -0,0 +1,226 @@ +/* Copyright (C) 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * This header file contains public constants and structures used by + * the scsi code for linux. + */ + +#ifndef _SCSI_SCSI_H +#define _SCSI_SCSI_H 1 + +//#include <features.h> + +/* + * SCSI opcodes + */ + +#define TEST_UNIT_READY 0x00 +#define REZERO_UNIT 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define READ_BLOCK_LIMITS 0x05 +#define REASSIGN_BLOCKS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SEEK_6 0x0b +#define READ_REVERSE 0x0f +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define INQUIRY 0x12 +#define RECOVER_BUFFERED_DATA 0x14 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define ERASE 0x19 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RECEIVE_DIAGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e + +#define SET_WINDOW 0x24 +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define PRE_FETCH 0x34 +#define READ_POSITION 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define LOCK_UNLOCK_CACHE 0x36 +#define READ_DEFECT_DATA 0x37 +#define MEDIUM_SCAN 0x38 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a +#define WRITE_BUFFER 0x3b +#define READ_BUFFER 0x3c +#define UPDATE_BLOCK 0x3d +#define READ_LONG 0x3e +#define WRITE_LONG 0x3f +#define CHANGE_DEFINITION 0x40 +#define WRITE_SAME 0x41 +#define READ_TOC 0x43 +#define LOG_SELECT 0x4c +#define LOG_SENSE 0x4d +#define MODE_SELECT_10 0x55 +#define RESERVE_10 0x56 +#define RELEASE_10 0x57 +#define MODE_SENSE_10 0x5a +#define PERSISTENT_RESERVE_IN 0x5e +#define PERSISTENT_RESERVE_OUT 0x5f +#define MOVE_MEDIUM 0xa5 +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define WRITE_VERIFY_12 0xae +#define SEARCH_HIGH_12 0xb0 +#define SEARCH_EQUAL_12 0xb1 +#define SEARCH_LOW_12 0xb2 +#define READ_ELEMENT_STATUS 0xb8 +#define SEND_VOLUME_TAG 0xb6 +#define WRITE_LONG_2 0xea + +/* + * Status codes + */ + +#define GOOD 0x00 +#define CHECK_CONDITION 0x01 +#define CONDITION_GOOD 0x02 +#define BUSY 0x04 +#define INTERMEDIATE_GOOD 0x08 +#define INTERMEDIATE_C_GOOD 0x0a +#define RESERVATION_CONFLICT 0x0c +#define COMMAND_TERMINATED 0x11 +#define QUEUE_FULL 0x14 + +#define STATUS_MASK 0x3e + +/* + * SENSE KEYS + */ + +#define NO_SENSE 0x00 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define MEDIUM_ERROR 0x03 +#define HARDWARE_ERROR 0x04 +#define ILLEGAL_REQUEST 0x05 +#define UNIT_ATTENTION 0x06 +#define DATA_PROTECT 0x07 +#define BLANK_CHECK 0x08 +#define COPY_ABORTED 0x0a +#define ABORTED_COMMAND 0x0b +#define VOLUME_OVERFLOW 0x0d +#define MISCOMPARE 0x0e + + +/* + * DEVICE TYPES + */ + +#define TYPE_DISK 0x00 +#define TYPE_TAPE 0x01 +#define TYPE_PROCESSOR 0x03 /* HP scanners use this */ +#define TYPE_WORM 0x04 /* Treated as ROM by our system */ +#define TYPE_ROM 0x05 +#define TYPE_SCANNER 0x06 +#define TYPE_MOD 0x07 /* Magneto-optical disk - + * - treated as TYPE_DISK */ +#define TYPE_MEDIUM_CHANGER 0x08 +#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_NO_LUN 0x7f + +/* + * standard mode-select header prepended to all mode-select commands + * + * moved here from cdrom.h -- kraxel + */ + +struct ccs_modesel_head + { + unsigned char _r1; /* reserved. */ + unsigned char medium; /* device-specific medium type. */ + unsigned char _r2; /* reserved. */ + unsigned char block_desc_length; /* block descriptor length. */ + unsigned char density; /* device-specific density code. */ + unsigned char number_blocks_hi; /* number of blocks in this block + desc. */ + unsigned char number_blocks_med; + unsigned char number_blocks_lo; + unsigned char _r3; + unsigned char block_length_hi; /* block length for blocks in this + desc. */ + unsigned char block_length_med; + unsigned char block_length_lo; + }; + +/* + * MESSAGE CODES + */ + +#define COMMAND_COMPLETE 0x00 +#define EXTENDED_MESSAGE 0x01 +#define EXTENDED_MODIFY_DATA_POINTER 0x00 +#define EXTENDED_SDTR 0x01 +#define EXTENDED_EXTENDED_IDENTIFY 0x02 /* SCSI-I only */ +#define EXTENDED_WDTR 0x03 +#define SAVE_POINTERS 0x02 +#define RESTORE_POINTERS 0x03 +#define DISCONNECT 0x04 +#define INITIATOR_ERROR 0x05 +#define ABORT 0x06 +#define MESSAGE_REJECT 0x07 +#define NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define LINKED_CMD_COMPLETE 0x0a +#define LINKED_FLG_CMD_COMPLETE 0x0b +#define BUS_DEVICE_RESET 0x0c + +#define INITIATE_RECOVERY 0x0f /* SCSI-II only */ +#define RELEASE_RECOVERY 0x10 /* SCSI-II only */ + +#define SIMPLE_QUEUE_TAG 0x20 +#define HEAD_OF_QUEUE_TAG 0x21 +#define ORDERED_QUEUE_TAG 0x22 + +/* + * Here are some scsi specific ioctl commands which are sometimes useful. + */ +/* These are a few other constants only used by scsi devices. */ + +#define SCSI_IOCTL_GET_IDLUN 0x5382 + +/* Used to turn on and off tagged queuing for scsi devices. */ + +#define SCSI_IOCTL_TAGGED_ENABLE 0x5383 +#define SCSI_IOCTL_TAGGED_DISABLE 0x5384 + +/* Used to obtain the host number of a device. */ +#define SCSI_IOCTL_PROBE_HOST 0x5385 + +/* Used to get the bus number for a device. */ +#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386 + +#endif /* scsi/scsi.h */ diff --git a/src/filo/usb/scsi_cmds.c b/src/filo/usb/scsi_cmds.c new file mode 100644 index 000000000..bd7b7fa8a --- /dev/null +++ b/src/filo/usb/scsi_cmds.c @@ -0,0 +1,512 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + + +#include "scsi.h" + + +#include "usb_scsi_low.h" + +#ifndef NULL +#define NULL (void *) 0x0 +#endif + +#include "scsi_cmds.h" + +devhandle sgh; + +typedef struct sense_data { + uchar code; + + uchar sense_key:4; + uchar res1:4; + + uchar additional_code; + uchar qualifier; + + uchar res2[3]; + + uchar length; +} __attribute__ ((packed)) sense_data_t; + +typedef struct fixed_sense_data { + uchar code:7; + uchar valid:1; + + uchar obs1; + + uchar sense_key:4; + uchar res1:1; + uchar ili:1; + uchar eom:1; + uchar mark:1; + + unsigned int info; + + uchar add_len; +} __attribute__ ((packed)) fixed_sense_data_t; + +typedef struct additional_fixed_data { + unsigned int info; + + uchar code; + uchar qualifier; + uchar fru; + + uchar specific[3]; +} __attribute__ ((packed)) additional_fixed_data_t; + + +void PrintSense(uchar *sense, int len) +{ + int i; + + DPRINTF( "sense data "); + for(i=0;i<len; i++) { + DPRINTF( ":%02x", sense[i]); + } + DPRINTF("\n\n"); + + if( (sense[0] & 0x7f) >=0x72) { + sense_data_t *sd = (sense_data_t *) sense; + uchar *pos = sense+sizeof(sense_data_t); + uchar remaining = sd->length; + int dlen; + + DPRINTF("code = %02x, key = %1x, additional = %02x, qual = %02x\n", sd->code, sd->sense_key, sd->additional_code, sd->qualifier); + + while(remaining) { + DPRINTF("type = %02x", pos[0]); + dlen = pos[1]; + pos+=2; + remaining -=2; + + for(i=0; i<dlen; i++) + DPRINTF( ": %02x", pos[i]); + + DPRINTF("\n"); + pos+=i; + remaining -=i; + } + + } else { + fixed_sense_data_t *fd = (fixed_sense_data_t *) sense; + uchar remaining = fd->add_len; + additional_fixed_data_t *afd; + + + DPRINTF("code = %02x key = %1x\n", fd->code, fd->sense_key); + if(fd->mark) { + DPRINTF("filemark "); + } + + if(fd->eom) { + DPRINTF(" End Of Media "); + } + + if(fd->ili) { + DPRINTF("Illegal instruction"); + } + + DPRINTF("\n"); + + if(fd->valid) { + DPRINTF( "(valid) "); + } + + DPRINTF( "Info: %08x\n", ntohl(fd->info)); + + afd = (additional_fixed_data_t *) (sense + 8); + +// while(remaining) { + if(remaining) { + DPRINTF("command info = %08x\n", ntohl(afd->info)); + DPRINTF("code = %02x, qual = %02x, fru = %02x\n", afd->code, afd->qualifier, afd->fru); + DPRINTF("sense key data = %02x:%02x:%02x\n\n", afd->specific[2], afd->specific[1], afd->specific[0]); + + afd++; + remaining -= sizeof(additional_fixed_data_t); + } + } + +} + +typedef struct query_response { + uchar type:5; + uchar qualifier:3; + + uchar reserved1:7; + uchar removable:1; + + uchar version; + + uchar ResponseDataFormat:4; // should == 2 + uchar HiSup:1; // report luns cmd supported + uchar NormACA:1; + uchar obsolete:1; + uchar aerc:1; + + uchar AdditionalLength; // length of vendor specific data (beyond 96 bytes) + + uchar reserved2:7; + uchar sccs:1; // have raid controller + + uchar addr16:1; // + uchar obsolete2:2; + uchar MChnger:1; // media changer + uchar MultiP:1; // multi port + uchar vs:1; // ??? + uchar EncServ:1; // enclosure service + uchar BQue:1; // basic command queueing + + uchar vs2:1; + uchar CmdQue:1; // full command queueing + uchar obsolete4:1; + uchar linked:1; + uchar sync:1; + uchar wbus16:1; // + uchar obsolete3:1; + uchar RelAddr:1; // treletive addressing + + char vendor[8]; + char product[16]; + char revision[4]; + char vendor_data[20]; + + uchar ius:1; + uchar qas:1; + uchar clocking:2; // + uchar reserved3:4; + + unsigned short version_desc[8]; + + char reserved4[21]; +} query_response_t; + +typedef struct ReadBlockCMD { + uchar cmd; + + uchar reladdr:1; + uchar reserved:2; + uchar fua:1; // force unit access flush to media + uchar dpo:1; // direct page out, do not cache + uchar reserved2:3; + + unsigned int block_address; + uchar reserved3; + + unsigned short block_count; + + uchar control; +} __attribute__ ((packed)) ReadBlockCMD_t ; + +int ll_read_block(devhandle sgd, char *buffer, int blocknum, int count) +{ + int ret; + ReadBlockCMD_t rb; + char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = READ_10; + rb.block_address = htonl(blocknum); + rb.block_count = htons(count); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + + if(ret<0) { + DPRINTF("ERROR: ll_read_block( %x, %x, %x, %x) = %d\n", sgd, buffer, blocknum, count, ret); + PrintSense(sensedat, 32); + } + + return(ret); + +} + +int ll_write_block(devhandle sgd, char *buffer, int blocknum, int count) +{ + int ret; + ReadBlockCMD_t rb; + char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = WRITE_10; + rb.block_address = htonl(blocknum); + rb.block_count = htons(count); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_TO_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + + return(ret); +} + +typedef struct ReadLongCMD { + uchar cmd; + + uchar reladdr:1; + uchar correct:1; + uchar reserved:5; + + unsigned int block_address; + uchar reserved3; + + unsigned short length; + + uchar control; +} __attribute__ ((packed)) ReadLongCMD_t ; + +int ll_read_long(devhandle sgd, char *buffer, int blocknum, int size) +{ + int ret; + ReadLongCMD_t rb; + char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = READ_LONG; + rb.block_address = htonl(blocknum); + rb.length = htons(size); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, size, sensedat, sizeof(sensedat)); + return(ret); +} + +unsigned char ReadCapacityCMD[10] = { READ_CAPACITY, 0, 0,0,0,0, 0,0,0, 0}; + +struct ReadCapacityResponse { + unsigned int block_address; + unsigned int block_length; +}; + +int get_capacity(devhandle sgd, unsigned long *block_count, unsigned int *blk_len) +{ + int ret; + struct ReadCapacityResponse response; + char sensedat[32]; + + ret = scsi_command(sgd, ReadCapacityCMD, sizeof(ReadCapacityCMD), SG_DXFER_FROM_DEV, (uint8_t *)&response, sizeof(response), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("ERROR:get capacity: %d\n", ret); + PrintSense(sensedat,32); + } + + + *block_count = ntohl(response.block_address) +1; + *blk_len = ntohl(response.block_length); + + return(ret); +} + +#define INQ_REP_LEN 96 +unsigned char InquiryCMD[6] = { INQUIRY, 0, 0, 0, INQ_REP_LEN, 0}; + +int query(devhandle sgd, query_response_t *qr) +{ + int ret; + char sensedat[32]; + + ret = scsi_command(sgd, InquiryCMD, sizeof(InquiryCMD), SG_DXFER_FROM_DEV, (uint8_t *)qr, sizeof(query_response_t), sensedat, sizeof(sensedat) ); + + if(ret<0){ + DPRINTF("query: IOCTL"); + } + + return(ret); +} + +typedef struct lun_list { + unsigned int list_length; + unsigned int reserved; + unsigned long long lun[16]; +} lun_list_t; + +#define REPORT_LUNS 0xa0 +unsigned char ReportLunsCMD[12] = { REPORT_LUNS, 0, 2, 0, 0, 0, 0, 0, 0, 128, 0, 0 }; + +int ReportLUNS(devhandle sgd, lun_list_t *list) +{ + int ret; + char sensedat[32]; + + memset (list, 0, sizeof(lun_list_t)); + ret = scsi_command(sgd, ReportLunsCMD, sizeof(ReportLunsCMD), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(lun_list_t), sensedat, sizeof(sensedat) ); + + if(ret<0) { + DPRINTF("Report Luns: IOCTL"); + } + + list->list_length = ntohl(list->list_length); + + return(ret); +} + +typedef struct command_descriptor { + uchar opcode; + uchar reserved; + unsigned short service_action; + uchar reserved2; + + uchar action_valid:1; + uchar reserved3:7; + + unsigned short cdb_len; +} __attribute__ ((packed)) command_descriptor_t; + +typedef struct report_opcodes_result { + unsigned long length; + + command_descriptor_t command[256]; +} __attribute__ ((packed)) report_opcode_result_t; + + +#define REPORT_OPCODES 0xa3 + +typedef struct report_opcodes_cmd { + uchar cmd; + uchar reserved[5]; + unsigned int reply_len; + uchar reserved2; + uchar control; +} __attribute__ ((packed)) ReportOpcodesCMD_t; + +//ReportOpcodesCMD_t ReportOpcodesCMD = { cmd : REPORT_OPCODES, reply_len: htonl(sizeof(report_opcode_result_t)) }; + +int ReportOpCodes(devhandle sgd, report_opcode_result_t *list) +{ + int ret; + char sensedat[32]; + ReportOpcodesCMD_t ReportOpcodesCMD; + + memset (list, 0, sizeof(report_opcode_result_t)); + ReportOpcodesCMD.cmd = REPORT_OPCODES; + ReportOpcodesCMD.reply_len = htonl( sizeof(report_opcode_result_t)); + + ret = scsi_command(sgd, (uint8_t *)&ReportOpcodesCMD, sizeof(ReportOpcodesCMD_t), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(report_opcode_result_t), sensedat, sizeof(sensedat) ); + + if(ret<0) { + DPRINTF("Report Luns: IOCTL"); + } + + list->length = ntohl(list->length); + + return(ret); +} + + +#define READ_ATTRIBUTE 0x8c +#define VOLUME_LIST 2 +#define PARTITION_LIST 3 + +typedef struct read_attribute_cmd { + uchar cmd; + + uchar action:5; + uchar res:3; + + uchar restricted[3]; + + uchar volume; + uchar res2; + uchar partition; + + ushort attribute; + unsigned int reply_len; + uchar res3; + uchar control; +} __attribute__ ((packed)) ReadAttributeCMD_t; + +int CheckVolumes(devhandle sgd) +{ + int ret; + uchar reply[4]; + uchar sensedat[32]; + ReadAttributeCMD_t cmd; + + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd=READ_ATTRIBUTE; + cmd.action = VOLUME_LIST; + cmd.reply_len = htonl(4); + + ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("Report Volumes: IOCTL"); + return(-1); + } + + if(! reply[0] && !reply[1]) + return(0); + + return(reply[3]); +} + +int CheckPartitions(devhandle sgd) +{ + int ret; + uchar reply[4]; + uchar sensedat[32]; + ReadAttributeCMD_t cmd; + + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd=READ_ATTRIBUTE; + cmd.action = PARTITION_LIST; + cmd.reply_len = htonl(4); + + ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("Report PARTITIONVolumes: IOCTL"); + return(-1); + } + + if(! reply[0] && !reply[1]) + return(0); + + return(reply[3]); +} + +int UnitReady(devhandle sgd) +{ + uchar cmd[6]; + uchar sensedat[32]; + int ret; + + memset(cmd,0,sizeof(cmd)); + + ret = scsi_command(sgd, &cmd, sizeof(cmd), SG_DXFER_FROM_DEV, NULL, 0, sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("UnitReady :"); + return(0); + } + + return(1); +} + + +#endif diff --git a/src/filo/usb/scsi_cmds.h b/src/filo/usb/scsi_cmds.h new file mode 100644 index 000000000..1c64a3bf3 --- /dev/null +++ b/src/filo/usb/scsi_cmds.h @@ -0,0 +1,14 @@ +#ifndef _SCSI_CMDS_H +#define _SCSI_CMDS_H + +#define devhandle uint8_t + +#define uchar uint8_t +#define ushort uint16_t + +void PrintSense(uchar *sense, int len); +int ll_read_block(devhandle sgd, char *buffer, int blocknum, int count); + +int get_capacity(devhandle sgd, unsigned long *block_count, unsigned int *blk_len); +int UnitReady(uchar sgd); +#endif diff --git a/src/filo/usb/uhci.c b/src/filo/usb/uhci.c new file mode 100644 index 000000000..4ce4b499c --- /dev/null +++ b/src/filo/usb/uhci.c @@ -0,0 +1,1143 @@ +#ifdef USB_DISK + +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + + +#include "usb.h" +#include "uhci.h" +#include "debug_x.h" + +#define ALLOCATE 1 + +extern int usec_offset; + +int wait_head( queue_head_t *head, int count) +{ + td_t *td; + + + while(!head->depth.terminate) { + td = MEM_ADDR(head->depth.link); + if(!td->active) + return(-1); // queue failed + + if(count) + if(! --count) + return(0); // still active + + udelay(500); // give it some time + } + + return(1); // success +} + +queue_head_t *free_qh; +queue_head_t _queue_heads[MAX_QUEUEHEAD]; +queue_head_t *queue_heads = _queue_heads; + +queue_head_t *new_queue_head(void) +{ + queue_head_t *qh; + + if(!free_qh) + return(NULL); + + qh = free_qh; + free_qh = MEM_ADDR(qh->bredth.link); + + memset(qh,0,sizeof(queue_head_t)); + qh->bredth.terminate = qh->depth.terminate=1; + + return(qh); +} + +void free_queue_head( queue_head_t *qh) +{ + + qh->bredth.link = LINK_ADDR(free_qh); + if(!free_qh) + qh->bredth.terminate=1; + + qh->depth.terminate=1; + free_qh = qh; +} + +void init_qh(void) +{ + int i; + + for(i=0; i<MAX_QUEUEHEAD-1; i++) { + memset(queue_heads+i, 0, sizeof(queue_head_t)); + queue_heads[i].bredth.link = LINK_ADDR( &queue_heads[i+1] ); + queue_heads[i].depth.terminate=1; + } + + queue_heads[MAX_QUEUEHEAD-1].depth.terminate=1; + queue_heads[MAX_QUEUEHEAD-1].bredth.terminate=1; + + free_qh = queue_heads; +} + +td_t *free_td_list; +td_t _tds[MAX_TD]; +td_t *tds = _tds; // indirection added for kernel testing + + +void init_td(void) +{ + int i; + + for(i=0; i<MAX_TD-1; i++) { + memset(tds+i, 0, sizeof(td_t)); + tds[i].link.link = LINK_ADDR( &tds[i+1]); + } + + memset( &tds[MAX_TD-1], 0, sizeof(td_t)); + tds[MAX_TD-1].link.terminate=1; + + free_td_list = tds; +} + +td_t *new_td(void) +{ + td_t *td; + + if(!free_td_list) + return(NULL); + +// DPRINTF("new_td: free_td = %p\n", free_td_list); + td = free_td_list; + + free_td_list = MEM_ADDR( td->link.link); +// DPRINTF("new_td: free_td_list = %p\n", free_td_list); + + memset(td, 0, sizeof(td_t)); + td->link.terminate=1; + +// DPRINTF("new_td: returning %p\n", td); + return(td); +} + +td_t *find_last_td(td_t *td) +{ + td_t *last; + + last = td; + + while(!last->link.terminate) + last = MEM_ADDR(last->link.link); + + return(last); +} + +void free_td( td_t *td) +{ + td_t *last_td; + + last_td = find_last_td(td); + + last_td->link.link = LINK_ADDR(free_td_list); + if(!free_td_list) + last_td->link.terminate=1; + else + last_td->link.terminate=0; + + free_td_list = td; + +} + +link_pointer_t *queue_end( queue_head_t *queue) +{ + link_pointer_t *link; + + link = &(queue->depth); + + while(!link->terminate) + link = MEM_ADDR(link->link); + + return(link); +} + +void add_td( queue_head_t *head, td_t *td) +{ + link_pointer_t *link; + + link = queue_end(head); + + link->link = LINK_ADDR(td); + link->terminate=0; +} + +transaction_t transactions[MAX_TRANSACTIONS]; +transaction_t *free_transactions; + +void init_transactions(void) +{ + int i; + + memset(transactions, 0, sizeof(transactions)); + + for(i=0; i<MAX_TRANSACTIONS-1; i++) + transactions[i].next = &transactions[i+1]; + + free_transactions = transactions; +} + +void free_transaction( transaction_t *trans ) +{ + transaction_t *my_current, *last; + + my_current = trans; + + if(my_current==0) return; + + while(my_current) { + free_td( my_current->td_list ); + free_queue_head( my_current->qh ); + + last = my_current; + my_current = my_current->next; + } + + last->next = free_transactions; + free_transactions = trans; +} + +transaction_t *new_transaction(td_t *td) +{ + transaction_t *trans = free_transactions; + queue_head_t *qh; + + if(!trans) { + DPRINTF("new_transaction( td = %x) failed!\n", td); + return(NULL); + } + + free_transactions = trans->next; + + memset(trans, 0, sizeof(transaction_t)); + + if(td) { + qh = new_queue_head(); + if(!qh) { + free_transaction(trans); + return(NULL); + } + + trans->qh = qh; + trans->td_list = td; + qh->depth.link = LINK_ADDR(td); + qh->depth.terminate = 0; + qh->bredth.terminate=1; + } + + return(trans); +} + +transaction_t *add_transaction( transaction_t *trans, td_t *td) +{ + transaction_t *t1; + + + t1 = new_transaction(td); + if(!t1) + return(NULL); + + trans->next = t1; + trans->qh->bredth.terminate=0; + trans->qh->bredth.link = LINK_ADDR(t1->qh); + trans->qh->bredth.queue=1; + + return(trans); +} + +link_pointer_t *frame_list[MAX_CONTROLLERS]; +#if 0 +uchar fl_buffer[MAX_CONTROLLERS][8192]; +#endif + +void init_framelist(uchar dev) +{ + + int i; +#if 0 + DPRINTF("raw frame_list is at %x\n", fl_buffer[dev]); + frame_list[dev] = (link_pointer_t *) ((bus_to_virt)(((unsigned int)virt_to_bus(fl_buffer[dev]) & ~0xfff) + 0x1000)); +#else + frame_list[dev] = (link_pointer_t *) allot2(sizeof(link_pointer_t)*1024, 0xfff); // 4K alignment + if(frame_list[dev]==0) { + printf("init_framelist: no mem\n"); + } +#endif + memset(frame_list[dev], 0, 1024 * sizeof(link_pointer_t)); + + + DPRINTF("frame_list is at %x\n", frame_list[dev]); + + for(i=0;i<1024;i++) + frame_list[dev][i].terminate=1; + +} + + +extern int num_controllers; + +extern uint32_t hc_base[MAX_CONTROLLERS]; +extern uint8_t hc_type[MAX_CONTROLLERS]; + +void uhc_clear_stat() +{ + unsigned short value; + + value = inw(USBSTS(0)); + outw(value, USBSTS(0)); +} + +void clear_uport_stat(unsigned short port) +{ + unsigned short value; + + value = inw(port); + outw(value, port); +} + +void uport_suspend( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x1000; + outw( value, port); + +} + +void uport_wakeup( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x1000; + outw( value, port); + +} + +#if 0 +void uport_resume( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x40; + outw(value, port); + udelay(20000+usec_offset); + value &= ~0x40; + outw(value, port); + + do { + value = inw(port); + } while(value & 0x40); +} + +#endif +void uport_enable( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x04; + outw( value, port); + + do { + value = inw(port); + } while( !(value & 0x04) && (value & 0x01)); + +} + + +void uport_disable( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x04; + outw( value, port); +} + +void uport_reset(unsigned short port) +{ + unsigned short value; + int i; + + value = inw(port); + value |= 0x200; + + outw( value, port); + + for(i=0;i<5;i++) + udelay(10000+usec_offset); + + value &= ~0x200; + outw( value, port); + +// DPRINTF("Port %04x reset\n", port); +} + +void uport_reset_long(unsigned short port) +{ + unsigned short value; + int i; + + value = inw(port); + value |= 0x200; + outw( value, port); + + for(i=0; i<20; i++) + udelay(10000); + + value &= ~0x200; + outw( value, port); + +// DPRINTF("Port %04x reset\n", port); +} + +void uhc_reset(uchar controller) +{ + DPRINTF("Resetting UHCI\n"); + outw(0x04, USBCMD(controller)); + udelay(20000); + outw(0, USBCMD(controller)); +} +#if 0 +int uhc_stop(uchar dev) +{ + unsigned short tmp; + + tmp = inw(USBCMD(dev)); + tmp &= ~USBCMDRUN; + outw( tmp, USBCMD(dev)); + + while(! (inw(USBSTS(dev)) & USBSTSHALTED) ); + outw( USBSTSHALTED, USBSTS(dev)); // clear the status + + return(0); +} + +#endif + +int uhc_start(uchar dev) { + unsigned short tmp; + + DPRINTF("Starting UHCI\n"); + + tmp = inw(USBCMD(dev)); + tmp |= USBCMDRUN; + +// tmp |= USBCMD_DEBUG; + outw( tmp, USBCMD(dev)); + + return(0); +} + +int uhc_init(struct pci_device *dev) +{ + int16_t word; + + + pci_read_config_word(dev, 0x20, &word); + hc_base[num_controllers] = word; + hc_base[num_controllers] &= ~1; + + DPRINTF("Found UHCI at %04x\n", hc_base[num_controllers]); + uhc_reset(num_controllers); + + // set master + pci_read_config_word(dev, 0x04, &word); + word |= 0x04; + pci_write_config_word(dev, 0x04, word); + +#if 0 + if( ((unsigned int) virt_to_bus(frame_list[num_controllers])) != ( ( (unsigned int)virt_to_bus(frame_list[num_controllers])) & ~0x7ff) ) { + DPRINTF("UHCI: grave error, misaligned framelist (%x)\n", frame_list[num_controllers]); + return(-1); + } +#endif + + DPRINTF("uhc_init setting framelist to: %08x\n", (unsigned int) virt_to_bus( (frame_list[num_controllers]) )); + outl( (unsigned int) virt_to_bus(frame_list[num_controllers]), FLBASE(num_controllers)); + outw( 0, FRNUM(num_controllers)); + outw( 0, USBINTR(num_controllers)); // no interrupts! + + outw(0x1000, PORTSC1(num_controllers)); + outw(0x1000, PORTSC2(num_controllers)); + + uhc_start(num_controllers); + + dump_uhci(hc_base[num_controllers]); + + num_controllers++; + return(0); +} + +queue_head_t *sched_queue[MAX_CONTROLLERS]; +queue_head_t *term_qh[MAX_CONTROLLERS]; +//td_t *dummy_td[MAX_CONTROLLERS]; +td_t *loop_td[MAX_CONTROLLERS]; + +void init_sched(uchar dev) +{ + int i; + +// dummy_td[dev] = new_td(); + loop_td[dev] = new_td(); + term_qh[dev] = new_queue_head(); + + sched_queue[dev] = new_queue_head(); + sched_queue[dev]->bredth.terminate=0; + sched_queue[dev]->bredth.queue=1; + sched_queue[dev]->bredth.link=LINK_ADDR(term_qh[dev]); + sched_queue[dev]->depth.terminate=1; + + term_qh[dev]->bredth.terminate=1; + term_qh[dev]->depth.link = LINK_ADDR(loop_td[dev]); + term_qh[dev]->depth.terminate=0; + +// dummy_td->link.link = LINK_ADDR(sched_queue); +// dummy_td->link.queue = 1; +// dummy_td->link.depth=1; +// dummy_td->link.terminate=0; +// dummy_td->packet_type = IN_TOKEN; +// dummy_td->max_transfer = 0x7; +// dummy_td->isochronous=1; +// dummy_td->active=1; +// dummy_td->device_addr = 0x7f; +// dummy_td->endpoint=0x01; +// dummy_td->buffer = virt_to_bus(&dummy_td->data[2]); +// dummy_td->retrys=3; + +//dump_hex( (uchar *) dummy_td, sizeof(td_t), "dummy_td "); + + loop_td[dev]->link.link = LINK_ADDR(loop_td[dev]); + loop_td[dev]->link.terminate=0; + loop_td[dev]->link.queue=0; + loop_td[dev]->packet_type = IN_TOKEN; + loop_td[dev]->max_transfer=7; + loop_td[dev]->retrys=0; + loop_td[dev]->device_addr=0x7f; + + for(i=0; i< 1024; i++) { + frame_list[dev][i].link = LINK_ADDR(sched_queue[dev]); + frame_list[dev][i].queue=1; + frame_list[dev][i].terminate=0; +// frame_list[dev][i].terminate=1; + } + + dump_link( frame_list[dev], "frame_list_link: "); +// DPRINTF("dummy_td = %x\n", dummy_td[dev]); + +// dump_frame_list("sched:"); + +} + +void uhci_init(void) +{ + int i; + + init_td(); + init_qh(); + init_transactions(); + + for(i=0;i<MAX_CONTROLLERS; i++) { + if(hc_type[i] == 0x00) { + init_framelist(i); + init_sched(i); + } + } + + // From now should not change num_controllers any more +} + +int poll_queue_head( queue_head_t *qh) +{ + td_t *td; + int strikes=3; + + if(qh->depth.terminate) + return(1); + + while(strikes--) { + if(qh->depth.terminate) + return(1); + + td = MEM_ADDR(qh->depth.link); + + if(td->active) + return(0); + + udelay(1000); + +// if(!td->active) +// return(1); + } + + return(1); +} + +int wait_queue_complete( queue_head_t *qh) +{ + int ret; + int spins=1000; + + while( --spins && !(ret = poll_queue_head(qh))) { + udelay(1500); +// if(!(spins%30)) +// DPRINTF("wait_queue_complete: spin\n"); + } +// DPRINTF("wait_queue_complete: returning %d\n", ret); + + if(!spins) + return(-1); + + return(ret); +} + +#define BULK_DEPTH 1 + +transaction_t *_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + uchar dt; + transaction_t *trans; + td_t *td, *cur, *last; + int remaining = len; + uchar *pos = data; + int max; + uchar type = OUT_TOKEN; + int packet_length; + + + if(ep & 0x80) + type = IN_TOKEN; + + ep &= 0x7f; + + td = cur = last = NULL; + dt = usb_device[devnum].toggle[ep]; + max = usb_device[devnum].max_packet[ep]; + + while(remaining) { + cur = new_td(); + cur->packet_type = type; + cur->data_toggle = dt; + cur->endpoint = ep&0x7f; + cur->device_addr = devnum; + cur->detect_short=1; + cur->active=1; + dt = dt^0x01; + + if(!td){ + td = cur; + } + + if(last) { + last->link.terminate=0; + last->link.link = LINK_ADDR(cur); + } + + cur->buffer = (void *) virt_to_bus(pos); + + if(remaining>max) { + packet_length = max; + } + else { + packet_length = remaining; + } + + cur->max_transfer=packet_length-1; + cur->link.depth = BULK_DEPTH; + + remaining -= packet_length; + pos+= packet_length; + last = cur; + } + +// if( packet_length == max) { // if final packet wasn't short, add a zero packet +// cur = new_td(); +// dt = dt^0x01; +// cur->packet_type = type; +// cur->max_transfer = 0x7ff; // zero length code +// last->link.terminate=0; +// last->link.link = LINK_ADDR(cur); +// +// } + + cur->link.terminate=1; + + trans = new_transaction(td); + usb_device[devnum].toggle[ep] = dt; + + return(trans); +} + +#define DEPTH 0 + +transaction_t *ctrl_msg(uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, uchar *data) +{ + td_t *td; + td_t *current_td; + td_t *last_td; + transaction_t *trans; + + ctrl_msg_t *message; + + unsigned char type; + int remaining = wLength; + uchar *pos = data; + uchar dt=1; + +// DPRINTF("ctrl_msg( %02x, %02x, %02x, %04x, %04x, %04x, %p)\n", devnum, request_type, request, wValue, wIndex, wLength, data); +// DPRINTF("%d bytes in payload\n", remaining); +// DPRINTF("lowspeed = %u\n", usb_device[devnum].lowspeed); + last_td = td = new_td(); + + td->packet_type = SETUP_TOKEN; + td->device_addr = devnum & 0x7f; + td->max_transfer = 7; // fixed for setup packets + td->retrys = CTRL_RETRIES; + td->active=1; + td->data_toggle=0; + td->link.depth=DEPTH; + td->detect_short=0; + td->interrupt=1; + td->lowspeed = usb_device[devnum].lowspeed; + +// steal 8 bytes from so-called software area to hole the control message itself + td->buffer = (void *) virt_to_bus(&(td->data[2])); + message = bus_to_virt( (unsigned int) td->buffer); + + message->bmRequestType = request_type; + message->bRequest = request; + message->wValue = wValue; + message->wIndex = wIndex; + message->wLength = wLength; +//dump_hex(td, sizeof(td_t), "ctrl_msg:"); + trans = new_transaction(td); + + if(!trans) { + DPRINTF("ctrl_msg: couldn't allocate a transaction!\n"); + return(NULL); + } + + if(request_type & CONTROL_DIR_MASK) + type = IN_TOKEN; + else + type = OUT_TOKEN; + + while(remaining >0) { + int length; + +// DPRINTF("ctrl_msg loop %d remaining, maxpacket = %u\n", remaining, usb_device[devnum].max_packet[0]); + current_td = new_td(); + + last_td->link.link = LINK_ADDR(current_td); + last_td->link.terminate=0; + last_td->link.queue=0; + last_td->link.depth=DEPTH; + + + current_td->device_addr = devnum & 0x7f; + current_td->retrys = CTRL_RETRIES; + current_td->active=1; + current_td->data_toggle=dt; + current_td->link.depth=DEPTH; + current_td->lowspeed = usb_device[devnum].lowspeed; + current_td->detect_short=1; + + dt = dt^0x01; + + current_td->packet_type = type; +// if(type == IN_TOKEN) +// current_td->detect_short=1; + + if(remaining >usb_device[devnum].max_packet[0]) + length = usb_device[devnum].max_packet[0]; + else + length = remaining; + + current_td->max_transfer = length-1; + current_td->buffer = (void *) virt_to_bus(pos); + remaining -= length; + pos += length; + + last_td = current_td; + } + + current_td = new_td(); + + current_td->device_addr = devnum & 0x7f; + current_td->retrys = CONTROL_STS_RETRIES; + current_td->active=1; + current_td->lowspeed = usb_device[devnum].lowspeed; + + if(type == IN_TOKEN) + current_td->packet_type = OUT_TOKEN; + else + current_td->packet_type = IN_TOKEN; + + current_td->max_transfer=0x7ff; + + current_td->link.terminate=1; + current_td->data_toggle=1; + current_td->link.depth=DEPTH; + + + last_td->link.link = LINK_ADDR(current_td); + last_td->link.terminate=0; + last_td->link.queue=0; + last_td->link.depth=DEPTH; + + return(trans); +} + + +int schedule_transaction( uchar dev, transaction_t *trans) +{ + unsigned short value; + + if(!sched_queue[dev]->depth.terminate) + return(-EBUSY); + + sched_queue[dev]->depth.link = LINK_ADDR(trans->qh); + sched_queue[dev]->depth.terminate = 0; + sched_queue[dev]->depth.queue=1; + + if(hc_type[dev]==0x00) { + value = inw(hc_base[dev]); + value |=1; + outw( value, hc_base[dev]); + } +#if 0 + else if (hc_type[dev]==0x10) { + uint32_t value; + ohci_regs_t *ohci_regs = (ohci_regs_t *) hc_base[dev]; + value = readl(&ohci_regs->control); + value |=OHCI_USB_OPER; + writel( value, &ohci_regs->control); + + } +#endif + + return(0); +} + +int wait_transaction( transaction_t *trans) +{ + queue_head_t *qh; + + qh = trans->qh; + + while(!qh->bredth.terminate) + qh = MEM_ADDR(qh->bredth.link); + + return( wait_queue_complete(qh)); +} + +void unlink_transaction( uchar dev, transaction_t *trans) +{ + sched_queue[dev]->depth.terminate=1; + sched_queue[dev]->depth.link = 0; // just in case +} + +int uhci_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + transaction_t *trans; + td_t *td; + int data_len; + int ret; + uchar *buffer; + DPRINTF("bulk_transfer: ep = %x len=%d\n", ep, len); +#if ALLOCATE==1 + buffer = allot2(2048, 0x7ff); + if(buffer==0){ + printf("bulk_transfer: can not allot\n"); + } + memset(buffer,0,2048); +// DPRINTF("bulk_transfer: buffer(virt) = %x buffer(phys) = %x len = %d\n", buffer, virt_to_phys(buffer), len); + + if( !(ep & 0x80)) + memcpy(buffer, data, len); +#else + buffer = data; +#endif + + + trans = _bulk_transfer(devnum, ep, len, buffer); +#if 0 +#ifdef DEBUG + dump_transaction(trans, "bulk_transfer:"); +#endif +#endif + schedule_transaction( usb_device[devnum].controller, trans); + ret = wait_transaction(trans); + + if(ret<0) { +#ifdef DEBUG + dump_uhci(hc_base[usb_device[devnum].controller] ); + dump_td(trans->td_list, "failed_bulk_transaction: "); +#endif + unlink_transaction( usb_device[devnum].controller, trans); + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + unlink_transaction( usb_device[devnum].controller, trans); + + data_len=0; + td = trans->td_list; + do { + if(td->active) + break; + + if(td->max_transfer == 0x7ff) + break; + + data_len += td->actual +1; + + if(td->actual < td->max_transfer) // short packet also check for errors here + break; + + if(!td->link.terminate){ + td = MEM_ADDR(td->link.link); + } + else { + td=NULL; + } + } while(td); +#if 0 + +#ifdef DEBUG + dump_td(trans->td_list, "bulk_transfer_success:"); +#endif +#endif + + if(data_len < len) { + DPRINTF("bulk_transfer( dev= %d, ep = %d, len = %d, buffer = %x) = %d:short transaction:\n", devnum, ep, len, data, data_len); + dump_td(trans->td_list, "short_transaction:"); + } + + free_transaction(trans); + +#if ALLOCATE==1 + if( (ep & 0x80)) + memcpy(data, buffer, len); + forget2(buffer); +#endif + + + DPRINTF("bulk_transfer returning %d\n", data_len); + return(data_len); +} + +int uhci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data) +{ + transaction_t *trans; + td_t *td; + int data_len=0; + uchar *buffer; + int ret; + DPRINTF("uhci_control_msg: request_type = %x request = %x wLength=%d\n", request_type, request, wLength); +#if ALLOCATE==1 +// if( (wLength!=0) && (data!=NULL) ) { + buffer = allot2(2048+wLength,0x7ff); + if(buffer==0){ + printf("uhci_control_msg: can not allot\n"); + } + + memset(buffer,0,2048+wLength); + //DPRINTF("uhci_control_msg: buffer(virt) = %x buffer(phys) = %x wLength=%d\n", buffer, virt_to_phys(buffer), wLength); + if( !(request_type & 0x80)) + memcpy(buffer, data, wLength); +// } else { +// buffer=NULL; +// } + +#else + buffer = data; +#endif + + trans = ctrl_msg(devnum, request_type, request, wValue, wIndex, wLength, buffer); + if(!trans) { + DPRINTF("uhci_control_msg: ctrl_msg failed!\n"); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + schedule_transaction( usb_device[devnum].controller, trans); + ret = wait_transaction(trans); + + if(ret<0) { +#ifdef DEBUG + dump_uhci(hc_base[usb_device[devnum].controller] ); + dump_td(trans->td_list, "failed_transaction: "); +#endif + unlink_transaction( usb_device[devnum].controller, trans); + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(ret); + } + +//#ifdef DEBUG +// dump_td(trans->td_list, "success: "); +//#endif + + unlink_transaction( usb_device[devnum].controller, trans); + + // now, see what happened + + if(!trans->qh->depth.terminate) { +// handle setup error + + dump_uhci(hc_base); + dump_td(trans->td_list, "qh->depth failed_transaction: "); + + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + td = trans->td_list; + + do { + if(td->packet_type != SETUP_TOKEN) + data_len += td->actual; + + if(td->actual < td->max_transfer) // short packet also check for errors here + break; + + if(!td->link.terminate) { + td = MEM_ADDR(td->link.link); + } + else { + td=NULL; + } + } while(td); + + free_transaction(trans); + +#if ALLOCATE==1 + if ( (wLength!=0) && (data!=NULL)){ + if( (request_type & 0x80)) + memcpy(data, buffer, wLength); + forget2(buffer); + } +#endif + + DPRINTF("usb_control_message returning %d\n", data_len); + + return(data_len); +} + + +int poll_u_root_hub(unsigned short port, uchar controller) +{ + ushort value; + int addr=0; + int i; + static int do_over=0; + + value = inw(port); + + debug("poll_u_root_hub1 v=%08x\t", value); + + if(value & 0x02 || do_over == port) { + debug("poll_u_root_hub2 v=%08x\t", value); + do_over=0; + if(value & 0x01 ) { // if port connected + debug("poll_u_root_hub21 v=%08x\t", value); + DPRINTF("Connection on port %04x\n", port); + + outw(value, port); + for(i=0; i<40; i++) { + udelay(10000+usec_offset); + value = inw(port); + if(value & 0x02) { + outw(value, port); + i=0; + DPRINTF("BOUNCE!\n"); + } + } + + uport_wakeup(port); +// DPRINTF("Wakup %04x\n", port); + + uport_reset(port); + udelay(10); + uport_enable(port); + + if(!value & 0x01) { + DPRINTF("Device went away!\n"); + return(-1); + } + + addr = configure_device( port, controller, value & 0x100); + + if(addr<0) { + uport_disable(port); + udelay(20000); +// uport_reset(port); + uport_reset_long(port); + uport_suspend(port); + do_over=port; + uhc_clear_stat(); +// dump_uhci(0x38c0); + } + } else { + uport_suspend(port); + uport_disable(port); + DPRINTF("Port %04x disconnected\n", port); + // wave hands, deconfigure devices on this port! + } + } + + + return(addr); +} + +#endif diff --git a/src/filo/usb/uhci.h b/src/filo/usb/uhci.h new file mode 100644 index 000000000..17370a13d --- /dev/null +++ b/src/filo/usb/uhci.h @@ -0,0 +1,175 @@ +#ifndef _UHCI_H +#define _UHCI_H + +/* + * The link pointer is multi use. Some fields are valid only for some uses. + * In other cases, they must be 0 + * + */ + +#define MAX_POLLDEV 10 + +#define MAX_TRANSACTIONS 10 +#define MAX_QUEUEHEAD 255 +#define MAX_TD 1024 + + +typedef struct link_pointer { + unsigned long terminate:1; + unsigned long queue:1; + unsigned long depth:1; + unsigned long reserved:1; + unsigned long link:28; +} __attribute__((packed)) link_pointer_t; + +extern link_pointer_t *frame_list[]; + +void init_framelist(uchar dev); + + +#define SETUP_TOKEN 0x2d +#define IN_TOKEN 0x69 +#define OUT_TOKEN 0xe1 + +#define CTRL_RETRIES 3 +#define CONTROL_STS_RETRIES 0 + + +// some port features +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOW_SPEED 9 +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 + +// features +#define FEATURE_HALT 0 + +typedef struct td { + + link_pointer_t link; + + unsigned long actual:11; // actual length + unsigned long reserved2:5; + +// status/error flags + unsigned long res1:1; + unsigned long bitstuff:1; + unsigned long crc:1; + unsigned long nak:1; + unsigned long babble:1; + unsigned long buffer_error:1; + unsigned long stall:1; + unsigned long active:1; + + unsigned long interrupt:1; // interrupt on complete + unsigned long isochronous:1; + unsigned long lowspeed:1; + unsigned long retrys:2; + unsigned long detect_short:1; + unsigned long reserved3:2; + + unsigned long packet_type:8; // one of in (0x69), out (0xe1) or setup (0x2d) + unsigned long device_addr:7; + unsigned long endpoint:4; + unsigned long data_toggle:1; + unsigned long reserved:1; + unsigned long max_transfer:11; // misnamed. Desired length might be better + + void *buffer; + unsigned long data[4]; // free use by driver +} __attribute__((packed)) td_t; + +typedef struct queue_head { + link_pointer_t bredth; // depth must = 0 + link_pointer_t depth; // depth may vary randomly, ignore + unsigned long int udata[2]; +} __attribute__((packed)) queue_head_t; + +typedef struct transaction { + queue_head_t *qh; + td_t *td_list; + struct transaction *next; +} transaction_t; + +//##################################################### +int wait_head( queue_head_t *head, int count); + +extern queue_head_t *free_qh; +extern queue_head_t *queue_heads; + +queue_head_t *new_queue_head(void); +void free_queue_head( queue_head_t *qh); +void init_qh(void); + +extern td_t *free_td_list; +extern td_t *tds; + +void init_td(void); +td_t *new_td(void); +td_t *find_last_td(td_t *td); +void free_td( td_t *td); +link_pointer_t *queue_end( queue_head_t *queue); +void add_td( queue_head_t *head, td_t *td); + +extern transaction_t transactions[MAX_TRANSACTIONS]; +extern transaction_t *free_transactions; + +void init_transactions(void); +void free_transaction( transaction_t *trans ); +transaction_t *new_transaction(td_t *td); +transaction_t *add_transaction( transaction_t *trans, td_t *td); + + +#define USBCMD(x) hc_base[x] +#define USBSTS(x) (hc_base[x] + 0x02) +#define USBINTR(x) (hc_base[x] + 0x04) +#define FRNUM(x) ( hc_base[x] + 0x06) +#define FLBASE(x) ( hc_base[x] + 0x08) +#define SOFMOD(x) ( hc_base[x] + 0x0c) +#define PORTSC1(x) ( hc_base[x] + 0x10) +#define PORTSC2(x) ( hc_base[x] + 0x12) + +#define USBCMDRUN 0x01 +#define USBCMD_DEBUG 0x20 + +#define USBSTSHALTED 0x20 + + +void hc_reset(uchar dev); +int hc_stop(void); +int hc_start(uchar dev); + +extern queue_head_t *sched_queue[]; + +void init_sched(uchar dev); +int poll_queue_head( queue_head_t *qh); +int wait_queue_complete( queue_head_t *qh); + +extern int num_polls; +extern int (*devpoll[MAX_POLLDEV])(uchar); +extern uchar parm[MAX_POLLDEV]; + +transaction_t *_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +transaction_t *ctrl_msg(uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, uchar *data); +int schedule_transaction( uchar dev, transaction_t *trans); +int wait_transaction( transaction_t *trans); +void unlink_transaction( uchar dev, transaction_t *trans); +int uhci_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +int uhci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data); + + +// defined in uhci.c +int uhc_init(struct pci_device *dev); +void uhci_init(void); +void clear_uport_stat(unsigned short port); +int poll_u_root_hub(unsigned short port, uchar controller); + +#endif diff --git a/src/filo/usb/usb.c b/src/filo/usb/usb.c new file mode 100644 index 000000000..23afe9a71 --- /dev/null +++ b/src/filo/usb/usb.c @@ -0,0 +1,803 @@ +#ifdef USB_DISK + +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + + +#include "usb.h" +#include "uhci.h" +#include "ohci.h" +#include "debug_x.h" + + +#define ALLOCATE 1 + +int usec_offset=0; + +int num_controllers=0; + +uint32_t hc_base[MAX_CONTROLLERS]; +uint8_t hc_type[MAX_CONTROLLERS]; + + +void hci_init(void) +{ + int i; + struct pci_device *dev; + uint8_t prog_if; + + + for(i=0;i<MAX_CONTROLLERS; i++) { + hc_type[i] = 0xff; + } + + /* Find a PCI_SERIAL_USB class device */ + i=0; + num_controllers = 0; + while(num_controllers<MAX_CONTROLLERS) { + dev = pci_find_device(-1, -1, 0x0c03, -1, i); + if(!dev) break; + + prog_if = ((dev->class>>8) & 0xff); + if(prog_if == 0x00 ) { // UHCI + hc_type[num_controllers] = prog_if; + uhc_init(dev); + } + else if(prog_if == 0x10) { // OHCI + hc_type[num_controllers] = prog_if; + ohc_init(dev); + } +#if 0 + else if(prog_if == 0x20) { // EHCI + hc_type[num_controllers] = prog_if; + ehc_init(dev); + } +#endif + i++; + } + // From now should not change num_controllers any more + + uhci_init(); + ohci_init(); +} + + +int next_usb_dev; +usbdev_t usb_device[MAX_USB_DEV]; + +void init_devices(void) +{ + + memset(usb_device,0,sizeof(usb_device)); + usb_device[0].max_packet[0] = 8; + next_usb_dev=2; // 0 for all controller root hub, use MAX_CONTROLLERS instead??? + // do we need have one for every controller ?? or just use hc_base and hc_type instead + // For example 0 --> controller 1 root hub + // 1 --> controller 2 root hub + // 2 --> controller 3 root hub.... +} + + +inline int set_address( uchar address) +{ + int ret; + + ret = usb_control_msg(0, 0, SET_ADDRESS, address, 0, 0, NULL); + + return(ret); +} + +inline int clear_stall(uchar device, uchar endpoint) +{ + int ret; + + ret = usb_control_msg(device, CONTROL_ENDPOINT, CLEAR_FEATURE, FEATURE_HALT, endpoint, 0, NULL); + if(hc_type[device]==0x00) { + usb_device[device].toggle[endpoint]=0; + } + else if(hc_type[device]==0x10) { + usb_settoggle(&usb_device[device], endpoint & 0xf, ((endpoint & 0x80)>>7)^1, 0); + } + + return(ret); +} + +inline int device_reset(uchar device) { + return usb_control_msg(device, 0x21, 0xff, 0, 0, 0, NULL); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// String Descriptors +// +////////////////////////////////////////////////////////////////////////////////////// + +#define STRING_DESCRIPTOR 0x0300 + +int get_string( uchar addr, uchar string, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + ushort lang; + + if(!string) { + strcpy(buffer, "unknown"); + return(0); + } + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, 0, 4, buffer); + real_len = buffer[0]; + if(real_len>len) + real_len = len; + + lang = buffer[2] | buffer[3]<<8; + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, lang, real_len, buffer); + + // de-unicode it! + for(i=0, j=2; j<real_len; i++, j+=2) + buffer[i] = buffer[j]; + + buffer[i]=0; + real_len/=2; + + return(real_len); +} + +int get_string2( uchar addr, uchar string, ushort lang, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, lang, len, buffer); + + real_len = buffer[0]; + if(real_len>len) + real_len = len; + + if(real_len<=4) { + strcpy(buffer, "USB"); + real_len = 3; + buffer[real_len] = 0; + } else { + // de-unicode it! + for(i=0, j=2; j<real_len; i++, j+=2) + buffer[i] = buffer[j]; + + buffer[i]=0; + real_len/=2; + } + + return(real_len); +} +ushort get_lang( uchar addr, uchar string, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + ushort lang; + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, 0, 4, buffer); + lang = buffer[2] | buffer[3]<<8; + + return lang; +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// HUB functions. This will be moved to it's own module soonishly +// +/////////////////////////////////////////////////////////////////////////////////////// + +typedef struct port_charge { + ushort c_port_connection:1; + ushort c_port_enable:1; + ushort c_port_suspend:1; + ushort c_port_over_current:1; + ushort c_port_reset:1; + ushort reserved:11; +} port_change_t; + +typedef struct port_status { + ushort port_connection:1; + ushort port_enable:1; + ushort port_suspend:1; + ushort port_over_current:1; + ushort port_reset:1; + ushort reserved:3; + ushort port_power:1; + ushort port_lowspeed:1; + ushort port_highspeed:1; + ushort port_test:1; + ushort port_indicator:1; +} __attribute__ ((packed)) portstatus_t; + + +typedef struct portstat { + portstatus_t stat; + port_change_t change; +} __attribute__ ((packed)) portstat_t; + +int hub_port_reset( uchar addr, uchar port) +{ + int ret; + int tries=100; + portstat_t status; + + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_RESET, port, 0, NULL); // reset port + + while(tries--) { + udelay(10000); + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, port, 4, &status); + if(!status.change.c_port_reset) + continue; + + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_RESET, port, 0, NULL); // clear status + return(0); + } + + DPRINTF("hub_port_reset(%x, %x) failed,\n", addr, port); + dump_hex((uint8_t *)&status, 4, "status="); + + return(-1); +} + +int hub_port_resume( uchar addr, uchar port) +{ + int ret; + int tries=100; + portstat_t status; + + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, PORT_SUSPEND, port, 0, NULL); // reset port + + while(tries--) { + udelay(10000); + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, port, 4, &status); + if(!status.change.c_port_suspend) + continue; + + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_SUSPEND, port, 0, NULL); // clear status + return(0); + } + + return(-1); +} + +int poll_hub(uchar addr) +{ + int i; + int ret; + uchar devaddr=0; + hub_descriptor_t *desc; + portstat_t status; + + DPRINTF("Poll hub (%x)\n", addr); + desc = usb_device[addr].private; + + for(i=1; i<= desc->bNbrPorts; i++) { + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, i, 4, &status); +// DPRINTF("Get status for port %u returns: %d\n", i, ret); +// dump_hex(&status, 4, "status="); + + if(status.change.c_port_connection) { + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_CONNECTION, i, 0, NULL); // clear status + + if(status.stat.port_connection) { + udelay(desc->bPwrOn2PwrGood * 20000); + + hub_port_resume(addr, i); + + ret = hub_port_reset(addr,i); + udelay(10); + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_ENABLE, i, 0, NULL); // enable port + +// ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, i, 4, &status); +// DPRINTF("*****Get status again for port %u returns: %d\n", i, ret); +// dump_hex(&status, 4, "status="); + + devaddr = configure_device(i, usb_device[addr].controller, status.stat.port_lowspeed); + + // configure + } else { + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_SUSPEND, i, 0, NULL); // suspend port + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, PORT_ENABLE, i, 0, NULL); // disable port + DPRINTF("Hub %d, Port %04x disconnected\n", addr, i); + // deconfigure + } + } + } + return(devaddr); + +} + +int usb_hub_init( uchar addr) +{ + int i; + int ret; + hub_descriptor_t *desc; + + desc = allot(sizeof(hub_descriptor_t)); + + memset(desc, 0 , sizeof(hub_descriptor_t)); + + DPRINTF("hub init (%d)\n", addr); + + ret = usb_control_msg(addr, 0xa0, GET_DESCRIPTOR, 0x2900, 0, 8, desc); + ret = usb_control_msg(addr, 0xa0, GET_DESCRIPTOR, 0x2900, 0, desc->bLength, desc); + + usb_device[addr].private = desc; + + for(i=1; i<=desc->bNbrPorts; i++) + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_POWER, i, 0, NULL); // power port + + + // register hub to be polled + + devpoll[num_polls] = poll_hub; + parm[num_polls++] = addr; + + return(0); +} + +extern void ohci_dump_x(uchar controller); + +// will set up whatever device is answering at address 0. +int configure_device(uint32_t port, uchar controller, unsigned int lowspeed) +{ + device_descriptor_t *desc; + config_descriptor_t *conf; + interface_descriptor_t *iface; + endpoint_descriptor_t *epd; + int ret; + int i; + int addr = next_usb_dev++; + uchar buffer[512]; + uchar string[255]; + ushort lang; + uchar x[2]; + + desc = (device_descriptor_t *) buffer; + + memset( &usb_device[addr], 0, sizeof(usbdev_t)); + + printf("New USB device, setting address %d\n", addr); + if(lowspeed) { + usb_device[addr].lowspeed = usb_device[0].lowspeed = 1; + DPRINTF("LOWSPEED\n"); + } else + usb_device[addr].lowspeed = usb_device[0].lowspeed = 0; + + usb_device[0].port = usb_device[addr].port = port; + usb_device[0].controller = usb_device[addr].controller = controller; + usb_device[addr].toggle2[0]=0; + usb_device[addr].toggle2[1]=0; + +// hc_clear_stat(); + + ret = set_address(addr); + if(ret<0) { + DPRINTF("configure_device: set_address failed!\n"); + next_usb_dev--; + return(-1); + } + + mdelay(10); /* Let the SET_ADDRESS settle */ + + usb_device[addr].max_packet[0] = 8; + + + DPRINTF("Fetching device descriptor length\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x100, 0, 8, desc); + + usb_device[addr].max_packet[0] = desc->max_packet; + + DPRINTF("Fetching device descriptor\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x100, 0, desc->bLength, desc); + if(ret < desc->bLength) + return(-1); + + DPRINTF("Fetching config descriptor length\n"); + conf = (config_descriptor_t *) (buffer + sizeof(device_descriptor_t)); + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x200, 0, 8, conf); + + DPRINTF("Fetching config descriptor\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x200, 0, conf->wTotalLength, conf); + if(ret < conf->wTotalLength) + return(-1); + + iface = (interface_descriptor_t *) (buffer + sizeof(device_descriptor_t) + conf->bLength); + epd = (endpoint_descriptor_t *) (buffer + conf->bLength + iface->bLength + sizeof(device_descriptor_t)); + + DPRINTF("device:\n"); + dump_device_descriptor( desc, ""); + DPRINTF("config:\n"); + dump_config_descriptor( (uchar *)conf, ""); + + DPRINTF("Selecting Configuration number %x:\n", conf->bConfigurationValue); + ret = usb_control_msg(addr, 0, SET_CONFIGURATION, conf->bConfigurationValue, 0, 0, NULL); + +// mdelay(20); + +#if 0 + usb_control_msg(addr, 0x80, GET_CONFIGURATION, 0, 0, 1 , x); + DPRINTF("Configuration number = %x\n", x[0]); + + usb_control_msg(addr, 0x80, GET_STATUS, 0, addr, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); + + usb_control_msg(addr, 0x81, GET_STATUS, 0, 0, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); +#endif + + for(i=0; i<iface->bNumEndpoints;i++) { + if(!epd[i].bEndpointAddress) { + usb_device[addr].max_packet[ 1 ] = epd[i].wMaxPacketSize & 0x3ff; + } else { + usb_device[addr].max_packet[ epd[i].bEndpointAddress & 0x7f ] = epd[i].wMaxPacketSize & 0x3ff; + } + + if( (epd[i].bmAttributes & 0x03) == 0x01) // interrupt + usb_device[addr].interrupt = epd[i].bEndpointAddress; + + if( (epd[i].bmAttributes & 0x03) == 0x02) { // bulk +#if 0 + DPRINTF("clear stall on ep=%x\n", epd[i].bEndpointAddress); + clear_stall(addr, epd[i].bEndpointAddress); // to reset data toggle + udelay(10); +#endif + +#if 0 + usb_control_msg(addr, 0x82, GET_STATUS, 0, epd[i].bEndpointAddress, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); +#endif + + if(epd[i].bEndpointAddress & 0x80){ //in + usb_device[addr].bulk_in = epd[i].bEndpointAddress; + } + else { //out + usb_device[addr].bulk_out = epd[i].bEndpointAddress; + } + } + + } + + // determine device class + if(desc->Class) { + usb_device[addr].class = desc->Class; + usb_device[addr].subclass = desc->SubClass; + usb_device[addr].protocol = desc->protocol; + } else { + usb_device[addr].class = iface->bInterfaceClass; + usb_device[addr].subclass = iface->bInterfaceSubClass; + usb_device[addr].protocol = iface->bInterfaceProtocol; + } + + printf("%02x:%02x:%02x\n", usb_device[addr].class, usb_device[addr].subclass, usb_device[addr].protocol); +#if 0 + get_string(addr, desc->iManufacturor, sizeof(string), string); + printf("Manufacturor: %s\n", string); + + get_string(addr, desc->iProduct, sizeof(string), string); + printf("Product: %s\n", string); + + get_string(addr, desc->iSerial, sizeof(string), string); + printf("Serial: %s\n", string); +#else + lang = get_lang(addr, 0, sizeof(string), string); + + get_string2(addr, desc->iManufacturor, lang, sizeof(string), string); + printf("Manufacturor: %s\n", string); + + get_string2(addr, desc->iProduct, lang,sizeof(string), string); + printf("Product: %s\n", string); + + get_string2(addr, desc->iSerial, lang, sizeof(string), string); + printf("Serial: %s\n", string); +#endif + + switch( usb_device[addr].class) { + case 0x09: // hub + usb_hub_init(addr); + break; + + default: + break; + + } + + DPRINTF("DEVICE CONFIGURED\n"); + + return(addr); +} + +int num_polls=0; +int (*devpoll[MAX_POLLDEV])(uchar); +uchar parm[MAX_POLLDEV]; + +int poll_usb() +{ + int addr; + int found=0; + int i; + int j; + + for(i=0; i<num_controllers; i++) { + debug("poll_usb1 i=%d\t", i); + // if addr >0, should probably see what was attached! + if(hc_type[i]==0x00) { + addr = poll_u_root_hub(PORTSC1(i), i); + if(addr && !found) + found=addr; + + addr = poll_u_root_hub(PORTSC2(i), i); + if(addr && !found) + found=addr; + } + + else if(hc_type[i]==0x10) { + int NDP; + NDP = readl(&ohci_regs->roothub.a) & 0xff; + ohci_regs = (ohci_regs_t *)hc_base[i]; + for(j=0;j<NDP;j++) { + addr = poll_o_root_hub((uint32_t)&ohci_regs->roothub.portstatus[j], i); + if(addr && !found) + found=addr; + } + + } + + } + + // now poll registered drivers (such as the hub driver + for(i=0;i<num_polls; i++) { + debug("poll_usb2 i=%d\t", i); + addr = devpoll[i](parm[i]); + if(addr && !found) + found=addr; + } + + return(found); +} + + +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + uint8_t hc_num = usb_device[devnum].controller; + if(ep&0x80) { + ep = usb_device[devnum].bulk_in; + } else { + ep = usb_device[devnum].bulk_out; + } + + if(hc_type[hc_num] == 0x00) { //UHCI + return uhci_bulk_transfer(devnum, ep, len, data); + } + else if( hc_type[hc_num] == 0x10 ) { //OHCI + return ohci_bulk_transfer(devnum, ep, len, data); + } +#if 0 + else if (hc_type[hc_num] == 0x20 ) { //EHCI + return ehci_bulk_transfer(devnum, ep, len, data); + } +#endif + return 0; +} +int usb_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, + unsigned short wLength, void *data) +{ + + uint8_t hc_num = usb_device[devnum].controller; + + if(hc_type[hc_num] == 0x00) { //UHCI + return uhci_control_msg(devnum, request_type, request, wValue, wIndex, wLength, data); + } + else if( hc_type[hc_num] == 0x10 ) { //OHCI + return ohci_control_msg(devnum, request_type, request, wValue, wIndex, wLength, data); + } +#if 0 + else if (hc_type[hc_num] == 0x20 ) { //EHCI + return ehci_control_msg(devnum, request_type, request, wValue, wIndex, wLength, data); + } +#endif + return 0; +} + + +struct urb *usb_alloc_urb(int controller) +{ + struct urb *urb; + ohci_t *ohci = NULL; +#if URB_PRE_ALLOCATE!=1 + urb = (struct urb *)allot2(sizeof(struct urb),0xff); + if (!urb) { + printf("usb_alloc_urb: allot2 failed"); + return NULL; + } +#else + if(hc_type[controller] == 0x10) { //OHCI + ohci = &_ohci_x[controller]; + urb = ohci->urb; + } else { + urb = NULL; + } +#endif + + memset(urb, 0, sizeof(*urb)); + + return urb; +} +/** + * usb_free_urb - frees the memory used by a urb + * @urb: pointer to the urb to free + * + * If an urb is created with a call to usb_create_urb() it should be + * cleaned up with a call to usb_free_urb() when the driver is finished + * with it. + */ +void usb_free_urb(struct urb* urb) +{ +#if URB_PRE_ALLOCATE!=1 + if (urb) + forget2(urb); +#endif +} + +void usb_wait_urb_done(struct urb* urb, int timeout) +{ + usbdev_t *usb_dev = urb->dev; + if(hc_type[usb_dev->controller]==0x10) { + ohci_wait_urb_done(urb, timeout); + } + +} + + +int usb_submit_urb(struct urb *urb) +{ + if (urb && urb->dev) { +#if 0 + if(hc_type[urb->dev->controller] == 0x00) { + return uhci_submit_urb(urb); + } else +#endif + if(hc_type[urb->dev->controller] == 0x10) { + return ohci_submit_urb(urb); + } +#if 0 + else if(hc_type[urb->dev->controller] == 0x20) { + return ohci_submit_urb(urb); + } +#endif + return 0; + } + else + return -ENODEV; +} + +// Starts urb and waits for completion or timeout +static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) +{ + int status; + status = usb_submit_urb(urb); + +//for OHCI We will check the BLF and CLF, because HC after processing all td list, it will clear the BLF and CLF + usb_wait_urb_done(urb, timeout); +//Add by LYH to call complete function + if(urb->complete!=0) urb->complete(urb); + + if (actual_length) + *actual_length = urb->actual_length; + + usb_free_urb(urb); + return status; +} +// returns status (negative) or length (positive) +int usb_internal_control_msg(struct usbdev *usb_dev, unsigned int pipe, + struct usb_ctrlrequest *cmd, void *data, int len, int timeout, usb_complete_t complete) +{ + struct urb *urb; + int retv; + int length; + + urb = usb_alloc_urb(usb_dev->controller); + if (!urb) + return -ENOMEM; + + FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len, + complete,0); + + retv = usb_start_wait_urb(urb, timeout, &length); + if (retv < 0) + return retv; + else + return length; +} +int usb_control_msg_x(struct usbdev *dev, unsigned int pipe, u8 request, u8 requesttype, + u16 value, u16 index, void *data, u16 size, int timeout, usb_complete_t complete) +{ + struct usb_ctrlrequest *dr; + int ret; + int controller = dev->controller; + ohci_t *ohci; + +#if URB_PRE_ALLOCATE!=1 + dr = allot2(sizeof(struct usb_ctrlrequest), 0xf); + if (!dr) { + printf("usb_control_msg_x: dr allocate no MEM\n"); + return -ENOMEM; + } +#else + if(hc_type[controller] == 0x10) { //OHCI + ohci = &_ohci_x[controller]; + dr = ohci->dr; + } else { + dr = NULL; + } + +#endif + + dr->bRequestType = requesttype; + dr->bRequest = request; + dr->wValue = cpu_to_le16p(&value); + dr->wIndex = cpu_to_le16p(&index); + dr->wLength = cpu_to_le16p(&size); + + ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout, complete); + +#if URB_PRE_ALLOCATE!=1 + forget2(dr); +#endif + + return ret; +} +int usb_bulk_msg_x(struct usbdev *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout, usb_complete_t complete) +{ + struct urb *urb; + + if (len < 0) + return -EINVAL; + + urb=usb_alloc_urb(usb_dev->controller); + if (!urb) + return -ENOMEM; + + FILL_BULK_URB(urb, usb_dev, pipe, data, len, + complete, 0); + + return usb_start_wait_urb(urb,timeout,actual_length); +} + +#endif diff --git a/src/filo/usb/usb.h b/src/filo/usb/usb.h new file mode 100644 index 000000000..58fd57347 --- /dev/null +++ b/src/filo/usb/usb.h @@ -0,0 +1,435 @@ +#ifndef _USB_H +#define _USB_H + +#define URB_PRE_ALLOCATE 1 + +#define u32 uint32_t +#define u16 uint16_t +#define u8 uint8_t + +#define uchar uint8_t +#define ushort uint16_t +#define EBUSY 1 +#define ENOMEM 12 +#define ENODEV 19 +#define EINVAL 22 +#define EINPROGRESS 115 + +#define LINK_ADDR(x) ( virt_to_bus(x) >> 4) +#define MEM_ADDR(x) (void *) ( bus_to_virt( ((unsigned int) (x)) <<4) ) + +#define MAX_CONTROLLERS 4 + +extern int num_controllers; + +extern uint32_t hc_base[]; +extern uint8_t hc_type[]; + +// Some control message bmRequestType defines +#define CTRL_DEVICE 0 +#define CONTROL_INTERFACE 1 +#define CONTROL_ENDPOINT 2 +#define CONTROL_OTHER 3 +#define CONTROL_RECIPIENT_MASK 0x1f + +#define CONTROL_TYPE_STD 0 +#define CONTROL_TYPE_CLASS 0x20 +#define CONTROL_CLASS_VENDOR 0x40 +#define CONTROL_CLASS_MASK 0x60 + +#define CONTROL_OUT 0 +#define CONTROL_IN 0x80 +#define CONTROL_DIR_MASK 0x80 + +// bRequest values +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 + +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 + +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 + +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 + +#define SYNC_FRAME 12 + +// descriptor types +#define DEVICE_DESC 1 +#define CONFIGURATION_DESC 2 +#define STRING_DESC 3 +#define INTERFACE_DESC 4 +#define ENDPOINT_DESC 5 +#define OTHERSPEED_DESC 7 +#define POWER_DESC 8 + + +typedef struct device_descriptor { + uchar bLength; + uchar type; + + uchar bcdVersion[2]; + uchar Class; + uchar SubClass; + uchar protocol; + uchar max_packet; + + unsigned short idVendor; + unsigned short idProduct; + + uchar bcdDevice[2]; + uchar iManufacturor; + uchar iProduct; + uchar iSerial; + uchar bNumConfig; +} __attribute__((packed)) device_descriptor_t; + +#define GET_DESCRIPTOR 6 + +typedef struct config_descriptor { + uchar bLength; + uchar type; + + unsigned short wTotalLength; + uchar bNumInterfaces; + uchar bConfigurationValue; + uchar iConfiguration; + + uchar bmAttributes; + uchar bMaxPower; +} __attribute__((packed)) config_descriptor_t; + +typedef struct interface_descriptor { + uchar bLength; + uchar type; + + uchar bInterfaceNumber; + uchar bAlternateSetting; + + uchar bNumEndpoints; + uchar bInterfaceClass; + uchar bInterfaceSubClass; + uchar bInterfaceProtocol; + uchar iInterface; +} __attribute__((packed)) interface_descriptor_t; + +typedef struct endpoint_descriptor { + uchar bLength; + uchar type; + + uchar bEndpointAddress; + uchar bmAttributes; + unsigned short wMaxPacketSize; + uchar bInterval; +} __attribute__((packed)) endpoint_descriptor_t; + +typedef struct ctrl_msg { + uchar bmRequestType; + uchar bRequest; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; +} __attribute__((packed)) ctrl_msg_t; + +// Some descriptors for hubs, will be moved later +typedef struct hub_descriptor { + uchar bLength; + uchar type; + + uchar bNbrPorts; + ushort wHubCharacteristics; + uchar bPwrOn2PwrGood; + uchar bHubCntrCurrent; + + uchar DeviceRemovable; // assume bNbrPorts <=8 + uchar PortPwrCntrMask; +} __attribute__((packed)) hub_descriptor_t; + +#define MAX_USB_DEV 127 +#define MAX_EP 8 + +typedef struct usbdev { + uint32_t port; + uchar address; + uchar controller; + uchar class; + uchar subclass; + uchar protocol; + uchar bulk_in; + uchar bulk_out; + uchar interrupt; + uchar lowspeed; + uint32_t toggle2[2]; //For OHCI + uint32_t halted[2]; + uchar toggle[MAX_EP]; //for UHCI + unsigned short max_packet[MAX_EP]; + void *private; +} usbdev_t; + +// I will use urb as transaction for OHCI to remember the td and ed + +struct urb; +typedef void (*usb_complete_t)(struct urb *); + +struct urb +{ +#if 0 + spinlock_t lock; // lock for the URB +#endif + void *hcpriv; // private data for host controller +#if 0 + struct list_head urb_list; // list pointer to all active urbs + struct urb *next; // pointer to next URB +#endif + struct usbdev *dev; // pointer to associated USB device + unsigned int pipe; // pipe information + int status; // returned status + unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | etc. + void *transfer_buffer; // associated data buffer + void *transfer_dma; // dma addr for transfer_buffer + int transfer_buffer_length; // data buffer length + int actual_length; // actual data buffer length + int bandwidth; // bandwidth for this transfer request (INT or ISO) + unsigned char *setup_packet; // setup packet (control only) + void * setup_dma; // dma addr for setup_packet + // + int start_frame; // start frame (iso/irq only) + int number_of_packets; // number of packets in this request (iso) + int interval; // polling interval (irq only) + int error_count; // number of errors in this transfer (iso only) + int timeout; // timeout (in jiffies) + // + void *context; // context for completion routine + usb_complete_t complete; // pointer to completion routine + // +#if 0 + struct iso_packet_descriptor iso_frame_desc[0]; +#endif +}; + +typedef struct urb urb_t; + +/* + * urb->transfer_flags: + */ +#define USB_DISABLE_SPD 0x0001 +#define URB_SHORT_NOT_OK USB_DISABLE_SPD +#define USB_ISO_ASAP 0x0002 +#define USB_ASYNC_UNLINK 0x0008 +#define USB_QUEUE_BULK 0x0010 +#define USB_NO_FSBR 0x0020 +#define USB_ZERO_PACKET 0x0040 // Finish bulk OUTs always with zero length packet +#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */ + /* ... less overhead for QUEUE_BULK */ +#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD! + + +struct usb_ctrlrequest { + u8 bRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +} __attribute__ ((packed)); + +/* + * USB-status codes: + * USB_ST* maps to -E* and should go away in the future + */ + +#define USB_ST_NOERROR 0 +#define USB_ST_CRC (-EILSEQ) +#define USB_ST_BITSTUFF (-EPROTO) +#define USB_ST_NORESPONSE (-ETIMEDOUT) /* device not responding/handshaking */ +#define USB_ST_DATAOVERRUN (-EOVERFLOW) +#define USB_ST_DATAUNDERRUN (-EREMOTEIO) +#define USB_ST_BUFFEROVERRUN (-ECOMM) +#define USB_ST_BUFFERUNDERRUN (-ENOSR) +#define USB_ST_INTERNALERROR (-EPROTO) /* unknown error */ +#define USB_ST_SHORT_PACKET (-EREMOTEIO) +#define USB_ST_PARTIAL_ERROR (-EXDEV) /* ISO transfer only partially completed */ +#define USB_ST_URB_KILLED (-ENOENT) /* URB canceled by user */ +#define USB_ST_URB_PENDING (-EINPROGRESS) +#define USB_ST_REMOVED (-ENODEV) /* device not existing or removed */ +#define USB_ST_TIMEOUT (-ETIMEDOUT) /* communication timed out, also in urb->status**/ +#define USB_ST_NOTSUPPORTED (-ENOSYS) +#define USB_ST_BANDWIDTH_ERROR (-ENOSPC) /* too much bandwidth used */ +#define USB_ST_URB_INVALID_ERROR (-EINVAL) /* invalid value/transfer type */ +#define USB_ST_URB_REQUEST_ERROR (-ENXIO) /* invalid endpoint */ +#define USB_ST_STALL (-EPIPE) /* pipe stalled, also in urb->status*/ + +/** + * FILL_CONTROL_URB - macro to help initialize a control urb + * @URB: pointer to the urb to initialize. + * @DEV: pointer to the struct usb_device for this urb. + * @PIPE: the endpoint pipe + * @SETUP_PACKET: pointer to the setup_packet buffer + * @TRANSFER_BUFFER: pointer to the transfer buffer + * @BUFFER_LENGTH: length of the transfer buffer + * @COMPLETE: pointer to the usb_complete_t function + * @CONTEXT: what to set the urb context to. + * + * Initializes a control urb with the proper information needed to submit + * it to a device. This macro is depreciated, the usb_fill_control_urb() + * function should be used instead. + */ +#define FILL_CONTROL_URB(URB,DEV,PIPE,SETUP_PACKET,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \ + do {\ + (URB)->dev=DEV;\ + (URB)->pipe=PIPE;\ + (URB)->setup_packet=SETUP_PACKET;\ + (URB)->transfer_buffer=TRANSFER_BUFFER;\ + (URB)->transfer_buffer_length=BUFFER_LENGTH;\ + (URB)->complete=COMPLETE;\ + (URB)->context=CONTEXT;\ + } while (0) + + +/** + * FILL_BULK_URB - macro to help initialize a bulk urb + * @URB: pointer to the urb to initialize. + * @DEV: pointer to the struct usb_device for this urb. + * @PIPE: the endpoint pipe + * @TRANSFER_BUFFER: pointer to the transfer buffer + * @BUFFER_LENGTH: length of the transfer buffer + * @COMPLETE: pointer to the usb_complete_t function + * @CONTEXT: what to set the urb context to. + * + * Initializes a bulk urb with the proper information needed to submit it + * to a device. This macro is depreciated, the usb_fill_bulk_urb() + * function should be used instead. + */ +#define FILL_BULK_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \ + do {\ + (URB)->dev=DEV;\ + (URB)->pipe=PIPE;\ + (URB)->transfer_buffer=TRANSFER_BUFFER;\ + (URB)->transfer_buffer_length=BUFFER_LENGTH;\ + (URB)->complete=COMPLETE;\ + (URB)->context=CONTEXT;\ + } while (0) + + +/* + * USB directions + */ +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ + +#define PIPE_ISOCHRONOUS 0 +#define PIPE_INTERRUPT 1 +#define PIPE_CONTROL 2 +#define PIPE_BULK 3 + +#define usb_maxpacket(dev, pipe, out) ((dev)->max_packet[usb_pipeendpoint(pipe)]) +#define usb_packetid(pipe) (((pipe) & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT) + +#define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) +#define usb_pipein(pipe) (((pipe) >> 7) & 1) +#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f) +#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) +#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) +#define usb_pipedata(pipe) (((pipe) >> 19) & 1) +#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) +#define usb_pipetype(pipe) (((pipe) >> 30) & 3) +#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS) +#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT) +#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL) +#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK) + +#define PIPE_DEVEP_MASK 0x0007ff00 + + +/* The D0/D1 toggle bits */ +#define usb_gettoggle(dev, ep, out) (((dev)->toggle2[out] >> (ep)) & 1) +#define usb_dotoggle(dev, ep, out) ((dev)->toggle2[out] ^= (1 << (ep))) +static inline void usb_settoggle(struct usbdev *dev, + unsigned int ep, + unsigned int out, + int bit) +{ + dev->toggle2[out] &= ~(1 << ep); + dev->toggle2[out] |= bit << ep; +} + + +/* Endpoint halt control/status */ +#define usb_endpoint_out(ep_dir) (((ep_dir >> 7) & 1) ^ 1) +#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep))) +#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep))) +#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep))) + + +static inline unsigned int __create_pipe(usbdev_t *dev, unsigned int endpoint) +{ + return (dev->address << 8) | (endpoint << 15) | + ((dev->lowspeed == 1) << 26); +} + +static inline unsigned int __default_pipe(struct usbdev *dev) +{ + return ((dev->lowspeed == 1) << 26); +} + +/* Create various pipes... */ +#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#if 0 +#define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#endif +#define usb_sndbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#if 0 +#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#endif +#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev)) +#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev) | USB_DIR_IN) + + +extern int next_usb_dev; +usbdev_t usb_device[MAX_USB_DEV]; + +void init_devices(void); +void hci_init(void); +int hc_init(struct pci_device *dev); +inline int set_address(uchar address); +inline int clear_stall(uchar device, uchar endpoint); +int poll_usb(); +int configure_device(uint32_t port, uchar controller, unsigned int lowspeed); +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +int usb_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, + unsigned short wLength, void *data); + +int usb_control_msg_x(struct usbdev *dev, unsigned int pipe, u8 request, u8 requesttype, + u16 value, u16 index, void *data, u16 size, int timeout, usb_complete_t complete); +int usb_bulk_msg_x(struct usbdev *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout, usb_complete_t complete); + +#endif diff --git a/src/filo/usb/usb_scsi_low.c b/src/filo/usb/usb_scsi_low.c new file mode 100644 index 000000000..20afecae5 --- /dev/null +++ b/src/filo/usb/usb_scsi_low.c @@ -0,0 +1,172 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#define uchar uint8_t + +//#include "debug_x.h" +#include "usb_scsi_low.h" + +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); + +#define SG_DXFER_FROM_DEV -3 +#define SG_DXFER_TO_DEV -2 + +#define REQUEST_SENSE 0x03 + +#define CBW_SIG 0x43425355 + +typedef struct usb_cbw { + unsigned int signature; + unsigned int tag; + unsigned int transfer_len; // this is exclusive of cbw and csw + + uchar res1:7; + uchar direction:1; // 1 = device to host (read) + + uchar lun:4; + uchar res:4; + + uchar cbw_len:5; // the length of the SCSI command + uchar res3:3; + + uchar scsi_cmd[16]; +} __attribute__ ((packed)) usb_cbw_t; + +#define CSW_SIG 0x53425355 + +typedef struct usb_csw { + unsigned int signature; + unsigned int tag; + unsigned int residue; + uchar status; +} __attribute__ ((packed)) usb_csw_t; + + +int scsi_command( uchar device, unsigned char *cmd, int cmd_len, int direction, unsigned char *data, int data_len, char *sense_data, int sense_len) +{ + usb_cbw_t cbw; + usb_csw_t csw; + int ret; + + memset(&cbw,0,sizeof(usb_cbw_t)); + memset(&csw,0,sizeof(usb_csw_t)); + + cbw.signature = CBW_SIG; + cbw.tag = 777; + + memcpy(cbw.scsi_cmd, cmd, cmd_len); + cbw.cbw_len = cmd_len; + + if(direction == SG_DXFER_FROM_DEV) + cbw.direction=1; + + cbw.transfer_len = data_len; + + ret = usb_bulk_transfer(device, 2, sizeof(cbw), (uchar *) &cbw); + if(ret<0){ + DPRINTF("ERROR:Bulk write:\n"); + } + + if(data_len) { + if(cbw.direction) { + DPRINTF("scsi_command reading %d bytes\n", data_len); + ret = usb_bulk_transfer(device, 0x81, data_len, data); + DPRINTF("scsi_command read %d bytes\n", ret); + if(ret<0 || ret <data_len) { + DPRINTF("ERROR:Bulk read data ret = %d\n", ret); + } + } else { +// DPRINTF("scsi_command writing %u bytes\n", data_len); + ret = usb_bulk_transfer(device, 0x2, data_len, data); +// DPRINTF("scsi_command wrote %u bytes\n", ret); + if(ret<0) { + DPRINTF("ERROR:Bulk write data\n"); + } + } + } + +// DPRINTF("scsi_command fetching csw\n"); + ret = usb_bulk_transfer(device, 0x81, sizeof(csw), (uchar *) &csw); +// DPRINTF("scsi_command csw is %d bytes\n", ret); + if(ret<0 || ret < sizeof(csw)) { + DPRINTF("ERROR: Bulk read CSW ret = %d\n", ret); + return(-1); + } + + if(csw.status) { + DPRINTF("CSW: residue = %08x, status = %02x\n", csw.residue, csw.status); + DPRINTF("Getting sense data\n"); + request_sense( device, sense_data, sense_len); + return(-csw.status); + } + + return(data_len - csw.residue); +} + +int request_sense( uchar device, char *sense_data, int len) +{ + usb_cbw_t cbw; + usb_csw_t csw; + int ret; + + memset(&cbw,0,sizeof(usb_cbw_t)); + memset(&csw,0,sizeof(usb_csw_t)); + + cbw.signature = CBW_SIG; + cbw.tag = 666; + + cbw.scsi_cmd[0] = REQUEST_SENSE; + cbw.scsi_cmd[4] = len; + cbw.cbw_len = 6; + cbw.direction=1; + cbw.transfer_len = len; + + ret = usb_bulk_transfer(device, 2, sizeof(usb_cbw_t), (uchar *) &cbw); + if(ret<0 || ret < sizeof(usb_cbw_t)) { + DPRINTF("ERROR: sense Bulk write ret = %d\n", ret); + } + + ret = usb_bulk_transfer(device, 0x81, len, sense_data); + if(ret<0 || ret < len) { + DPRINTF("ERROR: sense Bulk read data ret = %d\n", ret); + } + + ret = usb_bulk_transfer(device, 0x81, sizeof(usb_csw_t), (uchar *) &csw); + if(ret<0 || ret < sizeof(usb_csw_t)) { + DPRINTF("ERROR: sense Bulk read CSW ret = %d\n", ret); + } + + return(-csw.status); +} + +#endif diff --git a/src/filo/usb/usb_scsi_low.h b/src/filo/usb/usb_scsi_low.h new file mode 100644 index 000000000..773614e43 --- /dev/null +++ b/src/filo/usb/usb_scsi_low.h @@ -0,0 +1,10 @@ +#ifndef _USB_SCSI_LOW_H +#define _USB_SCSI_LOW_H + +#define SG_DXFER_FROM_DEV -3 +#define SG_DXFER_TO_DEV -2 + +int scsi_command( unsigned char device, unsigned char *cmd, int cmd_len, int direction, unsigned char *data, int data_len, char *sense_data, int sense_len); +int request_sense( unsigned char device, char *sense_data, int len); + +#endif diff --git a/src/filo/usb/usb_x.c b/src/filo/usb/usb_x.c new file mode 100644 index 000000000..3da02d81f --- /dev/null +++ b/src/filo/usb/usb_x.c @@ -0,0 +1,163 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#include "usb.h" +#include "scsi_cmds.h" + +struct usbdisk_info_t { + struct controller *ctrl; + uint16_t heads; + uint16_t cylinders; + uint16_t sectors_per_track; + uint8_t model_number[41]; + uint8_t slave; + sector_t sectors; + int address_mode; +#define ADDRESS_MODE_CHS 0 +#define ADDRESS_MODE_LBA 1 +#define ADDRESS_MODE_LBA48 2 +#define ADDRESS_MODE_PACKET 3 + uint32_t hw_sector_size; + unsigned drive_exists : 1; + unsigned slave_absent : 1; + unsigned removable : 1; + + unsigned char usb_device_address; +}; + +struct usbdisk_info_t usbdisk_info; + +#define TEST 0 + +#if TEST==1 +#include "usb_scsi_low.h" +typedef struct partition_entry { + uchar boot_flag; + + uchar chs[7]; + + unsigned int lba_start; + unsigned int lba_len; +} __attribute__ ((packed)) partition_entry_t; + +typedef struct partition { + char loader[446]; + partition_entry_t entry[4]; + char sig[2]; +} __attribute__ ((packed)) partition_t; +#endif + +int usb_probe(int drive) +{ + struct usbdisk_info_t *info = &usbdisk_info; +#if TEST==1 + partition_t part; + unsigned char sense_data[32]; +#endif + int i,res; + int error_count=100; + + printf("LinuxLabs USB bootloader\n"); + +// outb( 0x30, 0x70); // reset primary boot +// outb( 0xff, 0x71); + init_devices(); + hci_init(); + + info->usb_device_address = 0; + // find first usb device + + while(error_count && (res = poll_usb())) // keep polling usb until no more devices are enumerated + if(res<0) + if(!--error_count) + printf("There is a USB device, but it won't init! This is a bad thing.\n"); + + for(i=0; i< next_usb_dev ; i++) { + if(usb_device[i].class == 0x08 && usb_device[i].subclass == 0x06 && usb_device[i].protocol == 0x50) { + printf("Found USB block device %d\n", i); + if(drive==0) { + info->usb_device_address = i; + break; + } + drive--; + } + } + + if(info->usb_device_address == 0) return -1; + + UnitReady(info->usb_device_address); + +#if TEST==1 +//Test + printf("Requesting initial sense data\n"); + request_sense( info->usb_device_address, sense_data, 32); + PrintSense(sense_data, 32); + + res = ll_read_block(info->usb_device_address, (uint8_t *)&part, 0, 1); + + printf("ll_read_block returns %d\n", res); + + res=-1; + + debug("part address (phy) = %x, (virt) = %x\n", (uint32_t) virt_to_phys(&part), (uint32_t)&part); + + for(i=0; i<4; i++) { + printf("%d: boot=%02x, start=%08x length=%08x\n",i, part.entry[i].boot_flag, part.entry[i].lba_start, part.entry[i] +.lba_len); + } + + +#endif + + return 0; +} +int usb_read(int drive, sector_t sector, void *buffer) +{ + struct usbdisk_info_t *info = &usbdisk_info; + int result; + int blocknum = sector; + int i; +// printf("sector= %d\t", blocknum); + result = ll_read_block(info->usb_device_address, buffer,blocknum, 1); +#if 0 + for(i=0;i<128;i++) { + if((i%4)==0) printf("\n %08x:",i*4); + printf(" %08x ",(uint32_t)*((uint32_t *)buffer+i)); + } +#endif + + if(result!=512) return -1; + + return 0; +} +#endif |
