From peterjeremy@optushome.com.au  Sat Mar 12 23:51:15 2005
Return-Path: <peterjeremy@optushome.com.au>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 305C916A4D5
	for <FreeBSD-gnats-submit@freebsd.org>; Sat, 12 Mar 2005 23:51:15 +0000 (GMT)
Received: from mail04.syd.optusnet.com.au (mail04.syd.optusnet.com.au [211.29.132.185])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 2703143D6A
	for <FreeBSD-gnats-submit@freebsd.org>; Sat, 12 Mar 2005 23:51:13 +0000 (GMT)
	(envelope-from peterjeremy@optushome.com.au)
Received: from server.vk2pj.dyndns.org (c211-30-75-229.belrs2.nsw.optusnet.com.au [211.30.75.229])
	by mail04.syd.optusnet.com.au (8.12.11/8.12.11) with ESMTP id j2CNpA8S011852
	(version=TLSv1/SSLv3 cipher=EDH-RSA-DES-CBC3-SHA bits=168 verify=NO)
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 13 Mar 2005 10:51:11 +1100
Received: from server.vk2pj.dyndns.org (localhost.vk2pj.dyndns.org [127.0.0.1])
	by server.vk2pj.dyndns.org (8.13.1/8.13.1) with ESMTP id j2CNpA8K079242;
	Sun, 13 Mar 2005 10:51:10 +1100 (EST)
	(envelope-from peter@server.vk2pj.dyndns.org)
Received: (from peter@localhost)
	by server.vk2pj.dyndns.org (8.13.1/8.13.1/Submit) id j2CNpAeu079241;
	Sun, 13 Mar 2005 10:51:10 +1100 (EST)
	(envelope-from peter)
Message-Id: <200503122351.j2CNpAeu079241@server.vk2pj.dyndns.org>
Date: Sun, 13 Mar 2005 10:51:10 +1100 (EST)
From: Peter Jeremy <peterjeremy@optushome.com.au>
Reply-To: Peter Jeremy <peterjeremy@optushome.com.au>
To: FreeBSD-gnats-submit@freebsd.org
Cc: peter@vk2pj.dyndns.org
Subject: [patch] Add support for re-sizing ATA disks
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         78758
>Category:       kern
>Synopsis:       [ata] [patch] Add support for re-sizing ATA disks
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun Mar 13 00:00:13 GMT 2005
>Closed-Date:    Sun Mar 25 03:31:25 UTC 2012
>Last-Modified:  Sun Mar 25 03:31:25 UTC 2012
>Originator:     Peter Jeremy
>Release:        FreeBSD 5.3-RELEASE-p5 i386
>Organization:
n/a
>Environment:
System: FreeBSD server.vk2pj.dyndns.org 5.3-RELEASE-p5 FreeBSD 5.3-RELEASE-p5 #5: Sat Mar 5 07:37:49 EST 2005 root@:/home/obj/usr/src/sys/server i386

>Description:
	Older PC BIOSes have various limits on disk sizes - 8GB and 33GB are
	common ones.  Some disk manufacturers provide tricks to get around
	these limits - typically a jumper that restricts capacity to either
	8GB or 33GB and then allows software to recover the capacity by
	issuing ATA READ NATIVE MAX and ATA SET MAX ADDRESS commands.

	The attached patch adds a new boolean boot-time tunable
	"hw.ata.reset_capacity".  If this tunable is true, then the kernel
	will issue an ATA READ NATIVE MAX during the disk attach.  If the
	returned size is greater than the size previously reported by
	the ATA IDENTIFY issued during the probe then an ATA SET MAX ADDRESS
	is issued with the updated size and the ATA IDENTIFY repeated.

	Note that this code does not handle the 48-bit extended command
	version and so only handles disks up to 137GB.  This is because
	neither the existing ATAng nor the new ATAmkIII code includes
	provision for reading the 48-bit LBA result from an extended command.
	By ensuring that the LBA returned by the READ NATIVE result is
	greater than the existing LBA, the code avoids truncating a correctly
	probed large disk down to 137GB.

>How-To-Repeat:
	I did my testing using a Seagate ST380011A disk on a Asus P5A-B
	motherboard.  This combination will hang during the BIOS probe
	unless the disk is jumpered down to 33GB.

	1) With the disk in 'native' mode, demonstrate that the BIOS will
	   hang during the BIOS probe.
	2) Install the "alternate capacity jumper".  Verify that both the
	   BIOS and FreeBSD report a capacity of less than 33GB (66,055,248
	   sectors in my case).
	3) Add the line 'hw.ata.reset_capacity="1"' to /boot/loader.conf and
	   reboot with the below patch installed.
	4) Verify that the BIOS reports a capacity of less than 33GB whilst
	   FreeBSD reports the full disk capacity (156,301,488 sectors for
	   the above disk).
	5) Using dd(1), newfs or fdisk, verify that the full disk capacity
	   is accessible.  [My first fix failed this step]
	6) Verify that the system reboots correctly using reboot(8) or
	   similar (ie resetting the system restores the limited capacity).

