2006-02-17 17:01:40

by linux-os (Dick Johnson)

[permalink] [raw]
Subject: C/H/S from user space



For those who think that C/H/S translation is useful,
attached is a simple program that gets whatever the BIOS
used from user-space. If you have large media (most do),
you will descover that C/H/S is useless because it is
always set to "MAX" like this:

Disk parameter table(s) at vector 0x41
Disk0
Cylinders = 1024
Sectors = 63
Heads = 255
Write precomp = 0
Landing zone = 65296
Reserved bit 0 set
Reserved bit 1 set
Reserved bit 2 set
More than 8 heads
Reserved bit 4 set
Defect map present
Disable retries
Disable retries
Disk1
Cylinders = 306
Sectors = 1
Heads = 4
Write precomp = 0
Landing zone = 12544
Disk parameter table(s) at vector 0x46
Disk0
Cylinders = 306
Sectors = 1
Heads = 4
Write precomp = 0
Landing zone = 12544
Disk1
Cylinders = 306
Sectors = 1
Heads = 4
Write precomp = 0
Landing zone = 12544


Cheers,
Dick Johnson
Penguin : Linux version 2.6.15.4 on an i686 machine (5589.55 BogoMips).
Warning : 98.36% of all statistics are fiction.
_



****************************************************************
The information transmitted in this message is confidential and may be privileged. Any review, retransmission, dissemination, or other use of this information by persons or entities other than the intended recipient is prohibited. If you are not the intended recipient, please notify Analogic Corporation immediately - by replying to this message or by sending an email to [email protected] - and destroy all copies of this information, including any attachments, without reading or disclosing them.

Thank you.


Attachments:
CHS.tar.gz (4.04 kB)
CHS.tar.gz

2006-02-17 17:40:57

by Jeffrey V. Merkey

[permalink] [raw]
Subject: Re: C/H/S from user space


Dick,

The model Netware uses for large drives for calculating C/H/S addressed
this problem years ago and
provides more useful output, since C/H/S is converted into a hieristic
which acts almost like an MD5 hash
and for a given dimension, does provide unique output for even very
large drives.

Code Attached.

Jeff



ULONG SetPartitionTableGeometry(BYTE *hd, BYTE *sec, BYTE *cyl,
ULONG LBA, ULONG TracksPerCylinder,
ULONG SectorsPerTrack)
{
ULONG offset, cylinders, head, sector;

if (!cyl || !hd || !sec)
return -1;

cylinders = (LBA / (TracksPerCylinder * SectorsPerTrack));
offset = LBA % (TracksPerCylinder * SectorsPerTrack);
head = (WORD)(offset / SectorsPerTrack);
sector = (WORD)(offset % SectorsPerTrack) + 1;

if (cylinders < 1023)
{
*sec = (BYTE)sector;
*hd = (BYTE)head;
*cyl = (BYTE)(cylinders & 0xff);
*sec |= (BYTE)((cylinders >> 2) & 0xC0);
}
else
{
*sec = (BYTE)(SectorsPerTrack | 0xC0);
*hd = (BYTE)(TracksPerCylinder - 1);
*cyl = (BYTE)0xFE;
}

return 0;
}

ULONG SetPartitionTableValues(struct PartitionTableEntry *Part,
ULONG Type,
ULONG StartingLBA,
ULONG EndingLBA,
ULONG Flag,
ULONG TracksPerCylinder,
ULONG SectorsPerTrack)
{

Part->SysFlag = (BYTE) Type;
Part->fBootable = (BYTE) Flag;
Part->StartLBA = StartingLBA;
Part->nSectorsTotal = (EndingLBA - StartingLBA) + 1;

SetPartitionTableGeometry(&Part->HeadStart, &Part->SecStart,
&Part->CylStart,
StartingLBA, TracksPerCylinder, SectorsPerTrack);

SetPartitionTableGeometry(&Part->HeadEnd, &Part->SecEnd, &Part->CylEnd,
EndingLBA, TracksPerCylinder, SectorsPerTrack);

return 0;

}




linux-os (Dick Johnson) wrote:

>For those who think that C/H/S translation is useful,
>attached is a simple program that gets whatever the BIOS
>used from user-space. If you have large media (most do),
>you will descover that C/H/S is useless because it is
>always set to "MAX" like this:
>
> Disk parameter table(s) at vector 0x41
>Disk0
> Cylinders = 1024
> Sectors = 63
> Heads = 255
>Write precomp = 0
> Landing zone = 65296
> Reserved bit 0 set
> Reserved bit 1 set
> Reserved bit 2 set
> More than 8 heads
> Reserved bit 4 set
> Defect map present
> Disable retries
> Disable retries
>Disk1
> Cylinders = 306
> Sectors = 1
> Heads = 4
>Write precomp = 0
> Landing zone = 12544
> Disk parameter table(s) at vector 0x46
>Disk0
> Cylinders = 306
> Sectors = 1
> Heads = 4
>Write precomp = 0
> Landing zone = 12544
>Disk1
> Cylinders = 306
> Sectors = 1
> Heads = 4
>Write precomp = 0
> Landing zone = 12544
>
>
>Cheers,
>Dick Johnson
>Penguin : Linux version 2.6.15.4 on an i686 machine (5589.55 BogoMips).
>Warning : 98.36% of all statistics are fiction.
>_
>
>
>
>****************************************************************
>The information transmitted in this message is confidential and may be privileged. Any review, retransmission, dissemination, or other use of this information by persons or entities other than the intended recipient is prohibited. If you are not the intended recipient, please notify Analogic Corporation immediately - by replying to this message or by sending an email to [email protected] - and destroy all copies of this information, including any attachments, without reading or disclosing them.
>
>Thank you.
>

2006-02-17 20:00:42

by Phillip Susi

[permalink] [raw]
Subject: Re: C/H/S from user space

Other than generate CHS addresses that are invalid due to having S > 62,
what is this code supposed to do?


Jeff V. Merkey wrote:
>
> Dick,
>
> The model Netware uses for large drives for calculating C/H/S addressed
> this problem years ago and
> provides more useful output, since C/H/S is converted into a hieristic
> which acts almost like an MD5 hash
> and for a given dimension, does provide unique output for even very
> large drives.
>
> Code Attached.
>
> Jeff
>
>
>
> ULONG SetPartitionTableGeometry(BYTE *hd, BYTE *sec, BYTE *cyl,
> ULONG LBA, ULONG TracksPerCylinder,
> ULONG SectorsPerTrack)
> {
> ULONG offset, cylinders, head, sector;
>
> if (!cyl || !hd || !sec)
> return -1;
>
> cylinders = (LBA / (TracksPerCylinder * SectorsPerTrack));
> offset = LBA % (TracksPerCylinder * SectorsPerTrack);
> head = (WORD)(offset / SectorsPerTrack);
> sector = (WORD)(offset % SectorsPerTrack) + 1;
>
> if (cylinders < 1023)
> {
> *sec = (BYTE)sector;
> *hd = (BYTE)head;
> *cyl = (BYTE)(cylinders & 0xff);
> *sec |= (BYTE)((cylinders >> 2) & 0xC0);
> }
> else
> {
> *sec = (BYTE)(SectorsPerTrack | 0xC0);
> *hd = (BYTE)(TracksPerCylinder - 1);
> *cyl = (BYTE)0xFE;
> }
>
> return 0;
> }
>
> ULONG SetPartitionTableValues(struct PartitionTableEntry *Part,
> ULONG Type,
> ULONG StartingLBA,
> ULONG EndingLBA,
> ULONG Flag,
> ULONG TracksPerCylinder,
> ULONG SectorsPerTrack)
> {
>
> Part->SysFlag = (BYTE) Type;
> Part->fBootable = (BYTE) Flag;
> Part->StartLBA = StartingLBA;
> Part->nSectorsTotal = (EndingLBA - StartingLBA) + 1;
>
> SetPartitionTableGeometry(&Part->HeadStart, &Part->SecStart,
> &Part->CylStart,
> StartingLBA, TracksPerCylinder, SectorsPerTrack);
>
> SetPartitionTableGeometry(&Part->HeadEnd, &Part->SecEnd, &Part->CylEnd,
> EndingLBA, TracksPerCylinder, SectorsPerTrack);
>
> return 0;
>
> }
>
>

