by Magnus on Tue Nov 10, 2009 8:37 pm
Hi
I've tested another approach now that also works.
Since last week I made a freedos partition on my drive that I runs hdat2 from.
If I do the auto HPA removal in hdat2 and then loads linux from dos it also works. (much simpler than setting password/lock and do a reboot.)
What I've tried within linux:
hdparm -N doesn't work.
$ sudo hdparm -N /dev/sda
/dev/sda:
max sectors = 268435456/488397168, HPA is enabled
$ sudo hdparm -N488397168 /dev/sda
/dev/sda:
setting max visible sectors to 488397168 (temporary)
SET_MAX_ADDRESS failed: Input/output error
max sectors = 268435456/488397168, HPA is enabled
$ sudo hdparm -Np488397168 /dev/sda
/dev/sda:
setting max visible sectors to 488397168 (permanent)
SET_MAX_ADDRESS failed: Input/output error
max sectors = 268435456/488397168, HPA is enabled
I tried to modify hdparm so it would so it would do the same trick as your program.
Aka, setting max lba28 first and then max lba48, but it didn't work either, same "Input/output" error twice instead.
$ sudo hdparm -N488397168 /dev/sda
/dev/sda:
setting max visible sectors to 488397168 (temporary)
SET_MAX_ADDRESS LBA28 failed: Input/output error
SET_MAX_ADDRESS LBA48 failed: Input/output error
max sectors = 268435456/488397168, HPA is enabled
My code mod for hdparm:
(my addition between the ============ lines.)
static int do_set_max_sectors (int fd, __u16 *id, __u64 max_lba, int permanent)
{
int err = 0;
struct hdio_taskfile r;
__u8 nsect = permanent ? 1 : 0;
if ((max_lba >= lba28_limit) || (id && ((id[83] & 0xc400) == 0x4400)
&& (id[86] & 0x0400))) {
/*===============================================================*/
init_hdio_taskfile(&r, ATA_OP_SET_MAX, RW_READ, LBA28_OK,
lba28_limit, nsect, 0);
if (!do_get_native_max_sectors(fd, id))
return errno;
if (do_taskfile_cmd(fd, &r, timeout_12secs)) {
err = errno;
perror(" SET_MAX_ADDRESS LBA28 failed");
}
/*===============================================================*/
init_hdio_taskfile(&r, ATA_OP_SET_MAX_EXT, RW_READ, LBA48_FORCE,
max_lba, nsect, 0);
} else {
init_hdio_taskfile(&r, ATA_OP_SET_MAX, RW_READ, LBA28_OK, max_lba, nsect, 0);
r.oflags.lob.feat = 1; /* this ATA op requires feat==0 */
}
/* spec requires that we do this immediately in front.. racey */
if (!do_get_native_max_sectors(fd, id))
return errno;
/* now set the new value */
if (do_taskfile_cmd(fd, &r, timeout_12secs)) {
err = errno;
perror(" SET_MAX_ADDRESS LBA48 failed");
}
return err;
}
I've tested Setmax also, it's written for just LBA28 tho, but it can't do anything just like hdparm.
I guess Linux can't access or set the registers of the hard-drive when it's booted on it.
The linxus kernel tries to do a HPA removal in the same fashion as hdparm during boot.
From the syslog:
ata1.00: failed to set max address (err_mask=0x1)
ata1.00: device aborted resize (268435456 -> 488397168), skipping HPA handling
ata1.00: ATA-8: WDC WD2500BEVE-00WZT0, 01.01A01, max UDMA/100
ata1.00: 268435456 sectors, multi 16: LBA48
It doesn't matter if I'm using a 32 or 64bit kernel, I've also tried with the latest Gentoo and Ubuntu 9.10. Running on Ubuntu 9.04 atm.
I took a look at the source code for GRUB 0.97, but stage1 seems to be written in assembler so it's too obscure for me to modify.
According the notations in the code, GRUB tries to do a set max operation, but I'm not a programmer so I'm limited to (more or less) just copy/paste stuff.