>Fix:

	Notes:
	1) The patch to ata-lowlevel.c is only needed on 5.x.  This fix has
	   been implemented in 6.x but not MFC'd.
	2) This patch matches the existing coding style in dev/ata rather than
	   style(9).

Index: dev/ata/ata-all.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/ata/ata-all.c,v
retrieving revision 1.222.2.4.2.1
diff -u -r1.222.2.4.2.1 ata-all.c
--- dev/ata/ata-all.c	24 Oct 2004 09:31:25 -0000	1.222.2.4.2.1
+++ dev/ata/ata-all.c	12 Mar 2005 01:41:56 -0000
@@ -68,7 +68,6 @@
 /* prototypes */
 static void ata_shutdown(void *, int);
 static void ata_interrupt(void *);
-static int ata_getparam(struct ata_device *, u_int8_t);
 static void ata_identify_devices(struct ata_channel *);
 static void ata_boot_attach(void);
 static void bswap(int8_t *, int);
@@ -81,6 +80,7 @@
 devclass_t ata_devclass;
 uma_zone_t ata_zone;
 int ata_wc = 1;
+int ata_capacity = 0;
 
 /* local vars */
 static struct intr_config_hook *ata_delayed_attach = NULL;
@@ -98,6 +98,9 @@
 TUNABLE_INT("hw.ata.atapi_dma", &atapi_dma);
 SYSCTL_INT(_hw_ata, OID_AUTO, atapi_dma, CTLFLAG_RDTUN, &atapi_dma, 0,
 	   "ATAPI device DMA mode control");
+TUNABLE_INT("hw.ata.reset_capacity", &ata_capacity);
+SYSCTL_INT(_hw_ata, OID_AUTO, reset_capacity, CTLFLAG_RDTUN, &ata_capacity, 0,
+	   "ATA disk reset drive capacity");
 
 /*
  * newbus device interface related functions
@@ -638,7 +641,7 @@
 /*
  * device probe functions
  */
