/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This 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. * * This 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 this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.driver.block.ide.disk; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.naming.NameNotFoundException; import javax.naming.NamingException; import org.apache.log4j.Logger; import org.jnode.bootlog.BootLogInstance; import org.jnode.driver.Bus; import org.jnode.driver.Device; import org.jnode.driver.DeviceAlreadyRegisteredException; import org.jnode.driver.DeviceManager; import org.jnode.driver.Driver; import org.jnode.driver.DriverException; import org.jnode.driver.block.BlockDeviceAPI; import org.jnode.driver.block.BlockDeviceAPIHelper; import org.jnode.driver.bus.ide.IDEBus; import org.jnode.driver.bus.ide.IDEConstants; import org.jnode.driver.bus.ide.IDEDevice; import org.jnode.driver.bus.ide.IDEDeviceAPI; import org.jnode.driver.bus.ide.IDEDeviceFactory; import org.jnode.driver.bus.ide.IDEDriveDescriptor; import org.jnode.driver.bus.ide.IDEDriverUtils; import org.jnode.driver.bus.ide.command.IDERWSectorsCommand; import org.jnode.driver.bus.ide.command.IDEReadSectorsCommand; import org.jnode.driver.bus.ide.command.IDEWriteSectorsCommand; import org.jnode.naming.InitialNaming; import org.jnode.partitions.ibm.IBMPartitionTable; import org.jnode.partitions.ibm.IBMPartitionTableEntry; import org.jnode.util.TimeoutException; /** * Device driver for IDE disks. * * @author epr */ public class IDEDiskDriver extends Driver implements IDEDeviceAPI<IBMPartitionTableEntry>, IDEConstants { /** * My logger */ private static final Logger log = Logger.getLogger(IDEDiskDriver.class); /** * Number of addressable sectors */ private long maxSector; /** Has LBA support? */ //private boolean lba; /** Has DMA support? */ //private boolean dma; /** * Support 48-bit addressing? */ private boolean is48bit; private IDEDiskBus diskBus; private IBMPartitionTable pt; protected void startDevice() throws DriverException { final IDEDevice dev = (IDEDevice) getDevice(); diskBus = new IDEDiskBus(dev); /* Register the IDEDevice API */ dev.registerAPI(IDEDeviceAPI.class, new IDEDeviceBlockAlignmentSupport<IBMPartitionTableEntry>(this, SECTOR_SIZE)); /* Get basic configuration */ final IDEDriveDescriptor descr = dev.getDescriptor(); //lba = descr.supportsLBA(); //dma = descr.supportsDMA(); is48bit = descr.supports48bitAddressing(); maxSector = descr.getSectorsAddressable(); // Look for partitions try { // Find the devicemanager DeviceManager devMan = InitialNaming.lookup(DeviceManager.NAME); // Read the bootsector final byte[] bs1 = new byte[SECTOR_SIZE]; read(0, ByteBuffer.wrap(bs1)); // Read the bootsector twice, since the first read seems to fail. // todo: THIS IS A WORKAROUND final byte[] bs = new byte[SECTOR_SIZE]; read(0, ByteBuffer.wrap(bs)); IDEDeviceFactory factory; try { factory = IDEDriverUtils.getIDEDeviceFactory(); } catch (NamingException ex) { throw new DriverException(ex); } log.debug("Creating partition table object on " + dev.getId()); this.pt = factory.createIBMPartitionTable(bs, dev); log.debug("Created partition table object"); int partIndex = 0; int i = 0; for (IBMPartitionTableEntry pte : pt) { log.debug("Processing partition " + i); if (pte == null) { BootLogInstance.get().warn("PartitionTableEntry #" + i + " is null"); } else if (pte.isValid()) { log.debug("Partition " + i + " is valid"); registerPartition(devMan, dev, pte, partIndex); } partIndex++; i++; } if (!pt.getExtendedPartitions().isEmpty()) { // Create partition devices for the extended partition log.debug("Extended"); partIndex = registerExtendedPartition(devMan, dev, partIndex); } } catch (DeviceAlreadyRegisteredException ex) { log.error("Partition device is already known"); throw new DriverException("Partition device is already known???? Probably a bug", ex); } catch (IOException ex) { log.error("Cannot read partition table", ex); throw new DriverException("Cannot read partition table", ex); } catch (NameNotFoundException ex) { log.error("Cannot find DeviceManager", ex); throw new DriverException("Cannot find DeviceManager", ex); } catch (Throwable ex) { log.error("Unknown error", ex); throw new DriverException("Unknown error", ex); } } protected void stopDevice() throws DriverException { final IDEDevice dev = (IDEDevice) getDevice(); // find mounted partitions on this device and unregister them ! try { DeviceManager devMan = InitialNaming.lookup(DeviceManager.NAME); Collection<Device> devices = devMan.getDevices(); final ArrayList<IDEDiskPartitionDevice> toStop = new ArrayList<IDEDiskPartitionDevice>(); for (Device device : devices) { if (device instanceof IDEDiskPartitionDevice) { IDEDiskPartitionDevice partition = (IDEDiskPartitionDevice) device; if (partition.getParent() == dev) { toStop.add(partition); } } } for (IDEDiskPartitionDevice partition : toStop) { devMan.unregister(partition); } } catch (NameNotFoundException e) { throw new DriverException("Problem while stopping this IDE device", e); } dev.unregisterAPI(BlockDeviceAPI.class); this.pt = null; } public void flush() { // Nothing to do yet } public long getLength() { return maxSector * SECTOR_SIZE; } public void read(long devOffset, ByteBuffer destBuf) throws IOException { transfer(devOffset, destBuf, false); } public void write(long devOffset, ByteBuffer srcBuf) throws IOException { transfer(devOffset, srcBuf, true); } protected void transfer(long devOffset, ByteBuffer buf, boolean isWrite) throws IOException { // int bufOffset = 0; int length = buf.remaining(); if (length < SECTOR_SIZE) { log.debug("Transfer length=" + length + (isWrite ? " Wr " : " Rd ")); } BlockDeviceAPIHelper.checkBounds(this, devOffset, length); BlockDeviceAPIHelper.checkAlignment(SECTOR_SIZE, this, devOffset, length); final long lbaStart = devOffset / SECTOR_SIZE; final int sectors = length / SECTOR_SIZE; final String errorSource = isWrite ? "write" : "read"; if (lbaStart + sectors > this.maxSector) { throw new IOException(errorSource + " beyond device sectors"); } final IDEDevice dev = (IDEDevice) getDevice(); final IDEBus bus = (IDEBus) dev.getBus(); final int maxSectorCount = is48bit ? MAX_SECTOR_COUNT_48 : MAX_SECTOR_COUNT_28; final boolean primary = dev.isPrimary(); final boolean master = dev.isMaster(); while (length > 0) { final long partLbaStart = devOffset / SECTOR_SIZE; final int partSectorCount = Math.min(length / SECTOR_SIZE, maxSectorCount); final int partLength = partSectorCount * SECTOR_SIZE; final IDERWSectorsCommand cmd = isWrite ? new IDEWriteSectorsCommand(primary, master, is48bit, partLbaStart, partSectorCount, buf) : new IDEReadSectorsCommand(primary, master, is48bit, partLbaStart, partSectorCount, buf); try { log.debug("bus.exAndWt" + (isWrite ? "W" : "R") + " dev=" + dev.getId() + " start=" + partLbaStart + " sectors=" + partSectorCount + " len=" + partLength); bus.executeAndWait(cmd, IDE_DATA_XFER_TIMEOUT); } catch (InterruptedException ex) { throw new IOException("IDE " + errorSource + " interrupted", ex); } catch (TimeoutException ex) { throw new InterruptedIOException("IDE timeout: " + ex.getMessage()); } if (cmd.hasError()) { throw new IOException("IDE " + errorSource + " error:" + cmd.getError()); } length -= partLength; devOffset += partLength; } } static class IDEDiskBus extends Bus { public IDEDiskBus(IDEDevice parent) { super(parent); } } /* * Register the given partition entry (maybe an extended partition entry) */ private void registerPartition(DeviceManager devMan, IDEDevice dev, IBMPartitionTableEntry pte, int partIndex) throws DeviceAlreadyRegisteredException, DriverException { final String id = dev.getId() + partIndex; final IDEDiskPartitionDevice pdev = new IDEDiskPartitionDevice( diskBus, id, dev, pte, pte.getStartLba(), pte.getNrSectors()); pdev.setDriver(new IDEDiskPartitionDriver()); devMan.register(pdev); } /** * register all the partitions included in the extended partition * * @param devMan * @param dev * @param partIndex the first partition index to use * @return the next partition index * @throws DeviceAlreadyRegisteredException * * @throws DriverException */ private int registerExtendedPartition(DeviceManager devMan, IDEDevice dev, int partIndex) throws DeviceAlreadyRegisteredException, DriverException { //now we should have an filled vector in the pt final List<IBMPartitionTableEntry> extendedPartitions = pt.getExtendedPartitions(); log.info("Have " + extendedPartitions.size() + " Extended partitions found"); for (int iPart = 0; iPart < extendedPartitions.size(); iPart++) { IBMPartitionTableEntry pteExt = extendedPartitions.get(iPart); registerPartition(devMan, dev, pteExt, partIndex); if (iPart < (extendedPartitions.size() - 1)) { partIndex++; } } return partIndex; } public int getSectorSize() throws IOException { return SECTOR_SIZE; } /** * Gets the partition table that this block device contains. * * @return {@code null} if no partition table is found. * @throws IOException */ public IBMPartitionTable getPartitionTable() throws IOException { return pt; } }