3.3.1 Preparing to Install the Hard Disk File System by Process 1
3.3.1.4 Read the Hard Disk
Execute the do_hd_request function to prepare for reading the hard disk, as shown in Figure 3.25.
First, analyze the members of the request item to get the head, sector, cylinder, and the number of sectors needed to operate. Then, establish the necessary hard drive read parameters and move the head to 0 cylinder. Then, send operation command(read/wrote) to the hard disk; now that the command is read, read boot block of the hard drive and call the hd_out function to send operation command to the hard disk. Take note of the last two real parameters: WIN_READ means read operation and read_intr is an interrupt ser- vice program corresponding to read operation, as shown in the third step in Figure 3.25.
The code is as follows:
//Code path:kernel/blk_dev/hd.c:
void do_hd_request(void) {
int i,r;
unsigned int block,dev;
unsigned int sec,head,cyl;
unsigned int nsect;
INIT_REQUEST;
dev = MINOR(CURRENT->dev);
block = CURRENT->sector;
if (dev > = 5*NR_HD || block+2 > hd[dev].nr_sects) { end_request(0);
goto repeat;
Kernel
ROM BIOS
and VGA Enable
interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
do_hd_request blk_dev[7]
Disk
Idle Using the structure of blk_dev,
find disk request processing function, prepare to send reading disk command Process status
Process 0 Process 1
Ready Interruptible
Current process
Figure 3.24 Bind the request item with the hard disk processing function.
Send read hard disk command as shown in the first step in Figure 3.26.
The specific code is as follows:
}
block + = hd[dev].start_sect;
dev/= 5;
__asm__(“divl%4”:“ = a” (block),“ = d” (sec):“0” (block),“1” (0),
“r” (hd_info[dev].sect));
__asm__(“divl%4”:“ = a” (cyl),“ = d” (head):“0” (block),“1” (0),
“r” (hd_info[dev].head));
sec++;
nsect = CURRENT->nr_sectors;
if (reset) {
reset = 0; //set, prevent mutiply execute if (reset) recalibrate = 1; //set, assure executing if(recalibrate) reset_hd(CURRENT_DEV); //send the command “WIN_SPECIFY” to hard
//disk by calling hd_out, and establish the //necessary parameter for reading hard disk return;
}
if (recalibrate) {
recalibrate = 0; //set, prevent mutiply execute if //(recalibrate)
hd_out(dev,hd_info[CURRENT_DEV].sect,0,0,0,
WIN_RESTORE,&recal_intr); //send the command “WIN_RESTORE” to //hard disk, and move the magnetic //head to cycle 0 for reading data //from hard disk
return;
}
if (CURRENT->cmd == WRITE) {
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
for(i = 0 ; i<3000 && !(r = inb_p(HD_STATUS)&DRQ_STAT) ; i++) /* nothing */;
if (!r) {
bad_rw_intr();
goto repeat;
}
port_write(HD_DATA,CURRENT->buffer,256);
} else if (CURRENT->cmd == READ) {
hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); //Note two parameters!
} else
panic(“unknown hd-command”);
}
//Code path:kernel/blk_dev/hd.c:
static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, unsigned int head,unsigned int cyl,unsigned int cmd,
void (*intr_addr)(void)) //the parameter is WIN_READ,&read_intr {
register int port asm(“dx”);
if (drive>1 || head>15)
panic(“Trying to write bad sector”);
if (!controller_ready())
panic(“HD controller not ready”);
do_hd = intr_addr; //determine it’s read_intr or
//write_intr according to the parameter, //in this case, it’s read_intr
outb_p(hd_info[drive].ctl,HD_CMD);
port = HD_DATA;
outb_p(hd_info[drive].wpcom>>2,++port);
outb_p(nsect,++port);
outb_p(sect,++port);
outb_p(cyl,++port);
outb_p(cyl>>8,++port);
do_hd = intr_addr binds the reading disk service routine with the hard disk interrupt service routine; here the do_hd is the content of “xchgl _do_hd,%edx” in _hd_interrupt function (system_call.s).
In this case, the operation is read disk, so attach to the read_intr(), if in the case of write disk, attach to the write_intr() functions.
Do read disk order!
The hard disk reads the data of the boot block into its cache; at the same time, the pro- gram also returns and will call hd_out(), do_hd_request(), add_request(), make_request(), and ll_rw_block() in the opposite direction until it returns to the bread() function.
outb_p(0xA0|(drive<<4)|head,++port);
outb(cmd,++port);
}
//Code path:kernel/system_call.s:
_hd_interrupt:
……
1: jmp 1f 1: xorl%edx,%edx xchgl _do_hd,%edx testl%edx,%edx jne 1f
movl $_unexpected_hd_interrupt,%edx
……
ROM BIOS and VGA
Kernel
Enable interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
Disk
Process status
Process 0 Process 1
hd_interrupt read_intr
hd_info hd
Request[32]
Step 1:
analyzing data sent to hard disk port register Step 2:
detect the state of hard disk
Step 3:
hook up interrupt response program
Ready Interrupt wait state
Current process
Figure 3.25 Prepare for reading the hard disk.
Now, the hard disk continues to read the boot block; if the program continues to execute, it needs to manipulate the data in the boot block, but these data are not read from the hard drive, so call the wait_on_buffer function and suspend the process!
The execution code is as follows:
//code path:fs/buffer.c:
struct buffer_head * bread(int dev,int block) {
struct buffer_head * bh;
if (!(bh = getblk(dev,block)))
panic(“bread: getblk returned NULL\n”);
if (bh->b_uptodate) return bh;
ll_rw_block(READ,bh);
wait_on_buffer(bh); //suspend the process waiting //for unlock buffer
if (bh->b_uptodate) return bh;
brelse(bh);
return NULL;
}
Enable interrupt ROM BIOS
and VGA
0x00000
Kernel 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
Bread ll_rw_block
Step 1: send disk the command to read
Step 2:
after sending the command, return to bread to run make_request add_request do_hd_request hd_out
Disk Process status
Process 0 Process 1
Ready Interruptible
Current process
Figure 3.26 Send parameters to hard drive port registers and suspend the current process.
After entering the wait_on_buffer, it checks whether the buffer block is locked or not.
If the buffer block is locked, it calls the sleep_on. The code is as follows:
Enter the sleep_on function and set process 1 to uninterruptible state, as shown in step 3 in Figure 3.27. Process 1 suspends then call the schedule function and be ready to switch processes. The execution code is as follows:
//code path:fs/buffer.c:
static inline void wait_on_buffer(struct buffer_head * bh) {
cli();
while (bh->b_lock) //has been lock before sleep_on(&bh->b_wait);
sti();
} Kernel
Disable interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
Schedule sleep_on wait_on_buffer
Disk
Process status
Process 0 Process 1
Uninterruptible Interruptible
Current process
Step 1: disable interrupt
Step 2: call sleep_on
Step 3: suspend process 1
ROM BIOS and VGA
Step 4:
execute schedule
Reading data continuously from disk
Figure 3.27 Process 1 suspends and executes schedule.