2006-02-17 20:04:42

by linux-os (Dick Johnson)

[permalink] [raw]
Subject: Re: C/H/S from user space


On Fri, 17 Feb 2006, Jeff V. Merkey wrote:

>
> Dick,
>
> The model Netware uses for large drives for calculating C/H/S addressed
> this problem years ago and
> provides more useful output, since C/H/S is converted into a hieristic
> which acts almost like an MD5 hash
> and for a given dimension, does provide unique output for even very
> large drives.
>
> Code Attached.
>
> Jeff
>
>
>
> ULONG SetPartitionTableGeometry(BYTE *hd, BYTE *sec, BYTE *cyl,
> ULONG LBA, ULONG TracksPerCylinder,
> ULONG SectorsPerTrack)
> {
> ULONG offset, cylinders, head, sector;
>
> if (!cyl || !hd || !sec)
> return -1;
>
> cylinders = (LBA / (TracksPerCylinder * SectorsPerTrack));
> offset = LBA % (TracksPerCylinder * SectorsPerTrack);
> head = (WORD)(offset / SectorsPerTrack);
> sector = (WORD)(offset % SectorsPerTrack) + 1;
>
> if (cylinders < 1023)
> {
> *sec = (BYTE)sector;
> *hd = (BYTE)head;
> *cyl = (BYTE)(cylinders & 0xff);
> *sec |= (BYTE)((cylinders >> 2) & 0xC0);
> }
> else
> {
> *sec = (BYTE)(SectorsPerTrack | 0xC0);
> *hd = (BYTE)(TracksPerCylinder - 1);
> *cyl = (BYTE)0xFE;
> }
>
> return 0;
> }
>
> ULONG SetPartitionTableValues(struct PartitionTableEntry *Part,
> ULONG Type,
> ULONG StartingLBA,
> ULONG EndingLBA,
> ULONG Flag,
> ULONG TracksPerCylinder,
> ULONG SectorsPerTrack)
> {
>
> Part->SysFlag = (BYTE) Type;
> Part->fBootable = (BYTE) Flag;
> Part->StartLBA = StartingLBA;
> Part->nSectorsTotal = (EndingLBA - StartingLBA) + 1;
>
> SetPartitionTableGeometry(&Part->HeadStart, &Part->SecStart,
> &Part->CylStart,
> StartingLBA, TracksPerCylinder, SectorsPerTrack);
>
> SetPartitionTableGeometry(&Part->HeadEnd, &Part->SecEnd, &Part->CylEnd,
> EndingLBA, TracksPerCylinder, SectorsPerTrack);
>
> return 0;
>
> }
>
>
>
[SNIPPED...]

Yes, it's a very good model, in fact what I've been talking about.
However, several people who refused to read or understand, insisted
upon obtaining the exact same C/H/S that the machine claimed to
use when it was booted.

So, since Linux doesn't destroy that information remaining in
the BIOS tables, I show how to make it available to a 'root' user.
Observation over several machines will show that the BIOS always
uses the same stuff for large media and, in fact, it has no choice.
Basically, this means that the first part of the boot-code, the
stuff that needs to be translated to fit into the int 0x13 registers,
needs to be below 1024 cylinders, 63 sectors-track, and 256 heads.
Trivial... even LILO was able to do that! Once the machine boots
past the requirement to use the BIOS services, it's a CHS=NOP.

Cheers,
Dick Johnson
Penguin : Linux version 2.6.15.4 on an i686 machine (5589.53 BogoMips).
Warning : 98.36% of all statistics are fiction.
_


****************************************************************
The information transmitted in this message is confidential and may be privileged. Any review, retransmission, dissemination, or other use of this information by persons or entities other than the intended recipient is prohibited. If you are not the intended recipient, please notify Analogic Corporation immediately - by replying to this message or by sending an email to [email protected] - and destroy all copies of this information, including any attachments, without reading or disclosing them.

Thank you.

2006-02-17 20:24:26

by Nick Warne

[permalink] [raw]
Subject: Re: C/H/S from user space

> So, since Linux doesn't destroy that information remaining in
> the BIOS tables, I show how to make it available to a 'root' user.
> Observation over several machines will show that the BIOS always
> uses the same stuff for large media and, in fact, it has no choice.
> Basically, this means that the first part of the boot-code, the
> stuff that needs to be translated to fit into the int 0x13 registers,
> needs to be below 1024 cylinders, 63 sectors-track, and 256 heads.
> Trivial... even LILO was able to do that! Once the machine boots
> past the requirement to use the BIOS services, it's a CHS=NOP.


If I am off the mark here, forgive me.

Since I moved exclusively to GNU/Linux 2 years ago, I notice when I
update kernel I get this:

nick@linuxamd:nick$ sudo /sbin/lilo -v
LILO version 22.5.9, Copyright (C) 1992-1998 Werner Almesberger
Development beyond version 21 Copyright (C) 1999-2004 John Coffman
Released 08-Apr-2004 and compiled at 00:18:50 on May 21 2004.

Warning: LBA32 addressing assumed
Reading boot sector from /dev/hda2
Warning: Kernel & BIOS return differing head/sector geometries for device 0x80
Kernel: 65535 cylinders, 16 heads, 63 sectors
BIOS: 1024 cylinders, 255 heads, 63 sectors
Warning: Kernel & BIOS return differing head/sector geometries for device 0x81
Kernel: 29777 cylinders, 16 heads, 63 sectors
BIOS: 1024 cylinders, 255 heads, 63 sectors

Now, from day one I never used the -v option with lilo, but as I get
more experienced (!) I do now and see the above... I have never
investigated due to worrying if I start messing with it I will trash
my disks - as I see all anyway on this disks (and no errors), all
works great/fast etc.

Is this what is going on here (re this thread?).

Nick

2006-02-17 20:30:15

by Seewer Philippe

[permalink] [raw]
Subject: Re: C/H/S from user space


<snip>

wow this is really great... but just for your information
on my ibm notebook the output is this:

Disk parameter table(s) at vector 0x41
Disk0
Cylinders = 1024
Sectors = 63
Heads = 240
Write precomp = 255
Landing zone = 65296
Reserved bit 0 set
Reserved bit 1 set
Reserved bit 2 set
More than 8 heads
Reserved bit 4 set
Defect map present
Disable retries
Disable retries
Disk1
Cylinders = 1024
Sectors = 4
Heads = 16
Write precomp = 0
Landing zone = 0
Disk parameter table(s) at vector 0x46
Disk0
Cylinders = 34281
Sectors = 240
Heads = 230
Write precomp = 7950
Landing zone = 61
Reserved bit 2 set
More than 8 heads
Disable retries
Disk1
Cylinders = 47631
Sectors = 102
Heads = 46
Write precomp = 3698
Landing zone = 7952
More than 8 heads
Defect map present
Disable retries


