📄 disk_io.c
字号:
/* disk_io.c - implement abstract BIOS disk input and output *//* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 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 <shared.h>#include <filesys.h>#ifdef SUPPORT_NETBOOT# define GRUB 1# include <etherboot.h>#endif#ifdef GRUB_UTIL# include <device.h>#endif/* instrumentation variables */void (*disk_read_hook) (int, int, int) = NULL;void (*disk_read_func) (int, int, int) = NULL;#ifndef STAGE1_5int print_possibilities;static int do_completion;static int unique;static char *unique_string;#endifint fsmax;struct fsys_entry fsys_table[NUM_FSYS + 1] ={ /* TFTP should come first because others don't handle net device. */# ifdef FSYS_TFTP {"tftp", tftp_mount, tftp_read, tftp_dir, tftp_close, 0},# endif# 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_VSTAFS {"vstafs", vstafs_mount, vstafs_read, vstafs_dir, 0, 0},# 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_UFS2 {"ufs2", ufs2_mount, ufs2_read, ufs2_dir, 0, ufs2_embed},# endif# ifdef FSYS_ISO9660 {"iso9660", iso9660_mount, iso9660_read, iso9660_dir, 0, 0},# endif /* XX FFS should come last as it's superblock is commonly crossing tracks on floppies from track 1 to 2, while others only use 1. */# ifdef FSYS_FFS {"ffs", ffs_mount, ffs_read, ffs_dir, 0, ffs_embed},# endif {0, 0, 0, 0, 0, 0}};/* These have the same format as "boot_drive" and "install_partition", but are meant to be working values. */unsigned long current_drive = GRUB_INVALID_DRIVE;unsigned long current_partition;#ifndef STAGE1_5/* The register ESI should contain the address of the partition to be used for loading a chain-loader when chain-loading the loader. */unsigned long boot_part_addr = 0;#endif/* * Global variables describing details of the filesystem *//* FIXME: BSD evil hack */#include "freebsd.h"int bsd_evil_hack;/* filesystem type */int fsys_type = NUM_FSYS;#ifndef NO_BLOCK_FILESstatic int block_file = 0;#endif /* NO_BLOCK_FILES *//* these are the translated numbers for the open partition */unsigned long part_start;unsigned long part_length;int current_slice;/* disk buffer parameters */int buf_drive = -1;int buf_track;struct geometry buf_geom;/* filesystem common variables */int filepos;int filemax;static inline unsigned longlog2 (unsigned long word){ asm volatile ("bsfl %1,%0" : "=r" (word) : "r" (word)); return word;}intrawread (int drive, int sector, int byte_offset, int byte_len, char *buf){ int slen, sectors_per_vtrack; int sector_size_bits = log2 (buf_geom.sector_size); if (byte_len <= 0) return 1; while (byte_len > 0 && !errnum) { int soff, num_sect, track, size = byte_len; char *bufaddr; /* * Check track buffer. If it isn't valid or it is from the * wrong disk, then reset the disk geometry. */ if (buf_drive != drive) { if (get_diskinfo (drive, &buf_geom)) { errnum = ERR_NO_DISK; return 0; } buf_drive = drive; buf_track = -1; sector_size_bits = log2 (buf_geom.sector_size); } /* Make sure that SECTOR is valid. */ if (sector < 0 || sector >= buf_geom.total_sectors) { errnum = ERR_GEOM; return 0; } slen = ((byte_offset + byte_len + buf_geom.sector_size - 1) >> sector_size_bits); /* Eliminate a buffer overflow. */ if ((buf_geom.sectors << sector_size_bits) > BUFFERLEN) sectors_per_vtrack = (BUFFERLEN >> sector_size_bits); else sectors_per_vtrack = buf_geom.sectors; /* Get the first sector of track. */ soff = sector % sectors_per_vtrack; track = sector - soff; num_sect = sectors_per_vtrack - soff; bufaddr = ((char *) BUFFERADDR + (soff << sector_size_bits) + byte_offset); if (track != buf_track) { int bios_err, read_start = track, read_len = sectors_per_vtrack; /* * If there's more than one read in this entire loop, then * only make the earlier reads for the portion needed. This * saves filling the buffer with data that won't be used! */ if (slen > num_sect) { read_start = sector; read_len = num_sect; bufaddr = (char *) BUFFERADDR + byte_offset; } bios_err = biosdisk (BIOSDISK_READ, drive, &buf_geom, read_start, read_len, BUFFERSEG); if (bios_err) { buf_track = -1; if (bios_err == BIOSDISK_ERROR_GEOMETRY) errnum = ERR_GEOM; else { /* * If there was an error, try to load only the * required sector(s) rather than failing completely. */ if (slen > num_sect || biosdisk (BIOSDISK_READ, drive, &buf_geom, sector, slen, BUFFERSEG)) errnum = ERR_READ; bufaddr = (char *) BUFFERADDR + byte_offset; } } else buf_track = track; if ((buf_track == 0 || sector == 0) && (PC_SLICE_TYPE (BUFFERADDR, 0) == PC_SLICE_TYPE_EZD || PC_SLICE_TYPE (BUFFERADDR, 1) == PC_SLICE_TYPE_EZD || PC_SLICE_TYPE (BUFFERADDR, 2) == PC_SLICE_TYPE_EZD || PC_SLICE_TYPE (BUFFERADDR, 3) == PC_SLICE_TYPE_EZD)) { /* This is a EZD disk map sector 0 to sector 1 */ if (buf_track == 0 || slen >= 2) { /* We already read the sector 1, copy it to sector 0 */ memmove ((char *) BUFFERADDR, (char *) BUFFERADDR + buf_geom.sector_size, buf_geom.sector_size); } else { if (biosdisk (BIOSDISK_READ, drive, &buf_geom, 1, 1, BUFFERSEG)) errnum = ERR_READ; } } } if (size > ((num_sect << sector_size_bits) - byte_offset)) size = (num_sect << sector_size_bits) - byte_offset; /* * Instrumentation to tell which sectors were read and used. */ if (disk_read_func) { int sector_num = sector; int length = buf_geom.sector_size - byte_offset; if (length > size) length = size; (*disk_read_func) (sector_num++, byte_offset, length); length = size - length; if (length > 0) { while (length > buf_geom.sector_size) { (*disk_read_func) (sector_num++, 0, buf_geom.sector_size); length -= buf_geom.sector_size; } (*disk_read_func) (sector_num, 0, length); } } grub_memmove (buf, bufaddr, size); buf += size; byte_len -= size; sector += num_sect; byte_offset = 0; } return (!errnum);}intdevread (int sector, int byte_offset, int byte_len, char *buf){ /* * Check partition boundaries */ if (sector < 0 || ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS)) >= part_length)) { errnum = ERR_OUTSIDE_PART; return 0; } /* * Get the read to the beginning of a partition. */ sector += byte_offset >> SECTOR_BITS; byte_offset &= SECTOR_SIZE - 1;#if !defined(STAGE1_5) if (disk_read_hook && debug) printf ("<%d, %d, %d>", sector, byte_offset, byte_len);#endif /* !STAGE1_5 */ /* * Call RAWREAD, which is very similar, but: * * -- It takes an extra parameter, the drive number. * -- It requires that "sector" is relative to the beginning * of the disk. * -- It doesn't handle offsets of more than 511 bytes into the * sector. */ return rawread (current_drive, part_start + sector, byte_offset, byte_len, buf);}#ifndef STAGE1_5intrawwrite (int drive, int sector, char *buf){ if (sector == 0) { if (biosdisk (BIOSDISK_READ, drive, &buf_geom, 0, 1, SCRATCHSEG)) { errnum = ERR_WRITE; return 0; } if (PC_SLICE_TYPE (SCRATCHADDR, 0) == PC_SLICE_TYPE_EZD || PC_SLICE_TYPE (SCRATCHADDR, 1) == PC_SLICE_TYPE_EZD || PC_SLICE_TYPE (SCRATCHADDR, 2) == PC_SLICE_TYPE_EZD || PC_SLICE_TYPE (SCRATCHADDR, 3) == PC_SLICE_TYPE_EZD) sector = 1; } memmove ((char *) SCRATCHADDR, buf, SECTOR_SIZE); if (biosdisk (BIOSDISK_WRITE, drive, &buf_geom, sector, 1, SCRATCHSEG)) { errnum = ERR_WRITE; return 0; } if (sector - sector % buf_geom.sectors == buf_track) /* Clear the cache. */ buf_track = -1; return 1;}intdevwrite (int sector, int sector_count, char *buf){#if defined(GRUB_UTIL) && defined(__linux__) if (current_partition != 0xFFFFFF && is_disk_device (device_map, current_drive)) { /* If the grub shell is running under Linux and the user wants to embed a Stage 1.5 into a partition instead of a MBR, use system calls directly instead of biosdisk, because of the bug in Linux. *sigh* */ return write_to_partition (device_map, current_drive, current_partition, sector, sector_count, buf); } else#endif /* GRUB_UTIL && __linux__ */ { int i; for (i = 0; i < sector_count; i++) { if (! rawwrite (current_drive, part_start + sector + i, buf + (i << SECTOR_BITS))) return 0; } return 1; }}static intsane_partition (void){ /* network drive */ if (current_drive == NETWORK_DRIVE) return 1; if (!(current_partition & 0xFF000000uL) && ((current_drive & 0xFFFFFF7F) < 8 || current_drive == cdrom_drive) && (current_partition & 0xFF) == 0xFF && ((current_partition & 0xFF00) == 0xFF00 || (current_partition & 0xFF00) < 0x800) && ((current_partition >> 16) == 0xFF || (current_drive & 0x80))) return 1; errnum = ERR_DEV_VALUES; return 0;}#endif /* ! STAGE1_5 */static voidattempt_mount (void){#ifndef STAGE1_5 for (fsys_type = 0; fsys_type < NUM_FSYS; fsys_type++) if ((fsys_table[fsys_type].mount_func) ()) break; if (fsys_type == NUM_FSYS && errnum == ERR_NONE) errnum = ERR_FSYS_MOUNT;#else fsys_type = 0; if ((*(fsys_table[fsys_type].mount_func)) () != 1) { fsys_type = NUM_FSYS; errnum = ERR_FSYS_MOUNT; }#endif}#ifndef STAGE1_5/* Turn on the active flag for the partition SAVED_PARTITION in the drive SAVED_DRIVE. If an error occurs, return zero, otherwise return non-zero. */intmake_saved_active (void){ char mbr[512]; if (saved_drive & 0x80) { /* Hard disk */ int part = saved_partition >> 16; /* If the partition is not a primary partition, the active flag is meaningless. (XXX: Really?) */ if (part > 3) { errnum = ERR_DEV_VALUES; return 0; } /* Read the MBR in the scratch space. */ if (! rawread (saved_drive, 0, 0, SECTOR_SIZE, mbr)) return 0; /* If the partition is an extended partition, setting the active flag violates the specification by IBM. */ if (IS_PC_SLICE_TYPE_EXTENDED (PC_SLICE_TYPE (mbr, part))) { errnum = ERR_DEV_VALUES; return 0; } /* Check if the active flag is disabled. */ if (PC_SLICE_FLAG (mbr, part) != PC_SLICE_FLAG_BOOTABLE) { int i; /* Clear all the active flags in this table. */ for (i = 0; i < 4; i++) PC_SLICE_FLAG (mbr, i) = 0; /* Set the flag. */ PC_SLICE_FLAG (mbr, part) = PC_SLICE_FLAG_BOOTABLE; /* Write back the MBR. */ if (! rawwrite (saved_drive, 0, mbr)) return 0; } } else { /* If the drive is not a hard disk drive, you shouldn't call this function. (XXX: Should I just ignore this error?) */ errnum = ERR_DEV_VALUES; return 0; } return 1;}/* Hide/Unhide CURRENT_PARTITION. */intset_partition_hidden_flag (int hidden){ unsigned long part = 0xFFFFFF; unsigned long start, len, offset, ext_offset; int entry, type; char mbr[512]; /* The drive must be a hard disk. */ if (! (current_drive & 0x80)) { errnum = ERR_BAD_ARGUMENT; return 1; } /* The partition must be a PC slice. */ if ((current_partition >> 16) == 0xFF || (current_partition & 0xFFFF) != 0xFFFF) { errnum = ERR_BAD_ARGUMENT; return 1; } /* Look for the partition. */ while (next_partition (current_drive, 0xFFFFFF, &part, &type, &start, &len, &offset, &entry, &ext_offset, mbr)) { if (part == current_partition) { /* Found. */ if (hidden) PC_SLICE_TYPE (mbr, entry) |= PC_SLICE_TYPE_HIDDEN_FLAG; else PC_SLICE_TYPE (mbr, entry) &= ~PC_SLICE_TYPE_HIDDEN_FLAG; /* Write back the MBR to the disk. */ buf_track = -1; if (! rawwrite (current_drive, offset, mbr)) return 1; /* Succeed. */ return 0; } } return 1;}static voidcheck_and_print_mount (void){ attempt_mount (); if (errnum == ERR_FSYS_MOUNT) errnum = ERR_NONE; if (!errnum) print_fsys_type (); print_error ();}#endif /* STAGE1_5 *//* Get the information on next partition on the drive DRIVE. The caller must not modify the contents of the arguments when iterating this function. The partition representation in GRUB will be stored in *PARTITION. Likewise, the partition type in *TYPE, the start sector in *START, the length in *LEN, the offset of the partition table in *OFFSET, the entry number in the table in *ENTRY, the offset of the extended partition in *EXT_OFFSET. BUF is used to store a MBR, the boot sector of a partition, or a BSD label sector, and it must be at least 512 bytes length. When calling this function first, *PARTITION must be initialized to 0xFFFFFF. The return value is zero if fails, otherwise non-zero. */intnext_partition (unsigned long drive, unsigned long dest, unsigned long *partition, int *type, unsigned long *start, unsigned long *len, unsigned long *offset, int *entry, unsigned long *ext_offset, char *buf){ /* Forward declarations. */ auto int next_bsd_partition (void); auto int next_pc_slice (void); /* Get next BSD partition in current PC slice. */ int next_bsd_partition (void) { int i; int bsd_part_no = (*partition & 0xFF00) >> 8; /* If this is the first time... */ if (bsd_part_no == 0xFF) { /* Check if the BSD label is within current PC slice. */ if (*len < BSD_LABEL_SECTOR + 1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -