Skip to content

Commit

Permalink
[bioshd] Implement park for ancient MFM/RLL disks
Browse files Browse the repository at this point in the history
Really old drives, such as the two ST-412s in my IBM 5161, required
manual parking to ensure the r/w heads are over a safe location when the
disk spins down and the heads (intentionally!) crash onto the disk
surface.

In DOS land, this vital task was left up to the user, in the form of a
PARK command to be run before shutting down. In ELKS however, the kernel
should manage this automatically. An INT 13/AH=0Ch is invoked to seek
the disk to the last track when unmounted, and to all drives right
before halting the system.
  • Loading branch information
vkoskiv committed Aug 28, 2024
1 parent 8afd20c commit ad46cd7
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 21 deletions.
18 changes: 18 additions & 0 deletions elks/arch/i86/drivers/block/bios.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,24 @@ int INITPROC bios_gethdinfo(struct drive_infot *drivep) {
}
return ndrives;
}

static void BFPROC bios_disk_park(struct drive_infot *drive)
{
/* int 13 AH=0xC supports only 10 bits for cyl */
if (drive->cylinders > 1024 || drive->fdtype != -1) return;
BD_AX = 0x0C << 8;
BD_CX = ((drive->cylinders & 0xFF) << 8) | ((drive->cylinders & 0x300) >> 2) | 0x1; /* 0x1 = sector */
BD_DX = bios_drive_map[drive - drive_info];
call_bios(&bdt);
}

void bios_disk_park_all(void)
{
int i;
for (i = 0; i < NUM_DRIVES; ++i)
bios_disk_park(&drive_info[i]);
}

#endif

#ifdef CONFIG_BLK_DEV_BFD_HARD
Expand Down
2 changes: 1 addition & 1 deletion elks/arch/i86/drivers/block/bioshd.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ static int fd_count = 0; /* number of floppy disks */
static int hd_count = 0; /* number of hard disks */

static int access_count[NUM_DRIVES]; /* device open count */
static struct drive_infot drive_info[NUM_DRIVES]; /* operating drive info */
struct drive_infot drive_info[NUM_DRIVES]; /* operating drive info */
static struct drive_infot *cache_drive;
struct drive_infot *last_drive; /* set to last drivep-> used in read/write */
extern struct drive_infot fd_types[]; /* BIOS floppy formats */
Expand Down
40 changes: 20 additions & 20 deletions elks/arch/i86/drivers/block/rd.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static struct { /* ramdrive information*/
int start; /* starting memory segment*/
int valid; /* ramdisk data valid flag*/
rd_sector_t size; /* ramdisk size in 512 byte sectors*/
} drive_info[MAX_DRIVES] = {
} rd_info[MAX_DRIVES] = {
#if CONFIG_RAMDISK_SEGMENT
{0, 1, CONFIG_RAMDISK_SECTORS}
#endif
Expand Down Expand Up @@ -76,13 +76,13 @@ static int rd_open(struct inode *inode, struct file *filp)
debug("RD: open /dev/rd%d\n", target);
if (!rd_initialised || target >= MAX_DRIVES
#if CONFIG_RAMDISK_SEGMENT
|| !drive_info[target].valid
|| !rd_info[target].valid
#endif
)
return -ENXIO;

++access_count[target];
inode->i_size = (long)drive_info[target].size << 9;
inode->i_size = (long)rd_info[target].size << 9;
return 0;
}

Expand Down Expand Up @@ -115,9 +115,9 @@ static int dealloc(int target)
{
int i, j;

i = drive_info[target].start;
i = rd_info[target].start;
debug("RD: dealloc target %d, index %d, size %d sectors\n",
target, i, drive_info[target].size);
target, i, rd_info[target].size);
while (i != -1 && rd_segment[i].sectors != 0) {
j = i;
debug("RD: dealloc purging rd_segment[%d].seg = 0x%x, next index %d, size = %d\n",
Expand All @@ -132,8 +132,8 @@ static int dealloc(int target)
debug("RD: dealloc status rd_segment[%d].seg = 0x%x, next index %d, size = %d\n",
j, rd_segment[j].seg, rd_segment[j].next, rd_segment[j].sectors);
}
drive_info[target].valid = 0;
drive_info[target].size = 0;
rd_info[target].valid = 0;
rd_info[target].size = 0;
return 0;
}

Expand All @@ -150,10 +150,10 @@ static int rd_ioctl(register struct inode *inode, struct file *file,
debug("RD: ioctl %d %s\n", target, (cmd ? "kill" : "make"));
switch (cmd) {
case RDCREATE:
if (drive_info[target].valid)
if (rd_info[target].valid)
return -EBUSY;

drive_info[target].size = 0;
rd_info[target].size = 0;
k = -1;
for (i = 0; i <= (arg - 1) / ((ALLOC_SIZE / 1024) * PARA); i++) {
j = find_free_seg(); /* find free place in queue */
Expand All @@ -163,7 +163,7 @@ static int rd_ioctl(register struct inode *inode, struct file *file,
return -ENOMEM;
}
if (i == 0)
drive_info[target].start = j;
rd_info[target].start = j;

if (i == (arg / ((ALLOC_SIZE / 1024) * PARA)))
/* size in 16 byte pages = (arg % 64) * 64 */
Expand All @@ -185,7 +185,7 @@ static int rd_ioctl(register struct inode *inode, struct file *file,
/* recalculate size to reflect size in sectors, not pages */
size = size / (RD_SECTOR_SIZE / PARA);
rd_segment[j].sectors = size;
drive_info[target].size += rd_segment[j].sectors;
rd_info[target].size += rd_segment[j].sectors;
size = (long) rd_segment[j].sectors * RD_SECTOR_SIZE;
debug("RD: index %d set to %d sectors, %ld bytes\n",
j, rd_segment[j].sectors, size);
Expand All @@ -198,14 +198,14 @@ static int rd_ioctl(register struct inode *inode, struct file *file,
rd_segment[k].next = j; /* set link to next index */
k = j;
}
drive_info[target].valid = 1;
rd_info[target].valid = 1;
debug("RD: ramdisk %d created sectors %d index %d bytes %ld\n",
target, drive_info[target].size, drive_info[target].start,
(long)drive_info[target].size * RD_SECTOR_SIZE);
target, rd_info[target].size, rd_info[target].start,
(long)rd_info[target].size * RD_SECTOR_SIZE);
return 0;

case RDDESTROY:
if (drive_info[target].valid) {
if (rd_info[target].valid) {
invalidate_inodes(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
dealloc(target);
Expand Down Expand Up @@ -241,18 +241,18 @@ static void do_rd_request(void)
target = DEVICE_NR(req->rq_dev);
debug("RD: %s dev %d sector %d, ", req->rq_cmd == READ? "read": "write",
target, start);
if (drive_info[target].valid == 0 ||
start + req->rq_nr_sectors > drive_info[target].size) {
if (rd_info[target].valid == 0 ||
start + req->rq_nr_sectors > rd_info[target].size) {
debug("rd%d: sector %d beyond max %d\n",
target, start, drive_info[target].size);
target, start, rd_info[target].size);
end_request(0);
continue;
}

for (count = 0; count < req->rq_nr_sectors; count++) {
/* find appropriate memory segment and sector offset*/
offset = start;
index = drive_info[target].start;
index = rd_info[target].start;
debug("index %d, ", index);
while (offset > rd_segment[index].sectors) {
offset -= rd_segment[index].sectors;
Expand Down Expand Up @@ -293,7 +293,7 @@ void INITPROC rd_init(void)

#if CONFIG_RAMDISK_SEGMENT
printk("rd: %dK ramdisk at %x:0000\n",
drive_info[0].size >> 1, rd_segment[0].seg);
rd_info[0].size >> 1, rd_segment[0].seg);

#if (CONFIG_RAMDISK_SECTORS > 128)
/* build segment array for preloaded ramdisks > 64k*/
Expand Down
3 changes: 3 additions & 0 deletions elks/include/linuxmt/biosparm.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,8 @@ void BFPROC bios_copy_ddpt(void);
struct drive_infot;
void BFPROC bios_switch_device98(int target, unsigned int device,
struct drive_infot *drivep);
#ifdef CONFIG_BLK_DEV_BHD
void bios_disk_park_all(void);
#endif

#endif
1 change: 1 addition & 0 deletions elks/include/linuxmt/genhd.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ struct gendisk

extern struct drive_infot *last_drive; /* set to last drivep-> used in read/write */
extern unsigned char bios_drive_map[]; /* map drive to BIOS drivenum */
extern struct drive_infot drive_info[];

extern struct gendisk *gendisk_head; /* linked list of disks */

Expand Down
10 changes: 10 additions & 0 deletions elks/kernel/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/

#include <linuxmt/biosparm.h>
#include <linuxmt/config.h>
#include <linuxmt/errno.h>
#include <linuxmt/sched.h>
Expand Down Expand Up @@ -48,16 +49,25 @@ int sys_reboot(unsigned int magic, unsigned int magic_too, int flag)
C_A_D = flag;
return 0;
case 0x0123: /* reboot*/
#ifdef CONFIG_BLK_DEV_BHD
bios_disk_park_all();
#endif
hard_reset_now();
printk("Reboot failed\n");
/* fall through*/
case 0x6789: /* shutdown*/
sys_kill(1, SIGKILL);
sys_kill(-1, SIGKILL);
#ifdef CONFIG_BLK_DEV_BHD
bios_disk_park_all();
#endif
printk("System halted\n");
do_exit(0);
/* no return*/
case 0xDEAD: /* poweroff*/
#ifdef CONFIG_BLK_DEV_BHD
bios_disk_park_all();
#endif
apm_shutdown_now();
printk("APM shutdown failed\n");
}
Expand Down

0 comments on commit ad46cd7

Please sign in to comment.