Just Note the "MAX" of 240 heads (very typical for IBM notebooks).

But anyway this tool is very useful for me. many, many thanks.


2006-02-17 20:39:32

by linux-os (Dick Johnson)

[permalink] [raw]
Subject: Re: C/H/S from user space


On Fri, 17 Feb 2006, Nick Warne wrote:

> > So, since Linux doesn't destroy that information remaining in
>> the BIOS tables, I show how to make it available to a 'root' user.
>> Observation over several machines will show that the BIOS always
>> uses the same stuff for large media and, in fact, it has no choice.
>> Basically, this means that the first part of the boot-code, the
>> stuff that needs to be translated to fit into the int 0x13 registers,
>> needs to be below 1024 cylinders, 63 sectors-track, and 256 heads.
>> Trivial... even LILO was able to do that! Once the machine boots
>> past the requirement to use the BIOS services, it's a CHS=NOP.
>
>
> If I am off the mark here, forgive me.
>
> Since I moved exclusively to GNU/Linux 2 years ago, I notice when I
> update kernel I get this:
>
> nick@linuxamd:nick$ sudo /sbin/lilo -v
> LILO version 22.5.9, Copyright (C) 1992-1998 Werner Almesberger
> Development beyond version 21 Copyright (C) 1999-2004 John Coffman
> Released 08-Apr-2004 and compiled at 00:18:50 on May 21 2004.
>
> Warning: LBA32 addressing assumed
> Reading boot sector from /dev/hda2
> Warning: Kernel & BIOS return differing head/sector geometries for device 0x80
> Kernel: 65535 cylinders, 16 heads, 63 sectors
> BIOS: 1024 cylinders, 255 heads, 63 sectors
> Warning: Kernel & BIOS return differing head/sector geometries for device 0x81
> Kernel: 29777 cylinders, 16 heads, 63 sectors
> BIOS: 1024 cylinders, 255 heads, 63 sectors
>
> Now, from day one I never used the -v option with lilo, but as I get
> more experienced (!) I do now and see the above... I have never
> investigated due to worrying if I start messing with it I will trash
> my disks - as I see all anyway on this disks (and no errors), all
> works great/fast etc.
>
> Is this what is going on here (re this thread?).
>
> Nick
>

Nothing to worry about. If you make lots of new kernels to
try them out, you might wish to use GRUB instead of LILO.

However, once the boot-process gets out of 16-bit mode, it
isn't going to use the 16-bit disk services so it doesn't
care about any of that stuff. C/H/S is just a "key" to get
you through the fact that the 16-bit BIOS puts some minimal
stuff in registers to access the disk.

Cheers,
Dick Johnson
Penguin : Linux version 2.6.15.4 on an i686 machine (5589.53 BogoMips).
Warning : 98.36% of all statistics are fiction.
_


****************************************************************
The information transmitted in this message is confidential and may be privileged. Any review, retransmission, dissemination, or other use of this information by persons or entities other than the intended recipient is prohibited. If you are not the intended recipient, please notify Analogic Corporation immediately - by replying to this message or by sending an email to [email protected] - and destroy all copies of this information, including any attachments, without reading or disclosing them.

Thank you.

2006-02-17 21:05:31

by Phillip Susi

[permalink] [raw]
Subject: Re: C/H/S from user space

linux-os (Dick Johnson) wrote:
>
> Yes, it's a very good model, in fact what I've been talking about.
> However, several people who refused to read or understand, insisted
> upon obtaining the exact same C/H/S that the machine claimed to
> use when it was booted.
>

That's because if you don't use the same geometry that the bios reports
when calculating the CHS addresses of the sectors you intend to load,
you won't be requesting the right sectors from int 13.

> So, since Linux doesn't destroy that information remaining in
> the BIOS tables, I show how to make it available to a 'root' user.
> Observation over several machines will show that the BIOS always
> uses the same stuff for large media and, in fact, it has no choice.
> Basically, this means that the first part of the boot-code, the
> stuff that needs to be translated to fit into the int 0x13 registers,
> needs to be below 1024 cylinders, 63 sectors-track, and 256 heads.
> Trivial... even LILO was able to do that! Once the machine boots
> past the requirement to use the BIOS services, it's a CHS=NOP.
>

Generally yes, modern large disks will get clamped at 1024 cylinders,
255 heads, and 63 sectors. You seem to be arguing that this will always
be the case. If that is so, then the kernel doesn't need to store these
values since it is known a priori does it? But it isn't always going to
be 255/63, there are some bioses ( I forget which ) that cap the number
of heads at 240, and disks that are smaller than 8 gigs also will have
less than 255 heads.


2006-02-17 21:22:36

by linux-os (Dick Johnson)

[permalink] [raw]
Subject: Re: C/H/S from user space


On Fri, 17 Feb 2006, Phillip Susi wrote:

> linux-os (Dick Johnson) wrote:
>>
>> Yes, it's a very good model, in fact what I've been talking about.
>> However, several people who refused to read or understand, insisted
>> upon obtaining the exact same C/H/S that the machine claimed to
>> use when it was booted.
>>
>
> That's because if you don't use the same geometry that the bios reports
> when calculating the CHS addresses of the sectors you intend to load,
> you won't be requesting the right sectors from int 13.
^^^____

Who is YOU??? I would certainly be requesting the right sectors
if I (or anybody else who knows what they are doing), wrote
the boot code. The boot loader knows about OFFSETS into the
device where it's going to get its data, which eventually
becomes a whole operating system. It doesn't give a *uck about
anything else. There is a table of OFFSETS, obtained from
the file-system, of the correct pieces of files (since there
will not be a file-system until the machine is booted). This
table of offsets needs to be read somewhere in the first 63
sectors (32256 bytes). These offsets contain the junk to
be loaded into memory.

The boot-code, the code that executes in the 16-bit environment,
converts those offsets (after getting data from the DPB) into
the respective junk to put into the registers as I explained
over and over and over again.

You refuse to learn. Please go away.

[SNIPPED...]

Cheers,
Dick Johnson
Penguin : Linux version 2.6.15.4 on an i686 machine (5589.53 BogoMips).
Warning : 98.36% of all statistics are fiction.
_


****************************************************************
The information transmitted in this message is confidential and may be privileged. Any review, retransmission, dissemination, or other use of this information by persons or entities other than the intended recipient is prohibited. If you are not the intended recipient, please notify Analogic Corporation immediately - by replying to this message or by sending an email to [email protected] - and destroy all copies of this information, including any attachments, without reading or disclosing them.

Thank you.

2006-02-17 21:26:08

by Jeffrey V. Merkey

[permalink] [raw]
Subject: Re: C/H/S from user space


Here's the code to calculate that as well -- from NetWare -- with int 13
IOCTL calls and from within Windows 98. This code builds on the djgcc DOS
version og GNU tools.

Jeff

#if (DOS_UTIL)

#define READ_DEVICE 0x02
#define WRITE_DEVICE 0x03
#define NO_VERIFY 0x00
#define WRITE_VERIFY 0x01

// convert cylinder/head/sector addressing to LBA

ULONG CHStoLBA(ULONG disk, LONGLONG cyl, ULONG head, ULONG sector)
{
register ULONG lba;
ULONG heads = SystemDisk[disk]->TracksPerCylinder;
ULONG sectors = SystemDisk[disk]->SectorsPerTrack;

lba = cyl * (heads * sectors);
lba += (head * sectors);
lba += sector;
lba -= 1; // CHS is 1 relative so adjust LBA by subtracting 1

return lba;

}

// convert LBA to cylinder/head/sector addressing

ULONG LBAtoCHS(ULONG disk, ULONG lba, LONGLONG *cyl, ULONG *head,
ULONG *sector)
{
ULONG offset;
ULONG heads = SystemDisk[disk]->TracksPerCylinder;
ULONG sectors = SystemDisk[disk]->SectorsPerTrack;

if (!cyl || !head || !sector)
return -1;

*cyl = (lba / (heads * sectors));
offset = lba % (heads * sectors);
*head = (WORD)(offset / sectors);
*sector = (WORD)(offset % sectors) + 1; // sector addressing is
// 1 relative so add 1
return 0;
}

ULONG pReadDiskSectors(ULONG disk, ULONG StartingLBA, BYTE *Sector,
ULONG sectors, ULONG readAhead)
{
register ULONG bps, ccode, bytes = 0;
register NWDISK *NWDisk;
LONGLONG cylinder;
ULONG head, sector;
static _go32_dpmi_registers r;
#if (WINDOWS_98_UTIL)
static union REGS rl;
#endif

NWFSSet(&r, 0, sizeof(_go32_dpmi_registers));

NWDisk = SystemDisk[disk];
bps = NWDisk->BytesPerSector;

if ((sectors * bps) > IO_BLOCK_SIZE)
return bytes;

#if (WINDOWS_98_UTIL)
rl.x.ax = 0x440D; // generic IOCTL
rl.h.bh = 3; // locks levels 0, 1, 2 or 3
rl.h.bl = (0x80 | (disk & 0x7F)); // dos limit is 128 drives
rl.h.ch = 0x08; // device category
rl.h.cl = 0x4B; // lock physical volume
rl.x.dx = 0; // permissions
int86(0x21, &rl, &rl);

#endif

if (NWDisk->Int13Extensions)
{
EXT_REQUEST request;

NWFSSet(&request, 0, sizeof(EXT_REQUEST));

request.Size = sizeof(EXT_REQUEST);
request.Blocks = sectors;
request.TransferBuffer = (ULONG)(NWDisk->DataSegment << 16) &
0xFFFF0000;
request.LBA = StartingLBA;

movedata(_my_ds(),
(unsigned)&request,
NWDisk->RequestSelector,
0,
sizeof(EXT_REQUEST));

r.h.ah = 0x42;
r.h.dl = (0x80 | (disk & 0x7F));
r.x.ds = NWDisk->RequestSegment;
r.x.si = 0;
r.x.ss = r.x.sp = r.x.flags = 0;

ccode = _go32_dpmi_simulate_int(0x13, &r);
if (ccode)
goto ReadExit;

// error if carry flag is set
if (r.x.flags & 1)
{
// if the drive reported an ECC occurred, return success
if (r.h.ah == 0x11)
{
movedata(NWDisk->DataSelector,
0,
_my_ds(),
(unsigned)Sector,
(sectors * bps));

bytes = (sectors * bps);
goto ReadExit;
}
else
goto ReadExit;
}

movedata(NWDisk->DataSelector,
0,
_my_ds(),
(unsigned)Sector,
(sectors * bps));

bytes = (sectors * bps);
goto ReadExit;
}
else
{
ccode = LBAtoCHS(disk, StartingLBA, &cylinder, &head, &sector);
if (ccode)
goto ReadExit;

r.h.dh = (BYTE)head;
r.h.dl = (0x80 | (disk & 0x7F));
r.h.ch = (BYTE)(cylinder & 0xFF);
r.h.cl = (BYTE)((cylinder & 0x0300) >> 2);
r.h.cl |= (BYTE)(sector & 0x3F);
r.h.ah = READ_DEVICE;
r.h.al = (BYTE)sectors;
r.x.es = NWDisk->DataSegment;
r.x.bx = 0;
r.x.ss = r.x.sp = r.x.flags = 0;

ccode = _go32_dpmi_simulate_int(0x13, &r);
if (ccode)
goto ReadExit;

// error if carry flag is set
if (r.x.flags & 1)
{
// if the drive reported an ECC occurred, return success
if (r.h.ah == 0x11)
{
movedata(NWDisk->DataSelector,
0,
_my_ds(),
(unsigned)Sector,
(sectors * bps));

bytes = (sectors * bps);
goto ReadExit;
}
else
goto ReadExit;
}

movedata(NWDisk->DataSelector,
0,
_my_ds(),
(unsigned)Sector,
(sectors * bps));

bytes = (sectors * bps);
}

ReadExit:;
#if (WINDOWS_98_UTIL)
rl.x.ax = 0x440D; // generic IOCTL
rl.h.bl = (0x80 | (disk & 0x7F)); // dos limit is 128 drives
rl.h.ch = 0x08; // device category
rl.h.cl = 0x6B; // unlock physical volume
int86(0x21, &rl, &rl);
#endif

return bytes;

}


ULONG pWriteDiskSectors(ULONG disk, ULONG StartingLBA, BYTE *Sector,
ULONG sectors, ULONG readAhead)
{
register ULONG bps, ccode, bytes = 0;
register NWDISK *NWDisk;
LONGLONG cylinder;
ULONG head, sector;
static _go32_dpmi_registers r;
#if (WINDOWS_98_UTIL)
static union REGS rl;
#endif

NWFSSet(&r, 0, sizeof(_go32_dpmi_registers));

NWDisk = SystemDisk[disk];
bps = NWDisk->BytesPerSector;

if ((sectors * bps) > IO_BLOCK_SIZE)
return bytes;

#if (WINDOWS_98_UTIL)
rl.x.ax = 0x440D; // generic IOCTL
rl.h.bh = 3; // locks levels 0, 1, 2 or 3
rl.h.bl = (0x80 | (disk & 0x7F)); // dos limit is 128 drives
rl.h.ch = 0x08; // device category
rl.h.cl = 0x4B; // lock physical volume
rl.x.dx = 0; // permissions
int86(0x21, &rl, &rl);

#endif

if (NWDisk->Int13Extensions)
{
EXT_REQUEST request;

NWFSSet(&request, 0, sizeof(EXT_REQUEST));

request.Size = sizeof(EXT_REQUEST);
request.Blocks = sectors;
request.TransferBuffer = (ULONG)(NWDisk->DataSegment << 16) &
0xFFFF0000;
request.LBA = StartingLBA;

movedata(_my_ds(),
(unsigned)&request,
NWDisk->RequestSelector,
0,
sizeof(EXT_REQUEST));

movedata(_my_ds(),
(unsigned)Sector,
NWDisk->DataSelector,
0,
(sectors * bps));

r.h.ah = 0x43;
r.h.al = NO_VERIFY;
r.h.dl = (0x80 | (disk & 0x7F));
r.x.ds = NWDisk->RequestSegment;
r.x.si = 0;
r.x.ss = r.x.sp = r.x.flags = 0;

ccode = _go32_dpmi_simulate_int(0x13, &r);
if (ccode)
goto WriteExit;

// error if carry flag is set
if (r.x.flags & 1)
{
// if the drive reported an ECC occurred, return success
if (r.h.ah == 0x11)
{
bytes = (sectors * bps);
goto WriteExit;
}
else
goto WriteExit;
}
bytes = (sectors * bps);
goto WriteExit;
}
else
{
ccode = LBAtoCHS(disk, StartingLBA, &cylinder, &head, &sector);
if (ccode)
goto WriteExit;

movedata(_my_ds(),
(unsigned)Sector,
NWDisk->DataSelector,
0,
(sectors * bps));

r.h.dh = (BYTE)head;
r.h.dl = (0x80 | (disk & 0x7F));
r.h.ch = (BYTE)(cylinder & 0xFF);
r.h.cl = (BYTE)((cylinder & 0x0300) >> 2);
r.h.cl |= (BYTE)(sector & 0x3F);
r.h.ah = WRITE_DEVICE;
r.h.al = (BYTE)sectors;
r.x.es = NWDisk->DataSegment;
r.x.bx = 0;
r.x.ds = r.x.ss = r.x.sp = r.x.flags = 0;

ccode = _go32_dpmi_simulate_int(0x13, &r);
if (ccode)
goto WriteExit;

// error if carry flag is set
if (r.x.flags & 1)
{
// if the drive reported an ECC occurred, return success
if (r.h.ah == 0x11)
{
bytes = (sectors * bps);
goto WriteExit;
}
else
goto WriteExit;
}
bytes = (sectors * bps);
goto WriteExit;
}

WriteExit:;
#if (WINDOWS_98_UTIL)
rl.x.ax = 0x440D; // generic IOCTL
rl.h.bl = (0x80 | (disk & 0x7F)); // dos limit is 128 drives
rl.h.ch = 0x08; // device category
rl.h.cl = 0x6B; // unlock physical volume
int86(0x21, &rl, &rl);
#endif

return bytes;
}