-static int
+int
 ata_getparam(struct ata_device *atadev, u_int8_t command)
 {
     struct ata_request *request;
Index: dev/ata/ata-all.h
===================================================================
RCS file: /usr/ncvs/src/sys/dev/ata/ata-all.h,v
retrieving revision 1.81.2.3
diff -u -r1.81.2.3 ata-all.h
--- dev/ata/ata-all.h	10 Oct 2004 15:01:47 -0000	1.81.2.3
+++ dev/ata/ata-all.h	12 Mar 2005 01:41:14 -0000
@@ -387,7 +387,8 @@
 /* externs */
 extern devclass_t ata_devclass;
 extern int ata_wc;
- 
+extern int ata_capacity;
+
 /* public prototypes */
 /* ata-all.c: */
 int ata_probe(device_t dev);
@@ -408,6 +409,7 @@
 int ata_wmode(struct ata_params *ap);
 int ata_umode(struct ata_params *ap);
 int ata_limit_mode(struct ata_device *atadev, int mode, int maxmode);
+int ata_getparam(struct ata_device *atadev, u_int8_t command);
 
 /* ata-queue.c: */
 int ata_reinit(struct ata_channel *ch);
Index: dev/ata/ata-commands.h
===================================================================
RCS file: /usr/ncvs/src/sys/dev/ata/ata-commands.h,v
retrieving revision 1.2
diff -u -r1.2 ata-commands.h
--- dev/ata/ata-commands.h	30 Apr 2004 16:21:34 -0000	1.2
+++ dev/ata/ata-commands.h	12 Mar 2005 23:27:24 -0000
@@ -69,6 +69,9 @@
 #define		ATA_SF_DIS_RELIRQ	0xdd	/* disable release interrupt */
 #define		ATA_SF_ENAB_SRVIRQ	0x5e	/* enable service interrupt */
 #define		ATA_SF_DIS_SRVIRQ	0xde	/* disable service interrupt */
+#define ATA_READ_NATIVE_MAX		0xf8	/* Read native disk size */
+#define ATA_SET_MAX			0xf9	/* Set address range */
+#define		ATA_SM_ADDRESS		0x00	/* maximum address */
 
 /* ATAPI commands */
 #define ATAPI_TEST_UNIT_READY		0x00	/* check if device is ready */
Index: dev/ata/ata-disk.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/ata/ata-disk.c,v
retrieving revision 1.175.2.2
diff -u -r1.175.2.2 ata-disk.c
--- dev/ata/ata-disk.c	30 Sep 2004 21:29:19 -0000	1.175.2.2
+++ dev/ata/ata-disk.c	12 Mar 2005 21:31:26 -0000
@@ -68,6 +68,63 @@
 static MALLOC_DEFINE(M_AD, "AD driver", "ATA disk driver");
 static u_int32_t adp_lun_map = 0;
 
+static void
+ad_reset_capacity(struct ata_device *atadev)
+{
+    struct ata_request *request = ata_alloc_request();
+    u_int32_t	oldlba;
+
+    if (!request)
+	return;
+
+    /* Issue a READ NATIVE MAX */
+    request->device = atadev;
+    request->u.ata.command = ATA_READ_NATIVE_MAX;
+    request->u.ata.lba = 0;
+    request->u.ata.count = 0;
+    request->u.ata.feature = 0;
+    request->flags = ATA_R_CONTROL;
+    request->timeout = 5;
+    request->retries = 0;
+    ata_queue_request(request);
+
+    /* give up if any error */
+    if (request->status & ATA_S_ERROR)
+	goto error;
+
+    oldlba = (u_int32_t)atadev->param->lba_size_1 |
+	     ((u_int32_t)atadev->param->lba_size_2 << 16);
+
+    /* Don't continue unless read max returns a higher LBA than the identify */
+    if (oldlba >= request->u.ata.lba)
+	goto error;
+
+    if (bootverbose)
+	ata_prtdev(atadev, "Initial geometry: %u sectors (%u/%u/%u)\n",
+		   oldlba, atadev->param->cylinders, atadev->param->heads,
+		   atadev->param->sectors);
+
+    /* Reset the drive capacity */
+    request->device = atadev;
+    request->u.ata.command = ATA_SET_MAX;
+    request->u.ata.count = 0;		/* Leave the new max as volatile */
+    request->u.ata.feature = ATA_SM_ADDRESS;
+    request->flags = ATA_R_CONTROL;
+    request->timeout = 5;
+    request->retries = 0;
+    ata_queue_request(request);
+
+    /* give up if any error */
+    if (request->status & ATA_S_ERROR)
+	goto error;
+
+    /* Repeat drive identification to load the updated parameters */
+    ata_getparam(atadev, ATA_ATA_IDENTIFY);
+
+ error:
+    ata_free_request(request);
+}
+
 void
 ad_attach(struct ata_device *atadev)
 {
@@ -89,6 +146,9 @@
     adp->lun = ata_get_lun(&adp_lun_map);
 #endif
     ata_set_name(atadev, "ad", adp->lun);
+    if (ata_capacity && !(atadev->flags & ATA_D_USE_CHS))
+	ad_reset_capacity(atadev);
+
     adp->heads = atadev->param->heads;
     adp->sectors = atadev->param->sectors;
     adp->total_secs = atadev->param->cylinders * adp->heads * adp->sectors;	
Index: dev/ata/ata-lowlevel.c
===================================================================
RCS file: /usr/ncvs/src/sys/dev/ata/ata-lowlevel.c,v
retrieving revision 1.44.2.2
diff -u -r1.44.2.2 ata-lowlevel.c
--- dev/ata/ata-lowlevel.c	30 Sep 2004 21:29:19 -0000	1.44.2.2
+++ dev/ata/ata-lowlevel.c	12 Mar 2005 01:29:27 -0000
@@ -303,7 +303,8 @@
 	    request->u.ata.count = ATA_IDX_INB(ch, ATA_COUNT);
 	    request->u.ata.lba = ATA_IDX_INB(ch, ATA_SECTOR) |
 				 (ATA_IDX_INB(ch, ATA_CYL_LSB) << 8) |
-				 (ATA_IDX_INB(ch, ATA_CYL_MSB) << 16);
+				 (ATA_IDX_INB(ch, ATA_CYL_MSB) << 16) |
+				 ((ATA_IDX_INB(ch, ATA_DRIVE) & 0x0f) << 24);
 	}
 
 	/* if we got an error we are done with the HW */
Index: sys/ata.h
===================================================================
RCS file: /usr/ncvs/src/sys/sys/ata.h,v
retrieving revision 1.23
diff -u -r1.23 ata.h
--- sys/ata.h	20 May 2004 15:09:41 -0000	1.23
+++ sys/ata.h	12 Mar 2005 23:28:05 -0000
@@ -251,6 +251,9 @@
 #define		ATA_SF_DIS_RELIRQ	0xdd	/* disable release interrupt */
 #define		ATA_SF_ENAB_SRVIRQ	0x5e	/* enable service interrupt */
 #define		ATA_SF_DIS_SRVIRQ	0xde	/* disable service interrupt */
+#define ATA_READ_NATIVE_MAX		0xf8	/* Read native disk size */
+#define ATA_SET_MAX			0xf9	/* Set address range */
+#define		ATA_SM_ADDRESS		0x00	/* maximum address */
 
 /* ATAPI commands */
 #define ATAPI_TEST_UNIT_READY		0x00	/* check if device is ready */


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->sos 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Sun Apr 3 08:32:06 GMT 2005 
Responsible-Changed-Why:  
Over to maintainer. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=78758 
Responsible-Changed-From-To: sos->freebsd-bugs 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Tue May 12 04:31:56 UTC 2009 
Responsible-Changed-Why:  
sos@ is not actively working on ATA-related PRs. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=78758 
State-Changed-From-To: open->closed 
State-Changed-By: eadler 
State-Changed-When: Sun Mar 25 03:31:23 UTC 2012 
State-Changed-Why:  
seems to have been fixed a looooong time ago. Sorry for taking so long 
to get to this PR. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=78758 
>Unformatted:
