前篇只是完成 DEV_OPEN,必須完成後續的 DEV_READ 和 DEV_WRITE,以利檔案的操作。
「檔案系統」需要:
- 有地方存放 Metadata-super block
- 有地方紀錄磁區的使用情況-sector map
- 有地方來紀錄任一檔的資訊,比如佔用哪些磁區等-i-node map、inode_array (存放真正的 i-node)
- 有地方存放檔的索引-root 資料區
 |
檔案系統 |
程式碼
fs/main.c
/*************************************************************************//**
*****************************************************************************
* @file main.c
* @brief
* @author Forrest Y. Yu
* @date 2007
*****************************************************************************
*****************************************************************************/
#include "type.h"
//#include "config.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"
#include "hd.h"
/*****************************************************************************
* task_fs
*****************************************************************************/
/**
* <Ring 1> The main loop of TASK FS.
*
*****************************************************************************/
PUBLIC void task_fs()
{
printl("Task FS begins.\n");
/* open the device: hard disk */
MESSAGE driver_msg;
driver_msg.type = DEV_OPEN;
send_recv(BOTH, TASK_HD, &driver_msg);
spin("FS");
}
include/const.h
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
const.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#ifndef _ORANGES_CONST_H_
#define _ORANGES_CONST_H_
/* the assert macro */
#define ASSERT
#ifdef ASSERT
void assertion_failure(char *exp, char *file, char *base_file, int line);
#define assert(exp) if (exp) ; \
else assertion_failure(#exp, __FILE__, __BASE_FILE__, __LINE__)
#else
#define assert(exp)
#endif
/* EXTERN */
#define EXTERN extern /* EXTERN is defined as extern except in global.c */
/* 函數類型 */
#define PUBLIC /* PUBLIC is the opposite of PRIVATE */
#define PRIVATE static /* PRIVATE x limits the scope of x */
#define STR_DEFAULT_LEN 1024
/* max() & min() */
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
/* Color */
/*
* e.g. MAKE_COLOR(BLUE, RED)
* MAKE_COLOR(BLACK, RED) | BRIGHT
* MAKE_COLOR(BLACK, RED) | BRIGHT | FLASH
*/
#define BLACK 0x0 /* 0000 */
#define WHITE 0x7 /* 0111 */
#define RED 0x4 /* 0100 */
#define GREEN 0x2 /* 0010 */
#define BLUE 0x1 /* 0001 */
#define FLASH 0x80 /* 1000 0000 */
#define BRIGHT 0x08 /* 0000 1000 */
#define MAKE_COLOR(x,y) ((x<<4) | y) /* MAKE_COLOR(Background,Foreground) */
/* GDT 和 IDT 中描述符的個數 */
#define GDT_SIZE 128
#define IDT_SIZE 256
/* 權限 */
#define PRIVILEGE_KRNL 0
#define PRIVILEGE_TASK 1
#define PRIVILEGE_USER 3
/* RPL */
#define RPL_KRNL SA_RPL0
#define RPL_TASK SA_RPL1
#define RPL_USER SA_RPL3
/* Process */
#define SENDING 0x02 /* set when proc trying to send */
#define RECEIVING 0x04 /* set when proc trying to recv */
/* TTY */
#define NR_CONSOLES 3 /* consoles */
/* 8259A interrupt controller ports. */
#define INT_M_CTL 0x20 /* I/O port for interrupt controller <Master> */
#define INT_M_CTLMASK 0x21 /* setting bits in this port disables ints <Master> */
#define INT_S_CTL 0xA0 /* I/O port for second interrupt controller <Slave> */
#define INT_S_CTLMASK 0xA1 /* setting bits in this port disables ints <Slave> */
/* 8253/8254 PIT (Programmable Interval Timer) */
#define TIMER0 0x40 /* I/O port for timer channel 0 */
#define TIMER_MODE 0x43 /* I/O port for timer mode control */
#define RATE_GENERATOR 0x34 /* 00-11-010-0 :
* Counter0 - LSB then MSB - rate generator - binary
*/
#define TIMER_FREQ 1193182L/* clock frequency for timer in PC and AT */
#define HZ 100 /* clock freq (software settable on IBM-PC) */
/* AT keyboard */
/* 8042 ports */
#define KB_DATA 0x60 /* I/O port for keyboard data
Read : Read Output Buffer
Write: Write Input Buffer(8042 Data&8048 Command) */
#define KB_CMD 0x64 /* I/O port for keyboard command
Read : Read Status Register
Write: Write Input Buffer(8042 Command) */
#define LED_CODE 0xED
#define KB_ACK 0xFA
/* VGA */
#define CRTC_ADDR_REG 0x3D4 /* CRT Controller Registers - Addr Register */
#define CRTC_DATA_REG 0x3D5 /* CRT Controller Registers - Data Register */
#define START_ADDR_H 0xC /* reg index of video mem start addr (MSB) */
#define START_ADDR_L 0xD /* reg index of video mem start addr (LSB) */
#define CURSOR_H 0xE /* reg index of cursor position (MSB) */
#define CURSOR_L 0xF /* reg index of cursor position (LSB) */
#define V_MEM_BASE 0xB8000 /* base of color video memory */
#define V_MEM_SIZE 0x8000 /* 32K: B8000H -> BFFFFH */
/* Hardware interrupts */
#define NR_IRQ 16 /* Number of IRQs */
#define CLOCK_IRQ 0
#define KEYBOARD_IRQ 1
#define CASCADE_IRQ 2 /* cascade enable for 2nd AT controller */
#define ETHER_IRQ 3 /* default ethernet interrupt vector */
#define SECONDARY_IRQ 3 /* RS232 interrupt vector for port 2 */
#define RS232_IRQ 4 /* RS232 interrupt vector for port 1 */
#define XT_WINI_IRQ 5 /* xt winchester */
#define FLOPPY_IRQ 6 /* floppy disk */
#define PRINTER_IRQ 7
#define AT_WINI_IRQ 14 /* at winchester */
/* tasks */
/* 注意 TASK_XXX 的定義要與 global.c 中對應 */
#define INVALID_DRIVER -20
#define INTERRUPT -10
#define TASK_TTY 0
#define TASK_SYS 1
#define TASK_HD 2
/* #define TASK_FS 3 */
/* #define TASK_MM 4 */
#define ANY (NR_TASKS + NR_PROCS + 10)
#define NO_TASK (NR_TASKS + NR_PROCS + 20)
/* system call */
#define NR_SYS_CALL 3
/* ipc */
#define SEND 1
#define RECEIVE 2
#define BOTH 3 /* BOTH = (SEND | RECEIVE) */
/* magic chars used by `printx' */
#define MAG_CH_PANIC '\002'
#define MAG_CH_ASSERT '\003'
/**
* @enum msgtype
* @brief MESSAGE types
*/
enum msgtype {
/*
* when hard interrupt occurs, a msg (with type==HARD_INT) will
* be sent to some tasks
*/
HARD_INT = 1,
/* SYS task */
GET_TICKS,
/* message type for drivers */
DEV_OPEN = 1001,
DEV_CLOSE,
DEV_READ,
DEV_WRITE,
DEV_IOCTL
};
/* macros for messages */
/* #define FD u.m3.m3i1 */
/* #define PATHNAME u.m3.m3p1 */
/* #define FLAGS u.m3.m3i1 */
/* #define NAME_LEN u.m3.m3i2 */
#define CNT u.m3.m3i2
#define REQUEST u.m3.m3i2
#define PROC_NR u.m3.m3i3
#define DEVICE u.m3.m3i4
#define POSITION u.m3.m3l1
#define BUF u.m3.m3p2
/* #define OFFSET u.m3.m3i2 */
/* #define WHENCE u.m3.m3i3 */
/* #define PID u.m3.m3i2 */
/* #define STATUS u.m3.m3i1 */
#define RETVAL u.m3.m3i1
/* #define STATUS u.m3.m3i1 */
#define DIOCTL_GET_GEO 1
/* Hard Drive */
#define SECTOR_SIZE 512
#define SECTOR_BITS (SECTOR_SIZE * 8)
#define SECTOR_SIZE_SHIFT 9
/* major device numbers (corresponding to kernel/global.c::dd_map[]) */
#define NO_DEV 0
#define DEV_FLOPPY 1
#define DEV_CDROM 2
#define DEV_HD 3
#define DEV_CHAR_TTY 4
#define DEV_SCSI 5
/* make device number from major and minor numbers */
#define MAJOR_SHIFT 8
#define MAKE_DEV(a,b) ((a << MAJOR_SHIFT) | b)
/* separate major and minor numbers from device number */
#define MAJOR(x) ((x >> MAJOR_SHIFT) & 0xFF)
#define MINOR(x) (x & 0xFF)
/* device numbers of hard disk */
#define MINOR_hd1a 0x10
#define MINOR_hd2a 0x20
#define MINOR_hd2b 0x21
#define MINOR_hd3a 0x30
#define MINOR_hd4a 0x40
#define ROOT_DEV MAKE_DEV(DEV_HD, MINOR_BOOT) /* 3, 0x21 */
#define INVALID_INODE 0
#define ROOT_INODE 1
#define MAX_DRIVES 2
#define NR_PART_PER_DRIVE 4
#define NR_SUB_PER_PART 16
#define NR_SUB_PER_DRIVE (NR_SUB_PER_PART * NR_PART_PER_DRIVE)
#define NR_PRIM_PER_DRIVE (NR_PART_PER_DRIVE + 1)
/**
* @def MAX_PRIM_DEV
* Defines the max minor number of the primary partitions.
* If there are 2 disks, prim_dev ranges in hd[0-9], this macro will
* equals 9.
*/
#define MAX_PRIM (MAX_DRIVES * NR_PRIM_PER_DRIVE - 1)
#define MAX_SUBPARTITIONS (NR_SUB_PER_DRIVE * MAX_DRIVES)
#define P_PRIMARY 0
#define P_EXTENDED 1
#define ORANGES_PART 0x99 /* Orange'S partition */
#define NO_PART 0x00 /* unused entry */
#define EXT_PART 0x05 /* extended partition */
#define NR_FILES 64
#define NR_FILE_DESC 64 /* FIXME */
#define NR_INODE 64 /* FIXME */
#define NR_SUPER_BLOCK 8
/* INODE::i_mode (octal, lower 32 bits reserved) */
#define I_TYPE_MASK 0170000
#define I_REGULAR 0100000
#define I_BLOCK_SPECIAL 0060000
#define I_DIRECTORY 0040000
#define I_CHAR_SPECIAL 0020000
#define I_NAMED_PIPE 0010000
#define is_special(m) ((((m) & I_TYPE_MASK) == I_BLOCK_SPECIAL) || \
(((m) & I_TYPE_MASK) == I_CHAR_SPECIAL))
#define NR_DEFAULT_FILE_SECTS 2048 /* 2048 * 512 = 1MB */
#endif /* _ORANGES_CONST_H_ */
include/fs.h
/*************************************************************************//**
*****************************************************************************
* @file include/sys/fs.h
* @brief Header file for File System.
* @author Forrest Yu
* @date 2008
*****************************************************************************
*****************************************************************************/
#ifndef _ORANGES_FS_H_
#define _ORANGES_FS_H_
/**
* @struct dev_drv_map fs.h "include/sys/fs.h"
* @brief The Device_nr.\ - Driver_nr.\ MAP.
*
* \dot
* digraph DD_MAP {
* graph[rankdir=LR];
* node [shape=record, fontname=Helvetica];
* b [ label="Device Nr."];
* c [ label="Driver (the task)"];
* b -> c [ label="DD_MAP", fontcolor=blue, URL="\ref DD_MAP", arrowhead="open", style="dashed" ];
* }
* \enddot
*/
struct dev_drv_map {
int driver_nr; /**< The proc nr.\ of the device driver. */
};
/**
* @def MAGIC_V1
* @brief Magic number of FS v1.0
*/
#define MAGIC_V1 0x111
/**
* @struct super_block fs.h "include/fs.h"
* @brief The 2nd sector of the FS
*
* Remember to change SUPER_BLOCK_SIZE if the members are changed.
*
* @attention Remember to change boot/include/load.inc::SB_* if the members
* are changed.
*
* \dot
* digraph super_block {
* node [shape=plaintext];
*
* sb [label=<<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0">
* <TR>
* <TD HEIGHT="20" WIDTH="70" ALIGN="right" VALIGN="top" BORDER="0">Sector # </TD>
* <TD HEIGHT="20" WIDTH="90" ALIGN="left" VALIGN="top" BORDER="0">0</TD>
* <TD HEIGHT="20" WIDTH="90" ALIGN="left" VALIGN="top" BORDER="0">1</TD>
* <TD HEIGHT="20" WIDTH="290" ALIGN="center" VALIGN="top" BORDER="0">...</TD>
* <TD HEIGHT="20" WIDTH="10" ALIGN="right" BORDER="0"></TD>
* </TR>
* <TR>
* <TD HEIGHT="30" WIDTH="70" ALIGN="right" BORDER="0"></TD>
* <TD HEIGHT="30" WIDTH="90" ALIGN="center" BORDER="1" BGCOLOR="white">Boot Sector</TD>
* <TD HEIGHT="30" WIDTH="90" ALIGN="center" BORDER="1" BGCOLOR="grey">Super Block</TD>
* <TD HEIGHT="30" WIDTH="290" ALIGN="center" BORDER="1" BGCOLOR="white">...</TD>
* <TD HEIGHT="30" WIDTH="10" ALIGN="right" BORDER="0"></TD>
* </TR>
* </TABLE>>];
* }
* \enddot
*/
struct super_block {
u32 magic; /**< Magic number */
u32 nr_inodes; /**< How many inodes */
u32 nr_sects; /**< How many sectors (including bit maps) */
u32 nr_imap_sects; /**< How many inode-map sectors */
u32 nr_smap_sects; /**< How many sector-map sectors */
u32 n_1st_sect; /**< Number of the 1st data sector */
u32 nr_inode_sects; /**< How many inode sectors */
u32 root_inode; /**< Inode nr of root directory */
u32 inode_size; /**< INODE_SIZE */
u32 inode_isize_off; /**< Offset of `struct inode::i_size' */
u32 inode_start_off; /**< Offset of `struct inode::i_start_sect' */
u32 dir_ent_size; /**< DIR_ENTRY_SIZE */
u32 dir_ent_inode_off;/**< Offset of `struct dir_entry::inode_nr' */
u32 dir_ent_fname_off;/**< Offset of `struct dir_entry::name' */
/*
* the following item(s) are only present in memory
*/
int sb_dev; /**< the super block's home device */
};
/**
* @def SUPER_BLK_MAGIC_V1
* @brief Magic number of super block, version 1.
* @attention It must correspond with boot/include/load.h::SB_MAGIC_V1
*/
#define SUPER_BLK_MAGIC_V1 0x111
/**
* @def SUPER_BLOCK_SIZE
* @brief The size of super block \b in \b the \b device.
*
* Note that this is the size of the struct in the device, \b NOT in memory.
* The size in memory is larger because of some more members.
*/
#define SUPER_BLOCK_SIZE 56
/**
* @struct inode
* @brief i-node
*
* The file, currently, have tree valid attributes only:
* - size
* - start_sect
* - nr_sects
*
* The \c start_sect and\c nr_sects locate the file in the device,
* and the size show how many bytes is used.
* If <tt> size < (nr_sects * SECTOR_SIZE) </tt>, the rest bytes
* are wasted and reserved for later writing.
*
* \b NOTE: Remember to change INODE_SIZE if the members are changed
*/
struct inode {
u32 i_mode; /**< Accsess mode. Unused currently */
u32 i_size; /**< File size */
u32 i_start_sect; /**< The first sector of the data */
u32 i_nr_sects; /**< How many sectors the file occupies */
u8 _unused[16]; /**< Stuff for alignment */
/* the following items are only present in memory */
int i_dev;
int i_cnt; /**< How many procs share this inode */
int i_num; /**< inode nr. */
};
/**
* @def INODE_SIZE
* @brief The size of i-node stored \b in \b the \b device.
*
* Note that this is the size of the struct in the device, \b NOT in memory.
* The size in memory is larger because of some more members.
*/
#define INODE_SIZE 32
/**
* @struct file_desc
* @brief File Descriptor
*/
struct file_desc {
int fd_mode; /**< R or W */
int fd_pos; /**< Current position for R/W. */
struct inode* fd_inode; /**< Ptr to the i-node */
};
/**
* Since all invocations of `rw_sector()' in FS look similar (most of the
* params are the same), we use this macro to make code more readable.
*
* Before I wrote this macro, I found almost every rw_sector invocation
* line matchs this emacs-style regex:
* `rw_sector(\([-a-zA-Z0-9_>\ \*()+.]+,\)\{3\}\ *SECTOR_SIZE,\ *TASK_FS,\ *fsbuf)'
*/
#define RD_SECT(dev,sect_nr) rw_sector(DEV_READ, \
dev, \
(sect_nr) * SECTOR_SIZE, \
SECTOR_SIZE, /* read one sector */ \
TASK_FS, \
fsbuf);
#define WR_SECT(dev,sect_nr) rw_sector(DEV_WRITE, \
dev, \
(sect_nr) * SECTOR_SIZE, \
SECTOR_SIZE, /* write one sector */ \
TASK_FS, \
fsbuf);
#endif /* _ORANGES_FS_H_ */
include/hd.h
/*************************************************************************//**
*****************************************************************************
* @file include/sys/hd.h
* @brief
* @author Forrest Y. Yu
* @date 2008
*****************************************************************************
*****************************************************************************/
#ifndef _ORANGES_HD_H_
#define _ORANGES_HD_H_
/**
* @struct part_ent
* @brief Partition Entry struct.
*
* <b>Master Boot Record (MBR):</b>
* Located at offset 0x1BE in the 1st sector of a disk. MBR contains
* four 16-byte partition entries. Should end with 55h & AAh.
*
* <b>partitions in MBR:</b>
* A PC hard disk can contain either as many as four primary partitions,
* or 1-3 primaries and a single extended partition. Each of these
* partitions are described by a 16-byte entry in the Partition Table
* which is located in the Master Boot Record.
*
* <b>extented partition:</b>
* It is essentially a link list with many tricks. See
* http://en.wikipedia.org/wiki/Extended_boot_record for details.
*/
struct part_ent {
u8 boot_ind; /**
* boot indicator
* Bit 7 is the active partition flag,
* bits 6-0 are zero (when not zero this
* byte is also the drive number of the
* drive to boot so the active partition
* is always found on drive 80H, the first
* hard disk).
*/
u8 start_head; /**
* Starting Head
*/
u8 start_sector; /**
* Starting Sector.
* Only bits 0-5 are used. Bits 6-7 are
* the upper two bits for the Starting
* Cylinder field.
*/
u8 start_cyl; /**
* Starting Cylinder.
* This field contains the lower 8 bits
* of the cylinder value. Starting cylinder
* is thus a 10-bit number, with a maximum
* value of 1023.
*/
u8 sys_id; /**
* System ID
* e.g.
* 01: FAT12
* 81: MINIX
* 83: Linux
*/
u8 end_head; /**
* Ending Head
*/
u8 end_sector; /**
* Ending Sector.
* Only bits 0-5 are used. Bits 6-7 are
* the upper two bits for the Ending
* Cylinder field.
*/
u8 end_cyl; /**
* Ending Cylinder.
* This field contains the lower 8 bits
* of the cylinder value. Ending cylinder
* is thus a 10-bit number, with a maximum
* value of 1023.
*/
u32 start_sect; /**
* starting sector counting from
* 0 / Relative Sector. / start in LBA
*/
u32 nr_sects; /**
* nr of sectors in partition
*/
} PARTITION_ENTRY;
/********************************************/
/* I/O Ports used by hard disk controllers. */
/********************************************/
/* slave disk not supported yet, all master registers below */
/* Command Block Registers */
/* MACRO PORT DESCRIPTION INPUT/OUTPUT */
/* ----- ---- ----------- ------------ */
#define REG_DATA 0x1F0 /* Data I/O */
#define REG_FEATURES 0x1F1 /* Features O */
#define REG_ERROR REG_FEATURES /* Error I */
/* The contents of this register are valid only when the error bit
(ERR) in the Status Register is set, except at drive power-up or at the
completion of the drive's internal diagnostics, when the register
contains a status code.
When the error bit (ERR) is set, Error Register bits are interpreted as such:
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
| BRK | UNC | | IDNF| | ABRT|TKONF| AMNF|
+-----+-----+-----+-----+-----+-----+-----+-----+
| | | | | | | |
| | | | | | | `--- 0. Data address mark not found after correct ID field found
| | | | | | `--------- 1. Track 0 not found during execution of Recalibrate command
| | | | | `--------------- 2. Command aborted due to drive status error or invalid command
| | | | `--------------------- 3. Not used
| | | `--------------------------- 4. Requested sector's ID field not found
| | `--------------------------------- 5. Not used
| `--------------------------------------- 6. Uncorrectable data error encountered
`--------------------------------------------- 7. Bad block mark detected in the requested sector's ID field
*/
#define REG_NSECTOR 0x1F2 /* Sector Count I/O */
#define REG_LBA_LOW 0x1F3 /* Sector Number / LBA Bits 0-7 I/O */
#define REG_LBA_MID 0x1F4 /* Cylinder Low / LBA Bits 8-15 I/O */
#define REG_LBA_HIGH 0x1F5 /* Cylinder High / LBA Bits 16-23 I/O */
#define REG_DEVICE 0x1F6 /* Drive | Head | LBA bits 24-27 I/O */
/* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
| 1 | L | 1 | DRV | HS3 | HS2 | HS1 | HS0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
| | \_____________________/
| | |
| | `------------ If L=0, Head Select.
| | These four bits select the head number.
| | HS0 is the least significant.
| | If L=1, HS0 through HS3 contain bit 24-27 of the LBA.
| `--------------------------- Drive. When DRV=0, drive 0 (master) is selected.
| When DRV=1, drive 1 (slave) is selected.
`--------------------------------------- LBA mode. This bit selects the mode of operation.
When L=0, addressing is by 'CHS' mode.
When L=1, addressing is by 'LBA' mode.
*/
#define REG_STATUS 0x1F7 /* Status I */
/* Any pending interrupt is cleared whenever this register is read.
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
| BSY | DRDY|DF/SE| # | DRQ | | | ERR |
+-----+-----+-----+-----+-----+-----+-----+-----+
| | | | | | | |
| | | | | | | `--- 0. Error.(an error occurred)
| | | | | | `--------- 1. Obsolete.
| | | | | `--------------- 2. Obsolete.
| | | | `--------------------- 3. Data Request. (ready to transfer data)
| | | `--------------------------- 4. Command dependent. (formerly DSC bit)
| | `--------------------------------- 5. Device Fault / Stream Error.
| `--------------------------------------- 6. Drive Ready.
`--------------------------------------------- 7. Busy. If BSY=1, no other bits in the register are valid.
*/
#define STATUS_BSY 0x80
#define STATUS_DRDY 0x40
#define STATUS_DFSE 0x20
#define STATUS_DSC 0x10
#define STATUS_DRQ 0x08
#define STATUS_CORR 0x04
#define STATUS_IDX 0x02
#define STATUS_ERR 0x01
#define REG_CMD REG_STATUS /* Command O */
/*
+--------+---------------------------------+-----------------+
| Command| Command Description | Parameters Used |
| Code | | PC SC SN CY DH |
+--------+---------------------------------+-----------------+
| ECh @ | Identify Drive | D |
| 91h | Initialize Drive Parameters | V V |
| 20h | Read Sectors With Retry | V V V V |
| E8h @ | Write Buffer | D |
+--------+---------------------------------+-----------------+
KEY FOR SYMBOLS IN THE TABLE:
===========================================-----=========================================================================
PC Register 1F1: Write Precompensation @ These commands are optional and may not be supported by some drives.
SC Register 1F2: Sector Count D Only DRIVE parameter is valid, HEAD parameter is ignored.
SN Register 1F3: Sector Number D+ Both drives execute this command regardless of the DRIVE parameter.
CY Register 1F4+1F5: Cylinder low + high V Indicates that the register contains a valid paramterer.
DH Register 1F6: Drive / Head
*/
/* Control Block Registers */
/* MACRO PORT DESCRIPTION INPUT/OUTPUT */
/* ----- ---- ----------- ------------ */
#define REG_DEV_CTRL 0x3F6 /* Device Control O */
/* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
| HOB | - | - | - | - |SRST |-IEN | 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
| | |
| | `--------- Interrupt Enable.
| | - IEN=0, and the drive is selected,
| | drive interrupts to the host will be enabled.
| | - IEN=1, or the drive is not selected,
| | drive interrupts to the host will be disabled.
| `--------------- Software Reset.
| - The drive is held reset when RST=1.
| Setting RST=0 re-enables the drive.
| - The host must set RST=1 and wait for at least
| 5 microsecondsbefore setting RST=0, to ensure
| that the drive recognizes the reset.
`--------------------------------------------- HOB (High Order Byte)
- defined by 48-bit Address feature set.
*/
#define REG_ALT_STATUS REG_DEV_CTRL /* Alternate Status I */
/* This register contains the same information as the Status Register.
The only difference is that reading this register does not imply interrupt acknowledge or clear a pending interrupt.
*/
#define REG_DRV_ADDR 0x3F7 /* Drive Address I */
struct hd_cmd {
u8 features;
u8 count;
u8 lba_low;
u8 lba_mid;
u8 lba_high;
u8 device;
u8 command;
};
struct part_info {
u32 base; /* # of start sector (NOT byte offset, but SECTOR) */
u32 size; /* how many sectors in this partition (NOT byte size, but SECTOR number) */
};
/* main drive struct, one entry per drive */
struct hd_info
{
/* int cylinders; */
/* int heads; */
/* int sectors; */
/* int precomp; */
/* int lzone; */
/* int ctl; */
int open_cnt;
struct part_info primary[NR_PRIM_PER_DRIVE];
struct part_info logical[NR_SUB_PER_DRIVE];
};
/***************/
/* DEFINITIONS */
/***************/
#define HD_TIMEOUT 10000 /* in millisec */
#define PARTITION_TABLE_OFFSET 0x1BE
#define ATA_IDENTIFY 0xEC
#define ATA_READ 0x20
#define ATA_WRITE 0x30
/* for DEVICE register. */
#define MAKE_DEVICE_REG(lba,drv,lba_highest) (((lba) << 6) | \
((drv) << 4) | \
(lba_highest & 0xF) | 0xA0)
#endif /* _ORANGES_HD_H_ */
kernel/hd.c
/*************************************************************************//**
*****************************************************************************
* @file hd.c
* @brief HD driver.
* @author Forrest Y. Yu
* @date 2005~2008
*****************************************************************************
*****************************************************************************/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"
#include "hd.h"
PRIVATE void init_hd ();
PRIVATE void hd_cmd_out (struct hd_cmd* cmd);
PRIVATE int waitfor (int mask, int val, int timeout);
PRIVATE void interrupt_wait ();
PRIVATE void hd_identify (int drive);
PRIVATE void print_identify_info (u16* hdinfo);
PRIVATE u8 hd_status;
PRIVATE u8 hdbuf[SECTOR_SIZE * 2];
/*****************************************************************************
* task_hd
*****************************************************************************/
/**
* Main loop of HD driver.
*
*****************************************************************************/
PUBLIC void task_hd()
{
MESSAGE msg;
init_hd();
while (1) {
send_recv(RECEIVE, ANY, &msg);
int src = msg.source;
switch (msg.type) {
case DEV_OPEN:
hd_identify(0);
break;
default:
dump_msg("HD driver::unknown msg", &msg);
spin("FS::main_loop (invalid msg.type)");
break;
}
send_recv(SEND, src, &msg);
}
}
/*****************************************************************************
* init_hd
*****************************************************************************/
/**
* <Ring 1> Check hard drive, set IRQ handler, enable IRQ and initialize data
* structures.
*****************************************************************************/
PRIVATE void init_hd()
{
/* Get the number of drives from the BIOS data area */
u8 * pNrDrives = (u8*)(0x475);
printl("NrDrives:%d.\n", *pNrDrives);
assert(*pNrDrives);
put_irq_handler(AT_WINI_IRQ, hd_handler);
enable_irq(CASCADE_IRQ);
enable_irq(AT_WINI_IRQ);
}
/*****************************************************************************
* hd_identify
*****************************************************************************/
/**
* <Ring 1> Get the disk information.
*
* @param drive Drive Nr.
*****************************************************************************/
PRIVATE 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();
port_read(REG_DATA, hdbuf, SECTOR_SIZE);
print_identify_info((u16*)hdbuf);
}
/*****************************************************************************
* print_identify_info
*****************************************************************************/
/**
* <Ring 1> Print the hdinfo retrieved via ATA_IDENTIFY command.
*
* @param hdinfo The buffer read from the disk i/o port.
*****************************************************************************/
PRIVATE 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;
printl("%s: %s\n", iinfo[k].desc, s);
}
int capabilities = hdinfo[49];
printl("LBA supported: %s\n",
(capabilities & 0x0200) ? "Yes" : "No");
int cmd_set_supported = hdinfo[83];
printl("LBA48 supported: %s\n",
(cmd_set_supported & 0x0400) ? "Yes" : "No");
int sectors = ((int)hdinfo[61] << 16) + hdinfo[60];
printl("HD size: %dMB\n", sectors * 512 / 1000000);
}
/*****************************************************************************
* hd_cmd_out
*****************************************************************************/
/**
* <Ring 1> Output a command to HD controller.
*
* @param cmd The command struct ptr.
*****************************************************************************/
PRIVATE 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 */
out_byte(REG_DEV_CTRL, 0);
/* Load required parameters in the Command Block Registers */
out_byte(REG_FEATURES, cmd->features);
out_byte(REG_NSECTOR, cmd->count);
out_byte(REG_LBA_LOW, cmd->lba_low);
out_byte(REG_LBA_MID, cmd->lba_mid);
out_byte(REG_LBA_HIGH, cmd->lba_high);
out_byte(REG_DEVICE, cmd->device);
/* Write the command code to the Command Register */
out_byte(REG_CMD, cmd->command);
}
/*****************************************************************************
* interrupt_wait
*****************************************************************************/
/**
* <Ring 1> Wait until a disk interrupt occurs.
*
*****************************************************************************/
PRIVATE void interrupt_wait()
{
MESSAGE msg;
send_recv(RECEIVE, INTERRUPT, &msg);
}
/*****************************************************************************
* waitfor
*****************************************************************************/
/**
* <Ring 1> 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.
*****************************************************************************/
PRIVATE int waitfor(int mask, int val, int timeout)
{
int t = get_ticks();
while(((get_ticks() - t) * 1000 / HZ) < timeout)
if ((in_byte(REG_STATUS) & mask) == val)
return 1;
return 0;
}
/*****************************************************************************
* hd_handler
*****************************************************************************/
/**
* <Ring 0> Interrupt handler.
*
* @param irq IRQ nr of the disk interrupt.
*****************************************************************************/
PUBLIC 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 = in_byte(REG_STATUS);
inform_int(TASK_HD);
}
include/global.h
extern u8 * fsbuf; // 新增
extern const int FSBUF_SIZE; // 新增
include/proto.h
PUBLIC int rw_sector(int io_type, int dev, u64 pos, int bytes, int proc_nr, void * buf); // 新增
 |
執行結果 |
我們可將直接檢視 80m.vdi 的資料,以確認是否有正確寫入資料。首先,將 VDI 檔轉換為 RAW 檔,
$ VBoxManage clonehd -format RAW 80m.vdi t.img
並從上圖得知各欄位之起點為:
- Super block (sb): 0x1410000
- inode map (imap): 0x1410200
- sector map (smap): 0x1410400
- inode array (inodes): 0x1411000
- root (1st_sector): 0x1431000
可分別檢視其結果
$ xdd -u -a -g 1 -c 16 -s 0x1410000 -l 512 t.img
$ xdd -u -a -g 1 -c 16 -s 0x1410200 -l 512 t.img
$ xdd -u -a -g 1 -c 16 -s 0x1410400 -l 512 t.img
$ xdd -u -a -g 1 -c 16 -s 0x1411000 -l 512 t.img
$ xdd -u -a -g 1 -c 16 -s 0x1431000 -l 512 t.img
 |
inode array 與 root 的執行結果 |
目錄結構
.
├── 80m.img
├── 80m.vdi
├── a.img
├── boot
│ ├── boot.asm
│ ├── include
│ │ ├── fat12hdr.inc
│ │ ├── load.inc
│ │ └── pm.inc
│ └── loader.asm
├── fs
│ └── main.c
├── include
│ ├── config.h
│ ├── console.h
│ ├── const.h
│ ├── fs.h
│ ├── global.h
│ ├── hd.h
│ ├── keyboard.h
│ ├── keymap.h
│ ├── proc.h
│ ├── protect.h
│ ├── proto.h
│ ├── sconst.inc
│ ├── string.h
│ ├── tty.h
│ └── type.h
├── kernel
│ ├── clock.c
│ ├── console.c
│ ├── global.c
│ ├── hd.c
│ ├── i8259.c
│ ├── kernel.asm
│ ├── keyboard.c
│ ├── main.c
│ ├── printf.c
│ ├── proc.c
│ ├── protect.c
│ ├── start.c
│ ├── syscall.asm
│ ├── systask.c
│ ├── tty.c
│ └── vsprintf.c
├── lib
│ ├── kliba.asm
│ ├── klib.c
│ ├── misc.c
│ └── string.asm
└── Makefile
留言
張貼留言