ULONG pZeroFillDiskSectors(ULONG disk, ULONG StartingLBA, ULONG sectors,
ULONG readAhead)
{
register ULONG bps, ccode, bytes = 0;
register NWDISK *NWDisk;
LONGLONG cylinder;
ULONG head, sector;
static _go32_dpmi_registers r;
#if (WINDOWS_98_UTIL)
static union REGS rl;
#endif

NWFSSet(&r, 0, sizeof(_go32_dpmi_registers));

NWDisk = SystemDisk[disk];
bps = NWDisk->BytesPerSector;

if ((sectors * bps) > IO_BLOCK_SIZE)
return bytes;

movedata(_my_ds(),
(unsigned)ZeroBuffer,
NWDisk->DataSelector,
0,
(sectors * bps));

#if (WINDOWS_98_UTIL)
rl.x.ax = 0x440D; // generic IOCTL
rl.h.bh = 3; // locks levels 0, 1, 2 or 3
rl.h.bl = (0x80 | (disk & 0x7F)); // dos limit is 128 drives
rl.h.ch = 0x08; // device category
rl.h.cl = 0x4B; // lock physical volume
rl.x.dx = 0; // permissions
int86(0x21, &rl, &rl);

#endif

if (NWDisk->Int13Extensions)
{
EXT_REQUEST request;

NWFSSet(&request, 0, sizeof(EXT_REQUEST));

request.Size = sizeof(EXT_REQUEST);
request.Blocks = sectors;
request.TransferBuffer = (ULONG)(NWDisk->DataSegment << 16) &
0xFFFF0000;
request.LBA = StartingLBA;

movedata(_my_ds(),
(unsigned)&request,
NWDisk->RequestSelector,
0,
sizeof(EXT_REQUEST));

r.h.ah = 0x43;
r.h.al = NO_VERIFY;
r.h.dl = (0x80 | (disk & 0x7F));
r.x.ds = NWDisk->RequestSegment;
r.x.si = 0;
r.x.ss = r.x.sp = r.x.flags = 0;

ccode = _go32_dpmi_simulate_int(0x13, &r);
if (ccode)
goto FillExit;

// error if carry flag is set
if (r.x.flags & 1)
{
// if the drive reported an ECC occurred, return success
if (r.h.ah == 0x11)
{
bytes = (sectors * bps);
goto FillExit;
}
else
goto FillExit;
}
bytes = (sectors * bps);
goto FillExit;
}
else
{
ccode = LBAtoCHS(disk, StartingLBA, &cylinder, &head, &sector);
if (ccode)
goto FillExit;

r.h.dh = (BYTE)head;
r.h.dl = (0x80 | (disk & 0x7F));
r.h.ch = (BYTE)(cylinder & 0xFF);
r.h.cl = (BYTE)((cylinder & 0x0300) >> 2);
r.h.cl |= (BYTE)(sector & 0x3F);
r.h.ah = 0x03;
r.h.al = (BYTE)sectors;
r.x.es = NWDisk->DataSegment;
r.x.bx = 0;
r.x.ds = r.x.ss = r.x.sp = r.x.flags = 0;

ccode = _go32_dpmi_simulate_int(0x13, &r);
if (ccode)
goto FillExit;

// error if carry flag is set
if (r.x.flags & 1)
{
// if the drive reported an ECC occurred, return success
if (r.h.ah == 0x11)
{
bytes = (sectors * bps);
goto FillExit;
}
else
goto FillExit;
}
bytes = (sectors * bps);
goto FillExit;
}

FillExit:;
#if (WINDOWS_98_UTIL)
rl.x.ax = 0x440D; // generic IOCTL
rl.h.bl = (0x80 | (disk & 0x7F)); // dos limit is 128 drives
rl.h.ch = 0x08; // device category
rl.h.cl = 0x6B; // unlock physical volume
int86(0x21, &rl, &rl);
#endif

return bytes;

}

