atahd: Support inexact CHS.

The total size needs to have 3 factors cylinders, heads, & sectors. Imagine a disk having a total size with 3 prime factors 3 x 5 x 11. 15 cannot be assigned to heads because that would only leave 11 for sectors and cylinders. Therefore, test all heads and sectors combinations. If the third factor for cylinders is not found, then choose 16 heads, then the minimum number of sectors, and finally the maximum number of cylinders. The loop could be changed to skip values of heads that are not a factor, but it doesn't take any time to try them all.
This commit is contained in:
joevt 2024-03-28 04:54:49 -07:00 committed by dingusdev
parent 96dc02b249
commit 155b8cdad9
2 changed files with 35 additions and 32 deletions

View File

@ -209,50 +209,51 @@ uint64_t AtaHardDisk::get_lba() {
}
void AtaHardDisk::calc_chs_params() {
unsigned num_blocks, heads, sectors, max_sectors;
unsigned num_blocks, heads, sectors, max_sectors, cylinders, max_cylinders;
LOG_F(INFO, "%s: total sectors %d", this->name.c_str(), this->total_sectors);
if (this->total_sectors >= REAL_CHS_LIMIT) {
heads = 16;
sectors = 255;
cylinders = 65535;
LOG_F(WARNING, "%s: exceeds max CHS translation",
this->name.c_str());
goto done;
}
// use PC BIOS limit to keep number of sectors small for smaller disks
if (this->total_sectors >= ATA_BIOS_LIMIT)
if (this->total_sectors >= ATA_BIOS_LIMIT) {
max_sectors = 255;
else
max_cylinders = 65535;
} else {
max_sectors = 63;
max_cylinders = 16383;
}
num_blocks = this->total_sectors;
for (heads = 16; heads > 0; heads--)
if (!(num_blocks % heads))
break;
for (sectors = max_sectors; sectors > 0; sectors--)
if (!(num_blocks % (heads * sectors)))
if (heads * sectors * max_cylinders >= num_blocks) {
cylinders = num_blocks / (heads * sectors);
goto done;
}
heads = 16;
sectors = (num_blocks + heads * max_cylinders - 1) / (heads * max_cylinders);
cylinders = (num_blocks + heads * sectors - 1) / (heads * sectors);
LOG_F(WARNING, "%s: could not find a suitable CHS translation; increased sectors to %d",
this->name.c_str(), heads * sectors * cylinders);
done:
this->heads = heads;
if (!heads) {
LOG_F(WARNING, "%s: could not find a suitable number of heads",
this->name.c_str());
return;
}
num_blocks /= heads;
for (sectors = max_sectors; sectors > 0; sectors--) {
if (!(num_blocks % sectors)) {
if (num_blocks / sectors < 65536)
break;
}
}
this->sectors = sectors;
if (!sectors) {
LOG_F(WARNING, "%s: could not find a suitable CHS translation",
this->name.c_str());
return;
}
this->cylinders = num_blocks / sectors;
LOG_F(INFO, "%s: C=%d, H=%d, S=%d", this->name.c_str(), this->cylinders,
this->heads, this->sectors);
this->cylinders = cylinders;
LOG_F(INFO, "%s: C=%d, H=%d, S=%d", this->name.c_str(), cylinders,
heads, sectors);
}
static const PropMap AtaHardDiskProperties = {

View File

@ -30,7 +30,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#define ATA_HD_SEC_SIZE 512
#define ATA_BIOS_LIMIT 16514064
#define ATA_BIOS_LIMIT 16514064 // C:16383 x H:16 x S:63 = C:1032 x H:254 x S:63 = 8063.5078125 MiB = 8.46 GB
#define REAL_CHS_LIMIT 267382800 // C:65535 x H:16 x S:255 = 127.498 GiB = 136.900 GB = largest identify
#define CHS_LIMIT 267386880 // C:65536 x H:16 x S:255 = 127.500 GiB = 136.902 GB = largest address
class AtaHardDisk : public AtaBaseDevice
{