/// zcr copy whole file from Orange's and the file was modified. /*************************************************************************//** ***************************************************************************** * @file hd.c * @brief Hard disk (winchester) driver. * The `device nr' in this file means minor device nr. * @author Forrest Y. Yu * @date 2005~2008 ***************************************************************************** *****************************************************************************/ #include "type.h" #include "const.h" #include "protect.h" #include "string.h" #include "proc.h" #include "global.h" #include "proto.h" #include "fs_const.h" #include "hd.h" #include "fs.h" #include "fs_misc.h" #include "x86.h" #include "stdio.h" #include "assert.h" struct part_ent PARTITION_ENTRY; //added by xw, 18/8/28 static HDQueue hdque; static volatile int hd_int_waiting_flag; static u8 hd_status; static u8 hdbuf[SECTOR_SIZE * 2]; //static struct hd_info hd_info[1]; struct hd_info hd_info[1]; //modified by mingxuan 2020-10-27 static void init_hd_queue(HDQueue *hdq); static void in_hd_queue(HDQueue *hdq, RWInfo *p); static int out_hd_queue(HDQueue *hdq, RWInfo **p); static void hd_rdwt_real(RWInfo *p); static void get_part_table(int drive, int sect_nr, struct part_ent *entry); static void partition(int device, int style); static void print_hdinfo(struct hd_info *hdi); static void hd_identify(int drive); static void print_identify_info(u16 *hdinfo); static void hd_cmd_out(struct hd_cmd *cmd); static void inform_int(); static void interrupt_wait(); static void hd_handler(int irq); static int waitfor(int mask, int val, int timeout); //~xw #define DRV_OF_DEV(dev) (dev <= MAX_PRIM ? \ dev / NR_PRIM_PER_DRIVE : \ (dev - MINOR_hd1a) / NR_SUB_PER_DRIVE) /***************************************************************************** * init_hd *****************************************************************************/ /** * Check hard drive, set IRQ handler, enable IRQ and initialize data * structures. *****************************************************************************/ void init_hd() { int i; put_irq_handler(AT_WINI_IRQ, hd_handler); enable_irq(CASCADE_IRQ); enable_irq(AT_WINI_IRQ); for (i = 0; i < (sizeof(hd_info) / sizeof(hd_info[0])); i++) memset(&hd_info[i], 0, sizeof(hd_info[0])); hd_info[0].open_cnt = 0; init_hd_queue(&hdque); } /***************************************************************************** * hd_open *****************************************************************************/ /** * This routine handles DEV_OPEN message. It identify the drive * of the given device and read the partition table of the drive if it * has not been read. * * @param device The device to be opened. *****************************************************************************/ // void hd_open(int device) //no need for int device, mingxuan void hd_open(int drive) //modified by mingxuan 2020-10-27 { kprintf("Read hd information... "); /* Get the number of drives from the BIOS data area */ // u8 * pNrDrives = (u8*)(0x475); hd_identify(drive); if (hd_info[drive].open_cnt++ == 0) { partition(drive * (NR_PART_PER_DRIVE + 1), P_PRIMARY); print_hdinfo(&hd_info[drive]); } } /***************************************************************************** * hd_close *****************************************************************************/ /** * This routine handles DEV_CLOSE message. * * @param device The device to be opened. *****************************************************************************/ void hd_close(int device) { int drive = DRV_OF_DEV(device); hd_info[drive].open_cnt--; } /***************************************************************************** * hd_rdwt *****************************************************************************/ /** * This routine handles DEV_READ and DEV_WRITE message. * * @param p Message ptr. *****************************************************************************/ void hd_rdwt(MESSAGE * p) { int drive = DRV_OF_DEV(p->DEVICE); u64 pos = p->POSITION; //We only allow to R/W from a SECTOR boundary: u32 sect_nr = (u32)(pos >> SECTOR_SIZE_SHIFT); // pos / SECTOR_SIZE int logidx = (p->DEVICE - MINOR_hd1a) % NR_SUB_PER_DRIVE; sect_nr += p->DEVICE < MAX_PRIM ? hd_info[drive].primary[p->DEVICE].base : hd_info[drive].logical[logidx].base; struct hd_cmd cmd; cmd.features = 0; cmd.count = (p->CNT + SECTOR_SIZE - 1) / SECTOR_SIZE; cmd.lba_low = sect_nr & 0xFF; cmd.lba_mid = (sect_nr >> 8) & 0xFF; cmd.lba_high = (sect_nr >> 16) & 0xFF; cmd.device = MAKE_DEVICE_REG(1, drive, (sect_nr >> 24) & 0xF); cmd.command = (p->type == DEV_READ) ? ATA_READ : ATA_WRITE; hd_cmd_out(&cmd); int bytes_left = p->CNT; void * la = (void*)va2la(p->PROC_NR, p->BUF); while (bytes_left) { int bytes = min(SECTOR_SIZE, bytes_left); if (p->type == DEV_READ) { interrupt_wait(); insw(REG_DATA, hdbuf, SECTOR_SIZE); memcpy(la, hdbuf, bytes); } else { if (!waitfor(STATUS_DRQ, STATUS_DRQ, HD_TIMEOUT)) ("hd writing error."); memcpy(hdbuf, la, bytes); outsw(REG_DATA, hdbuf, SECTOR_SIZE); interrupt_wait(); } bytes_left -= SECTOR_SIZE; la += SECTOR_SIZE; } } //added by xw, 18/8/26 void hd_service() { RWInfo *rwinfo; while(1) { //the hd queue is not empty when out_hd_queue return 1. while(out_hd_queue(&hdque, &rwinfo)) { hd_rdwt_real(rwinfo); rwinfo->proc->task.stat = READY; } yield(); } } static void hd_rdwt_real(RWInfo *p) { int drive = DRV_OF_DEV(p->msg->DEVICE); u64 pos = p->msg->POSITION; //We only allow to R/W from a SECTOR boundary: u32 sect_nr = (u32)(pos >> SECTOR_SIZE_SHIFT); // pos / SECTOR_SIZE int logidx = (p->msg->DEVICE - MINOR_hd1a) % NR_SUB_PER_DRIVE; sect_nr += p->msg->DEVICE < MAX_PRIM ? hd_info[drive].primary[p->msg->DEVICE].base : hd_info[drive].logical[logidx].base; struct hd_cmd cmd; cmd.features = 0; cmd.count = (p->msg->CNT + SECTOR_SIZE - 1) / SECTOR_SIZE; cmd.lba_low = sect_nr & 0xFF; cmd.lba_mid = (sect_nr >> 8) & 0xFF; cmd.lba_high = (sect_nr >> 16) & 0xFF; cmd.device = MAKE_DEVICE_REG(1, drive, (sect_nr >> 24) & 0xF); cmd.command = (p->msg->type == DEV_READ) ? ATA_READ : ATA_WRITE; hd_cmd_out(&cmd); int bytes_left = p->msg->CNT; void *la = p->kbuf; //attention here! while (bytes_left) { int bytes = min(SECTOR_SIZE, bytes_left); if (p->msg->type == DEV_READ) { interrupt_wait(); insw(REG_DATA, hdbuf, SECTOR_SIZE); memcpy(la, hdbuf, bytes); } else { if (!waitfor(STATUS_DRQ, STATUS_DRQ, HD_TIMEOUT)) panic("hd writing error."); memcpy(hdbuf, la, bytes); outsw(REG_DATA, hdbuf, SECTOR_SIZE); interrupt_wait(); } bytes_left -= SECTOR_SIZE; la += SECTOR_SIZE; } } void hd_rdwt_sched(MESSAGE *p) { RWInfo rwinfo; struct memfree hdque_buf; int size = p->CNT; void *buffer; buffer = (void*)K_PHY2LIN(sys_kmalloc(size)); rwinfo.msg = p; rwinfo.kbuf = buffer; rwinfo.proc = p_proc_current; if (p->type == DEV_READ) { in_hd_queue(&hdque, &rwinfo); p_proc_current->task.channel = &hdque; p_proc_current->task.stat = SLEEPING; sched(); memcpy(p->BUF, buffer, p->CNT); } else { memcpy(buffer, p->BUF, p->CNT); in_hd_queue(&hdque, &rwinfo); p_proc_current->task.channel = &hdque; p_proc_current->task.stat = SLEEPING; sched(); } hdque_buf.addr = K_LIN2PHY((u32)buffer); hdque_buf.size = size; sys_free(&hdque_buf); } void init_hd_queue(HDQueue *hdq) { hdq->front = hdq->rear = NULL; } static void in_hd_queue(HDQueue *hdq, RWInfo *p) { p->next = NULL; if(hdq->rear == NULL) { //put in the first node hdq->front = hdq->rear = p; } else { hdq->rear->next = p; hdq->rear = p; } } static int out_hd_queue(HDQueue *hdq, RWInfo **p) { if (hdq->rear == NULL) return 0; //empty *p = hdq->front; if (hdq->front == hdq->rear) { //put out the last node hdq->front = hdq->rear = NULL; } else { hdq->front = hdq->front->next; } return 1; //not empty } //~xw /***************************************************************************** * hd_ioctl *****************************************************************************/ /** * This routine handles the DEV_IOCTL message. * * @param p Ptr to the MESSAGE. *****************************************************************************/ void hd_ioctl(MESSAGE * p) { int device = p->DEVICE; int drive = DRV_OF_DEV(device); struct hd_info * hdi = &hd_info[drive]; if (p->REQUEST == DIOCTL_GET_GEO) { void * dst = va2la(p->PROC_NR, p->BUF); void * src = va2la(proc2pid(p_proc_current), device < MAX_PRIM ? &hdi->primary[device] : &hdi->logical[(device - MINOR_hd1a) % NR_SUB_PER_DRIVE]); memcpy(dst, src, sizeof(struct part_info)); } else { // assert(0); } } /***************************************************************************** * get_part_table *****************************************************************************/ /** * Get a partition table of a drive. * * @param drive Drive nr (0 for the 1st disk, 1 for the 2nd, ...)n * @param sect_nr The sector at which the partition table is located. * @param entry Ptr to part_ent struct. *****************************************************************************/ static void get_part_table(int drive, int sect_nr, struct part_ent * entry) { struct hd_cmd cmd; cmd.features = 0; cmd.count = 1; cmd.lba_low = sect_nr & 0xFF; cmd.lba_mid = (sect_nr >> 8) & 0xFF; cmd.lba_high = (sect_nr >> 16) & 0xFF; cmd.device = MAKE_DEVICE_REG(1, /* LBA mode*/ drive, (sect_nr >> 24) & 0xF); cmd.command = ATA_READ; hd_cmd_out(&cmd); interrupt_wait(); insw(REG_DATA, hdbuf, SECTOR_SIZE); memcpy(entry, hdbuf + PARTITION_TABLE_OFFSET, sizeof(struct part_ent) * NR_PART_PER_DRIVE); } // added by mingxuan 2020-10-27 static void get_fs_flags(int drive, int sect_nr, struct fs_flags * fs_flags_buf) { struct hd_cmd cmd; cmd.features = 0; cmd.count = 1; cmd.lba_low = sect_nr & 0xFF; cmd.lba_mid = (sect_nr >> 8) & 0xFF; cmd.lba_high = (sect_nr >> 16) & 0xFF; cmd.device = MAKE_DEVICE_REG(1, /* LBA mode*/ drive, (sect_nr >> 24) & 0xF); cmd.command = ATA_READ; hd_cmd_out(&cmd); interrupt_wait(); insw(REG_DATA, hdbuf, SECTOR_SIZE); memcpy(fs_flags_buf, hdbuf, sizeof(struct fs_flags)); } /***************************************************************************** * partition *****************************************************************************/ /** * This routine is called when a device is opened. It reads the * partition table(s) and fills the hd_info struct. * * @param device Device nr. * @param style P_PRIMARY or P_EXTENDED. *****************************************************************************/ static void partition(int device, int style) { int i; int drive = DRV_OF_DEV(device); struct hd_info * hdi = &hd_info[drive]; struct part_ent part_tbl[NR_SUB_PER_DRIVE]; if (style == P_PRIMARY) { get_part_table(drive, drive, part_tbl); int nr_prim_parts = 0; for (i = 0; i < NR_PART_PER_DRIVE; i++) { /* 0~3 */ if (part_tbl[i].sys_id == NO_PART) continue; nr_prim_parts++; int dev_nr = i + 1; /* 1~4 */ hdi->primary[dev_nr].base = part_tbl[i].start_sect; hdi->primary[dev_nr].size = part_tbl[i].nr_sects; // added by mingxuan 2020-10-27 struct fs_flags fs_flags_buf; get_fs_flags(drive, hdi->primary[dev_nr].base+1, &fs_flags_buf); //hdi->primary[dev_nr].base + 1 beacause of orange and fat32 is in 2nd sector, mingxuan if(fs_flags_buf.orange_flag == 0x11) // Orange's Magic hdi->primary[dev_nr].fs_type = ORANGE_TYPE; else if(fs_flags_buf.fat32_flag1 == 0x534f4453 && fs_flags_buf.fat32_flag2 == 0x302e35) // FAT32 flags hdi->primary[dev_nr].fs_type = FAT32_TYPE; // added end, mingxuan 2020-10-27 if (part_tbl[i].sys_id == EXT_PART) /* extended */ partition(device + dev_nr, P_EXTENDED); } } else if (style == P_EXTENDED) { int j = device % NR_PRIM_PER_DRIVE; /* 1~4 */ int ext_start_sect = hdi->primary[j].base; int s = ext_start_sect; int nr_1st_sub = (j - 1) * NR_SUB_PER_PART; /* 0/16/32/48 */ for (i = 0; i < NR_SUB_PER_PART; i++) { int dev_nr = nr_1st_sub + i;/* 0~15/16~31/32~47/48~63 */ get_part_table(drive, s, part_tbl); hdi->logical[dev_nr].base = s + part_tbl[0].start_sect; hdi->logical[dev_nr].size = part_tbl[0].nr_sects; // added by mingxuan 2020-10-29 struct fs_flags fs_flags_buf; get_fs_flags(drive, hdi->logical[dev_nr].base+1, &fs_flags_buf); //hdi->primary[dev_nr].base + 1 beacause of orange and fat32 is in 2nd sector, mingxuan if(fs_flags_buf.orange_flag == 0x11) // Orange's Magic hdi->logical[dev_nr].fs_type = ORANGE_TYPE; else if(fs_flags_buf.fat32_flag1 == 0x534f4453 && fs_flags_buf.fat32_flag2 == 0x302e35) // FAT32 flags hdi->logical[dev_nr].fs_type = FAT32_TYPE; // added end, mingxuan 2020-10-29 s = ext_start_sect + part_tbl[1].start_sect; /* no more logical partitions in this extended partition */ if (part_tbl[1].sys_id == NO_PART) break; } } else { // assert(0); } } /***************************************************************************** * print_hdinfo *****************************************************************************/ /** * Print disk info. * * @param hdi Ptr to struct hd_info. *****************************************************************************/ static void print_hdinfo(struct hd_info * hdi) { int i; for (i = 0; i < NR_PART_PER_DRIVE + 1; i++) { if(i == 0) { kprintf(" "); } else { kprintf(" "); } kprintf("PART_%d: base %d, size: %d (in sector)\n", i, hdi->primary[i].base, hdi->primary[i].size); } for (i = 0; i < NR_SUB_PER_DRIVE; i++) { if (hdi->logical[i].size == 0) continue; kprintf(" %d: base %d, size %d (in sector)\n", i, hdi->logical[i].base, hdi->logical[i].size); } } /***************************************************************************** * hd_identify *****************************************************************************/ /** * Get the disk information. * * @param drive Drive Nr. *****************************************************************************/ static void hd_identify(int drive) { struct hd_cmd cmd; cmd.device = MAKE_DEVICE_REG(0, drive, 0); cmd.command = ATA_IDENTIFY; hd_cmd_out(&cmd); interrupt_wait(); insw(REG_DATA, hdbuf, SECTOR_SIZE); print_identify_info((u16*)hdbuf); u16* hdinfo = (u16*)hdbuf; hd_info[drive].primary[0].base = 0; /* Total Nr of User Addressable Sectors */ hd_info[drive].primary[0].size = ((int)hdinfo[61] << 16) + hdinfo[60]; } /***************************************************************************** * print_identify_info *****************************************************************************/ /** * Print the hdinfo retrieved via ATA_IDENTIFY command. * * @param hdinfo The buffer read from the disk i/o port. *****************************************************************************/ static void print_identify_info(u16* hdinfo) { int i, k; char s[64]; struct iden_info_ascii { int idx; int len; char * desc; } iinfo[] = {{10, 20, "HD SN"}, /* Serial number in ASCII */ {27, 40, "HD Model"} /* Model number in ASCII */ }; for (k = 0; k < sizeof(iinfo)/sizeof(iinfo[0]); k++) { char * p = (char*)&hdinfo[iinfo[k].idx]; for (i = 0; i < iinfo[k].len/2; i++) { s[i*2+1] = *p++; s[i*2] = *p++; } s[i*2] = 0; kprintf("%s: %s\n", iinfo[k].desc, s); } int capabilities = hdinfo[49]; kprintf("LBA supported:%s ", capabilities & 0x0200 ? "YES" : "NO"); int cmd_set_supported = hdinfo[83]; kprintf("LBA48 supported:%s ", cmd_set_supported & 0x0400 ? "YES" : "NO"); int sectors = ((int)hdinfo[61] << 16) + hdinfo[60]; kprintf("HD size:%dMB\n", sectors * 512 / 1000000); } /***************************************************************************** * hd_cmd_out *****************************************************************************/ /** * Output a command to HD controller. * * @param cmd The command struct ptr. *****************************************************************************/ static void hd_cmd_out(struct hd_cmd* cmd) { /** * For all commands, the host must first check if BSY=1, * and should proceed no further unless and until BSY=0 */ if (!waitfor(STATUS_BSY, 0, HD_TIMEOUT)) panic("hd error."); /* Activate the Interrupt Enable (nIEN) bit */ outb(REG_DEV_CTRL, 0); /* Load required parameters in the Command Block Registers */ outb(REG_FEATURES, cmd->features); outb(REG_NSECTOR, cmd->count); outb(REG_LBA_LOW, cmd->lba_low); outb(REG_LBA_MID, cmd->lba_mid); outb(REG_LBA_HIGH, cmd->lba_high); outb(REG_DEVICE, cmd->device); /* Write the command code to the Command Register */ outb(REG_CMD, cmd->command); } /***************************************************************************** * interrupt_wait *****************************************************************************/ /** * Wait until a disk interrupt occurs. * *****************************************************************************/ static void interrupt_wait() { while(hd_int_waiting_flag) { } hd_int_waiting_flag = 1; } /***************************************************************************** * waitfor *****************************************************************************/ /** * Wait for a certain status. * * @param mask Status mask. * @param val Required status. * @param timeout Timeout in milliseconds. * * @return One if sucess, zero if timeout. *****************************************************************************/ static int waitfor(int mask, int val, int timeout) { int t = sys_get_ticks(); while(((sys_get_ticks() - t) * 1000 / HZ) < timeout){ if ((inb(REG_STATUS) & mask) == val) return 1; } return 0; } /***************************************************************************** * hd_handler *****************************************************************************/ /** * Interrupt handler. * * @param irq IRQ nr of the disk interrupt. *****************************************************************************/ static void hd_handler(int irq) { /* * Interrupts are cleared when the host * - reads the Status Register, * - issues a reset, or * - writes to the Command Register. */ hd_status = inb(REG_STATUS); inform_int(); /* There is two stages - in kernel intializing or in process running. * Some operation shouldn't be valid in kernel intializing stage. * added by xw, 18/6/1 */ if(kernel_initial == 1){ return; } //some operation only for process return; } /***************************************************************************** * inform_int *****************************************************************************/ static void inform_int() { hd_int_waiting_flag = 0; return; }