void ScanDiskDevices(void)
{
register ULONG j, i, retCode;
BYTE *Sector;
static union REGS r;
DRIVE_INFO dinfo;

Sector = NWFSIOAlloc(IO_BLOCK_SIZE, DISKBUF_TAG);
if (!Sector)
{
NWFSPrint("nwfs: allocation error in AddDiskDevices\n");
return;
}

for (FirstValidDisk = (ULONG)-1, j = 0; j < MAX_DOS_DISKS; j++)
{

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bh = 1; // locks levels 0, 1, 2 or 3
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x4B; // lock physical volume
r.x.dx = 1; // permissions
r.h.cflag = 0x0001;

int86(0x21, &r, &r);
#endif

// test fixed disk status
r.h.ah = 0x10; // test drive status
r.h.dl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
int86(0x13, &r, &r);

// if we got the drive status, and the drive is active,
// assume the disk is valid.
if ((!r.h.cflag) && (!r.h.ah))
{
// now check if this device supports removable media. This
// would indicate this device is a CDROM drive. If we detect
// that this device supports removable meida, then do not
// add it -- it's most likely a CDROM or tape device and
// we should not add it.

// see if a change line is available for this device.
r.h.ah = 0x15; // test drive status
r.h.dl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
int86(0x13, &r, &r);

// 00-no drive, 01-diskette, 02-change line, 03-fixed disk
if (r.h.ch == 0x02)
{

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
continue;
}

if (!SystemDisk[j])
{
SystemDisk[j] = (NWDISK *) NWFSAlloc(sizeof(NWDISK), NWDISK_TAG);
if (!SystemDisk[j])
{
NWFSPrint("nwfs: memory allocation failure in AddDiskDevices\n");

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
continue;
}
NWFSSet(SystemDisk[j], 0, sizeof(NWDISK));
}

SystemDisk[j]->PhysicalDiskHandle = (void *)(0x80 | (j & 0x7F));
SystemDisk[j]->DiskNumber = j;
SystemDisk[j]->Int13Extensions = Int13ExtensionsPresent(j);

SystemDisk[j]->DataSegment =
__dpmi_allocate_dos_memory(PARAGRAPH_ALIGN(IO_BLOCK_SIZE),
&SystemDisk[j]->DataSelector);
if (SystemDisk[j]->DataSegment == (WORD) -1)
{
SystemDisk[j]->PhysicalDiskHandle = 0;
NWFSFree(SystemDisk[j]);
SystemDisk[j] = 0;

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
continue;
}

SystemDisk[j]->DriveInfoSegment =
__dpmi_allocate_dos_memory(PARAGRAPH_ALIGN(IO_BLOCK_SIZE),
&SystemDisk[j]->DriveInfoSelector);
if (SystemDisk[j]->DriveInfoSegment == (WORD) -1)
{
if (SystemDisk[j]->DataSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DataSelector);
SystemDisk[j]->DataSelector = 0;

SystemDisk[j]->PhysicalDiskHandle = 0;
NWFSFree(SystemDisk[j]);
SystemDisk[j] = 0;

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
continue;
}

SystemDisk[j]->RequestSegment =
__dpmi_allocate_dos_memory(PARAGRAPH_ALIGN(sizeof(EXT_REQUEST)),
&SystemDisk[j]->RequestSelector);
if (SystemDisk[j]->RequestSegment == (WORD) -1)
{
if (SystemDisk[j]->DataSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DataSelector);
SystemDisk[j]->DataSelector = 0;

if (SystemDisk[j]->DriveInfoSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DriveInfoSelector);
SystemDisk[j]->DriveInfoSelector = 0;

SystemDisk[j]->PhysicalDiskHandle = 0;
NWFSFree(SystemDisk[j]);
SystemDisk[j] = 0;

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
continue;
}

// if int 13 extensions are present, then adjust drive geometry
// based on reported drive info.

if (SystemDisk[j]->Int13Extensions)
{
register ULONG cylinders, heads, sectors;
_go32_dpmi_registers r32;

NWFSSet(&r32, 0, sizeof(_go32_dpmi_registers));
NWFSSet(&dinfo, 0, sizeof(DRIVE_INFO));

dinfo.Size = sizeof(DRIVE_INFO);

movedata(_my_ds(), (unsigned)&dinfo,
SystemDisk[j]->DriveInfoSelector, 0,
sizeof(DRIVE_INFO));

// get int 13 extension drive info
r32.h.ah = 0x48;
r32.h.dl = (0x80 | (j & 0x7F));
r32.x.si = 0;
r32.x.ds = SystemDisk[j]->DriveInfoSegment;

_go32_dpmi_simulate_int(0x13, &r32);

movedata(SystemDisk[j]->DriveInfoSelector, 0,
_my_ds(), (unsigned)&dinfo,
sizeof(DRIVE_INFO));

// if TotalSectors is 0, then there are no drives
// attached to this controller.

if (!dinfo.TotalSectors)
{
if (SystemDisk[j]->RequestSelector)
__dpmi_free_dos_memory(SystemDisk[j]->RequestSelector);
SystemDisk[j]->RequestSelector = 0;

if (SystemDisk[j]->DataSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DataSelector);
SystemDisk[j]->DataSelector = 0;

if (SystemDisk[j]->DriveInfoSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DriveInfoSelector);
SystemDisk[j]->DriveInfoSelector = 0;

SystemDisk[j]->PhysicalDiskHandle = 0;
NWFSFree(SystemDisk[j]);
SystemDisk[j] = 0;

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
continue;
}

r.h.ah = 0x08; // read drive parameters
r.h.dl = (0x80 | (j & 0x7F));

int86(0x13, &r, &r);

if (r.h.cflag)
{
if (SystemDisk[j]->RequestSelector)
__dpmi_free_dos_memory(SystemDisk[j]->RequestSelector);
SystemDisk[j]->RequestSelector = 0;

if (SystemDisk[j]->DataSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DataSelector);
SystemDisk[j]->DataSelector = 0;

if (SystemDisk[j]->DriveInfoSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DriveInfoSelector);
SystemDisk[j]->DriveInfoSelector = 0;

SystemDisk[j]->PhysicalDiskHandle = 0;
NWFSFree(SystemDisk[j]);
SystemDisk[j] = 0;

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
continue;
}

// translate drive geometry based on the total sectors
// reported by the Int 13 extension procedure.

heads = r.h.dh + 1;
sectors = ((WORD)r.h.cl & 0x003F);
cylinders = (dinfo.TotalSectors / (heads * sectors));

// make sure that cylinders are within the maximum value
// allowed by the Int 13 Extended Architecture. At present,
// this limit is 16 mega-tera sectors (2^64).

// assume the current IDE limits for cylinder count
if (cylinders > (ULONG) 65536)
{
if (SystemDisk[j]->RequestSelector)
__dpmi_free_dos_memory(SystemDisk[j]->RequestSelector);
SystemDisk[j]->RequestSelector = 0;

if (SystemDisk[j]->DataSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DataSelector);
SystemDisk[j]->DataSelector = 0;

if (SystemDisk[j]->DriveInfoSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DriveInfoSelector);
SystemDisk[j]->DriveInfoSelector = 0;

SystemDisk[j]->PhysicalDiskHandle = 0;
NWFSFree(SystemDisk[j]);
SystemDisk[j] = 0;

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
continue;
}

// Use the Phoenix method of drive translation
// to translate drive geometry for those drives
// that exceed 1024 cylinders in size.
//
// Phoenix Geometry Translation Table
// -----------------------------------
//
// Phys Cylinders Phys Heads Trans Cyl Trans Heads Max
// --------------------------------------------------------
// 1 <C<= 1024 1 <H<= 16 C = C H = H 528 MB
// 1024 <C<= 2048 1 <H<= 16 C = C/2 H = H*2 1.0 GB
// 2048 <C<= 4096 1 <H<= 16 C = C/4 H = H*4 2.1 GB
// 4096 <C<= 8192 1 <H<= 16 C = C/8 H = H*8 4.2 GB
// 8192 <C<= 16384 1 <H<= 16 C = C/16 H = H*16 8.4 GB
// 16384 <C<= 32768 1 <H<= 8 C = C/32 H = H*32 8.4 GB
// 32768 <C<= 65536 1 <H<= 4 C = C/64 H = H*64 8.4 GB
//
// LBA Assisted Translation Table
// ------------------------------
//
// (NOTE: The method below is an alternate method for
// translating large drives that does not place any limits
// on reported drive geometry. It has the disadvantage
// of always assuming 63 Sectors Per Track.)
//
// Range Sectors Heads Cylinders
// -----------------------------------------------------
// 1 MB <X< 528 MB 63 16 X/(63 * 16 * 512)
// 528 MB <X< 1.0 GB 63 32 X/(63 * 32 * 512)
// 1.0 GB <X< 2.1 GB 63 64 X/(63 * 64 * 512)
// 2.1 GB <X< 4.2 GB 63 128 X/(63 * 128 * 512)
// 4.2 GB <X< 8.4 GB 63 255 X/(63 * 255 * 512)
//

// Adjust cylinder and head dimensions until this drive
// presents a geometry with a cylinder count that is less
// than 1024 or Heads less than or equal to 255.

if (cylinders >= 1024)
{
while ((heads * 2) <= 256)
{
heads *= 2;
cylinders /= 2;
if (cylinders < 1024)
break;
}
}

if (FirstValidDisk == (ULONG)-1)
FirstValidDisk = j;

SystemDisk[j]->BytesPerSector = 512;
SystemDisk[j]->Cylinders = (LONGLONG) cylinders;
SystemDisk[j]->TracksPerCylinder = heads;
SystemDisk[j]->SectorsPerTrack = sectors;
SystemDisk[j]->driveSize = (LONGLONG)
(SystemDisk[j]->Cylinders *
SystemDisk[j]->TracksPerCylinder *
SystemDisk[j]->SectorsPerTrack *
SystemDisk[j]->BytesPerSector);
#if (VERBOSE)
NWFSPrint("disk-%d cyl-%d head-%d sector-%d (Int 13)\n",
(int)j,
(int)SystemDisk[j]->Cylinders,
(int)SystemDisk[j]->TracksPerCylinder,
(int)SystemDisk[j]->SectorsPerTrack);
#endif
}
else
{
// query the drive parameters with standard bios
r.h.ah = 0x08;
r.h.dl = (0x80 | (j & 0x7F));

int86(0x13, &r, &r);

if (r.h.cflag)
{
if (SystemDisk[j]->RequestSelector)
__dpmi_free_dos_memory(SystemDisk[j]->RequestSelector);
SystemDisk[j]->RequestSelector = 0;

if (SystemDisk[j]->DataSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DataSelector);
SystemDisk[j]->DataSelector = 0;

if (SystemDisk[j]->DriveInfoSelector)
__dpmi_free_dos_memory(SystemDisk[j]->DriveInfoSelector);
SystemDisk[j]->DriveInfoSelector = 0;

SystemDisk[j]->PhysicalDiskHandle = 0;
NWFSFree(SystemDisk[j]);
SystemDisk[j] = 0;

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
continue;
}

if (FirstValidDisk == (ULONG)-1)
FirstValidDisk = j;

SystemDisk[j]->BytesPerSector = 512;
SystemDisk[j]->Cylinders =
(LONGLONG)(((WORD)((WORD)r.h.cl << 2) & 0x0300) |
(WORD)r.h.ch) + 1;
SystemDisk[j]->TracksPerCylinder = (r.h.dh + 1);
SystemDisk[j]->SectorsPerTrack = ((WORD)r.h.cl & 0x003F);
SystemDisk[j]->driveSize = (LONGLONG)
(SystemDisk[j]->Cylinders *
SystemDisk[j]->TracksPerCylinder *
SystemDisk[j]->SectorsPerTrack *
SystemDisk[j]->BytesPerSector);
#if (VERBOSE)
NWFSPrint("disk-%d cyl-%d head-%d sector-%d\n",
(int)j,
(int)SystemDisk[j]->Cylinders,
(int)SystemDisk[j]->TracksPerCylinder,
(int)SystemDisk[j]->SectorsPerTrack);
#endif
}

#if (WINDOWS_98_UTIL)
r.x.ax = 0x440D; // generic IOCTL
r.h.bl = (0x80 | (j & 0x7F)); // dos limit is 128 drives
r.h.ch = 0x08; // device category
r.h.cl = 0x6B; // unlock physical volume
int86(0x21, &r, &r);
#endif
TotalDisks++;
if (TotalDisks > MaximumDisks)
MaximumDisks = TotalDisks;

retCode = ReadDiskSectors(j, 0, Sector,
IO_BLOCK_SIZE / SystemDisk[j]->BytesPerSector,
IO_BLOCK_SIZE / SystemDisk[j]->BytesPerSector);
if (!retCode)
{
NWFSPrint("nwfs: disk-%d read error in ScanDiskDevices\n",
(int)j);
continue;
}

NWFSCopy(&SystemDisk[j]->partitionSignature, &Sector[0x01FE], 2);

if (SystemDisk[j]->partitionSignature != 0xAA55)
{
#if (VERBOSE)
NWFSPrint("nwfs: partition signature 0xAA55 not found
disk(%d)\n", (int)j);
#endif
NWFSSet(&SystemDisk[j]->PartitionTable[0].fBootable, 0, 64);
continue;
}
else
{
NWFSCopy(&SystemDisk[j]->PartitionTable[0].fBootable,
&Sector[0x01BE], 64);
}

// scan for Netware Partitions and detect 3.x and 4.x/5.x
partition types
for (i=0, SystemDisk[j]->NumberOfPartitions = 0; i < 4; i++)
{
if (SystemDisk[j]->PartitionTable[i].nSectorsTotal)
SystemDisk[j]->NumberOfPartitions++;

if (SystemDisk[j]->PartitionTable[i].SysFlag == NETWARE_386_ID)
{
retCode = ReadDiskSectors(j,
SystemDisk[j]->PartitionTable[i].StartLBA,
Sector,
IO_BLOCK_SIZE / SystemDisk[j]->BytesPerSector,
IO_BLOCK_SIZE / SystemDisk[j]->BytesPerSector);
if (!retCode)
{
NWFSPrint("nwfs: disk-%d read error in ScanDiskDevices
(part)\n", (int)j);
continue;
}

if (!NWFSCompare(Sector, NwPartSignature, 16))
SystemDisk[j]->PartitionVersion[i] = NW4X_PARTITION;
else
SystemDisk[j]->PartitionVersion[i] = NW3X_PARTITION;
}
}
}
}
NWFSFree(Sector);
return;
}

Phillip Susi wrote:

> linux-os (Dick Johnson) wrote:
>
>>
>> Yes, it's a very good model, in fact what I've been talking about.
>> However, several people who refused to read or understand, insisted
>> upon obtaining the exact same C/H/S that the machine claimed to
>> use when it was booted.
>>
>
> That's because if you don't use the same geometry that the bios
> reports when calculating the CHS addresses of the sectors you intend
> to load, you won't be requesting the right sectors from int 13.
>
>> So, since Linux doesn't destroy that information remaining in
>> the BIOS tables, I show how to make it available to a 'root' user.
>> Observation over several machines will show that the BIOS always
>> uses the same stuff for large media and, in fact, it has no choice.
>> Basically, this means that the first part of the boot-code, the
>> stuff that needs to be translated to fit into the int 0x13 registers,
>> needs to be below 1024 cylinders, 63 sectors-track, and 256 heads.
>> Trivial... even LILO was able to do that! Once the machine boots
>> past the requirement to use the BIOS services, it's a CHS=NOP.
>>
>
> Generally yes, modern large disks will get clamped at 1024 cylinders,
> 255 heads, and 63 sectors. You seem to be arguing that this will
> always be the case. If that is so, then the kernel doesn't need to
> store these values since it is known a priori does it? But it isn't
> always going to be 255/63, there are some bioses ( I forget which )
> that cap the number of heads at 240, and disks that are smaller than 8
> gigs also will have less than 255 heads.
>
>
>

2006-02-17 21:27:43

by Jeffrey V. Merkey

[permalink] [raw]
Subject: Re: C/H/S from user space



And a very useful translation table for coversion to 63 head models.

Jeff

// Use the Phoenix method of drive translation
// to translate drive geometry for those drives
// that exceed 1024 cylinders in size.
//
// Phoenix Geometry Translation Table
// -----------------------------------
//
// Phys Cylinders Phys Heads Trans Cyl Trans Heads Max
// --------------------------------------------------------
// 1 <C<= 1024 1 <H<= 16 C = C H = H 528 MB
// 1024 <C<= 2048 1 <H<= 16 C = C/2 H = H*2 1.0 GB
// 2048 <C<= 4096 1 <H<= 16 C = C/4 H = H*4 2.1 GB
// 4096 <C<= 8192 1 <H<= 16 C = C/8 H = H*8 4.2 GB
// 8192 <C<= 16384 1 <H<= 16 C = C/16 H = H*16 8.4 GB
// 16384 <C<= 32768 1 <H<= 8 C = C/32 H = H*32 8.4 GB
// 32768 <C<= 65536 1 <H<= 4 C = C/64 H = H*64 8.4 GB
//
// LBA Assisted Translation Table
// ------------------------------
//
// (NOTE: The method below is an alternate method for
// translating large drives that does not place any limits
// on reported drive geometry. It has the disadvantage
// of always assuming 63 Sectors Per Track.)
//
// Range Sectors Heads Cylinders
// -----------------------------------------------------
// 1 MB <X< 528 MB 63 16 X/(63 * 16 * 512)
// 528 MB <X< 1.0 GB 63 32 X/(63 * 32 * 512)
// 1.0 GB <X< 2.1 GB 63 64 X/(63 * 64 * 512)
// 2.1 GB <X< 4.2 GB 63 128 X/(63 * 128 * 512)
// 4.2 GB <X< 8.4 GB 63 255 X/(63 * 255 * 512)
//

// Adjust cylinder and head dimensions until this drive
// presents a geometry with a cylinder count


linux-os (Dick Johnson) wrote:

>On Fri, 17 Feb 2006, Phillip Susi wrote:
>
>
>
>>linux-os (Dick Johnson) wrote:
>>
>>
>>>Yes, it's a very good model, in fact what I've been talking about.
>>>However, several people who refused to read or understand, insisted
>>>upon obtaining the exact same C/H/S that the machine claimed to
>>>use when it was booted.
>>>
>>>
>>>
>>That's because if you don't use the same geometry that the bios reports
>>when calculating the CHS addresses of the sectors you intend to load,
>>you won't be requesting the right sectors from int 13.
>>
>>
> ^^^____
>
>Who is YOU??? I would certainly be requesting the right sectors
>if I (or anybody else who knows what they are doing), wrote
>the boot code. The boot loader knows about OFFSETS into the
>device where it's going to get its data, which eventually
>becomes a whole operating system. It doesn't give a *uck about
>anything else. There is a table of OFFSETS, obtained from
>the file-system, of the correct pieces of files (since there
>will not be a file-system until the machine is booted). This
>table of offsets needs to be read somewhere in the first 63
>sectors (32256 bytes). These offsets contain the junk to
>be loaded into memory.
>
>The boot-code, the code that executes in the 16-bit environment,
>converts those offsets (after getting data from the DPB) into
>the respective junk to put into the registers as I explained
>over and over and over again.
>
>You refuse to learn. Please go away.
>
>[SNIPPED...]
>
>Cheers,
>Dick Johnson
>Penguin : Linux version 2.6.15.4 on an i686 machine (5589.53 BogoMips).
>Warning : 98.36% of all statistics are fiction.
>_
>
>
>****************************************************************
>The information transmitted in this message is confidential and may be privileged. Any review, retransmission, dissemination, or other use of this information by persons or entities other than the intended recipient is prohibited. If you are not the intended recipient, please notify Analogic Corporation immediately - by replying to this message or by sending an email to [email protected] - and destroy all copies of this information, including any attachments, without reading or disclosing them.
>
>Thank you.
>-
>To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>the body of a message to [email protected]
>More majordomo info at http://vger.kernel.org/majordomo-info.html
>Please read the FAQ at http://www.tux.org/lkml/
>
>
>

2006-02-17 22:21:38

by Phillip Susi

[permalink] [raw]
Subject: Re: C/H/S from user space

linux-os (Dick Johnson) wrote:
> Who is YOU??? I would certainly be requesting the right sectors
> if I (or anybody else who knows what they are doing), wrote
> the boot code. The boot loader knows about OFFSETS into the
> device where it's going to get its data, which eventually
> becomes a whole operating system. It doesn't give a *uck about
> anything else. There is a table of OFFSETS, obtained from
> the file-system, of the correct pieces of files (since there
> will not be a file-system until the machine is booted). This
> table of offsets needs to be read somewhere in the first 63
> sectors (32256 bytes). These offsets contain the junk to
> be loaded into memory.
>

We weren't talking about a boot loader you would write, or lilo even, we
were talking about windows's boot loader, which, to the best of my
knowledge, directly passes the CHS address of the starting sector of the
partition stored in the MBR to int 13. That CHS address is calculated
by fdisk based on the geometry it believes the disk has and is written
to the MBR when you partition the disk. The last time I checked a dos
MBR, the boot code there didn't recompute the CHS address at runtime
based on the LBA and current geometry according to the bios, so it has
to be written correctly in the first place by fdisk, which requires that
fdisk know the bios geometry.

> The boot-code, the code that executes in the 16-bit environment,
> converts those offsets (after getting data from the DPB) into
> the respective junk to put into the registers as I explained
> over and over and over again.

Maybe I'm just dense, but that's not what I heard you explaining. Is it
your position then, that the MS DOS/WIN MBR does actually recompute the
CHS address at boot time based on the geometry the bios currently
reports, and the LBA in the partition table, rather than use the CHS
address stored in the partition table and precomputed by fdisk?

If that is the case, then fdisk can use whatever geometry it chooses and
everything will be fine, so why does the kernel have to carry around
some geometry instead of just letting fdisk make one up when it is run?

>
> You refuse to learn. Please go away.
>

That is uncalled for. I don't understand why you have shown a bad
attitude this whole time.

2006-02-18 03:43:17

by Marcin Dalecki

[permalink] [raw]
Subject: Re: C/H/S from user space


On 2006-02-17, at 23:20, Phillip Susi wrote:
> If that is the case, then fdisk can use whatever geometry it
> chooses and everything will be fine, so why does the kernel have to
> carry around some geometry instead of just letting fdisk make one
> up when it is run?

It has been already tried years ago to eliminate this ridicle. The
answer to your
question is put bluntly: It's like that, because some kernel "prima
ballerinas",
whose names "relate to alternating current", will get at you with
mock up somke
and mirror woodo examples where it's supposed to be sooo required.

2006-02-18 16:21:20

by Phillip Susi

[permalink] [raw]
Subject: Re: C/H/S from user space

Marcin Dalecki wrote:
> It has been already tried years ago to eliminate this ridicle. The
> answer to your
> question is put bluntly: It's like that, because some kernel "prima
> ballerinas",
> whose names "relate to alternating current", will get at you with mock
> up somke
> and mirror woodo examples where it's supposed to be sooo required.

I myself have been trying to show why it is required. Unless I am wrong
about the msdos MBR code passing the CHS partition start values directly
to int 13 rather than computing them based on the LBA in the MBR and the
currently reported geometry from the bios, then fdisk does require the
correct bios geometry to maintain compatibility with msdos/windows.


My original point was that if it is required ( and since it is still in
the kernel, that seems to be the case ) then at least make sure it is
_correct_. Whether it is needed but wrong, or if it is simply not
needed, then it is silly to keep geometry in the kernel.