From df1e857ac4d1410ae2bd354e361210b842ab7bc8 Mon Sep 17 00:00:00 2001
From: Minep
Date: Thu, 10 Nov 2022 22:15:09 +0000
Subject: [PATCH 01/16] fix: (blkio) enforce disk io buffer size alignment (to
block size) fix: (blkio) handle buffer smaller than block size fix: (ahci)
handle the hba interrupt as per spec refactor: (vecbuf) a more compact
interface
---
lunaix-os/hal/ahci/ahci.c | 2 +-
lunaix-os/hal/ahci/atapi.c | 2 +-
lunaix-os/hal/ahci/io_event.c | 48 ++++++++++++++++++++++++------
lunaix-os/includes/hal/ahci/hba.h | 14 +++++++--
lunaix-os/includes/lunaix/buffer.h | 2 +-
lunaix-os/kernel/block/blkio.c | 2 --
lunaix-os/kernel/block/block.c | 45 ++++++++++++++++------------
lunaix-os/kernel/ds/buffer.c | 10 ++++---
8 files changed, 85 insertions(+), 40 deletions(-)
diff --git a/lunaix-os/hal/ahci/ahci.c b/lunaix-os/hal/ahci/ahci.c
index b9bca95..e07200e 100644
--- a/lunaix-os/hal/ahci/ahci.c
+++ b/lunaix-os/hal/ahci/ahci.c
@@ -27,7 +27,7 @@
#define HBA_FIS_SIZE 256
#define HBA_CLB_SIZE 1024
-#define HBA_MY_IE (HBA_PxINTR_DHR | HBA_PxINTR_TFEE | HBA_PxINTR_OFE)
+#define HBA_MY_IE (HBA_PxINTR_DHR | HBA_PxINTR_TFE | HBA_PxINTR_OF)
// #define DO_HBA_FULL_RESET
diff --git a/lunaix-os/hal/ahci/atapi.c b/lunaix-os/hal/ahci/atapi.c
index 26ad533..bf3e8c9 100644
--- a/lunaix-os/hal/ahci/atapi.c
+++ b/lunaix-os/hal/ahci/atapi.c
@@ -77,7 +77,7 @@ scsi_submit(struct hba_device* dev, struct blkio_req* io_req)
} else {
scsi_create_packet12((struct scsi_cdb12*)cdb,
write ? SCSI_WRITE_BLOCKS_12 : SCSI_READ_BLOCKS_12,
- io_req->blk_addr & -1,
+ io_req->blk_addr & (u32_t)-1,
count);
}
diff --git a/lunaix-os/hal/ahci/io_event.c b/lunaix-os/hal/ahci/io_event.c
index 632197c..e11016a 100644
--- a/lunaix-os/hal/ahci/io_event.c
+++ b/lunaix-os/hal/ahci/io_event.c
@@ -1,27 +1,54 @@
-#include
+#include
#include
#include
#include
+#include
-extern struct ahci_hba hba;
+LOG_MODULE("io_evt")
+
+extern struct llist_header ahcis;
void
__ahci_hba_isr(const isr_param* param)
{
+ struct ahci_hba* hba;
+ struct ahci_driver *pos, *n;
+ llist_for_each(pos, n, &ahcis, ahci_drvs)
+ {
+ if (pos->id == param->vector) {
+ hba = &pos->hba;
+ goto proceed;
+ }
+ }
+
+ return;
+
+proceed:
// ignore spurious interrupt
- if (!hba.base[HBA_RIS])
+ if (!hba->base[HBA_RIS])
return;
- u32_t port_num = 31 - __builtin_clz(hba.base[HBA_RIS]);
- struct hba_port* port = hba.ports[port_num];
+ u32_t port_num = 31 - __builtin_clz(hba->base[HBA_RIS]);
+ struct hba_port* port = hba->ports[port_num];
struct hba_cmd_context* cmdctx = &port->cmdctx;
- u32_t ci_filtered = port->regs[HBA_RPxCI] ^ cmdctx->tracked_ci;
+ u32_t processed = port->regs[HBA_RPxCI] ^ cmdctx->tracked_ci;
- if (!ci_filtered) {
+ sata_read_error(port);
+
+ // FIXME When error occurs, CI will not change. Need error recovery!
+ if (!processed) {
+ if (port->regs[HBA_RPxIS] & HBA_FATAL) {
+ // TODO perform error recovery
+ // This should include:
+ // 1. Discard all issued (but pending) requests (signaled as
+ // error)
+ // 2. Restart port
+ // Complete steps refer to AHCI spec 6.2.2.1
+ }
goto done;
}
- u32_t slot = 31 - __builtin_clz(ci_filtered);
+ u32_t slot = 31 - __builtin_clz(processed);
struct hba_cmd_state* cmdstate = cmdctx->issued[slot];
if (!cmdstate) {
@@ -29,17 +56,20 @@ __ahci_hba_isr(const isr_param* param)
}
struct blkio_req* ioreq = (struct blkio_req*)cmdstate->state_ctx;
- sata_read_error(port);
+
if ((port->device->last_result.status & HBA_PxTFD_ERR)) {
ioreq->errcode = port->regs[HBA_RPxTFD] & 0xffff;
ioreq->flags |= BLKIO_ERROR;
+ hba_clear_reg(port->regs[HBA_RPxSERR]);
}
+ blkio_schedule(ioreq->io_ctx);
blkio_complete(ioreq);
vfree(cmdstate->cmd_table);
done:
hba_clear_reg(port->regs[HBA_RPxIS]);
+ hba->base[HBA_RIS] &= ~(1 << (31 - port_num));
}
void
diff --git a/lunaix-os/includes/hal/ahci/hba.h b/lunaix-os/includes/hal/ahci/hba.h
index 287f868..9bebe8e 100644
--- a/lunaix-os/includes/hal/ahci/hba.h
+++ b/lunaix-os/includes/hal/ahci/hba.h
@@ -35,13 +35,21 @@
#define HBA_PxINTR_DMA (1 << 2)
#define HBA_PxINTR_DHR (1)
#define HBA_PxINTR_DPS (1 << 5)
-#define HBA_PxINTR_TFEE (1 << 30)
-#define HBA_PxINTR_OFE (1 << 24)
-#define HBA_PxINTR_IFE (1 << 27)
+#define HBA_PxINTR_TFE (1 << 30)
+#define HBA_PxINTR_HBF (1 << 29)
+#define HBA_PxINTR_HBD (1 << 28)
+#define HBA_PxINTR_IF (1 << 27)
+#define HBA_PxINTR_NIF (1 << 26)
+#define HBA_PxINTR_OF (1 << 24)
#define HBA_PxTFD_ERR (1)
#define HBA_PxTFD_BSY (1 << 7)
#define HBA_PxTFD_DRQ (1 << 3)
+#define HBA_FATAL \
+ (HBA_PxINTR_TFE | HBA_PxINTR_HBF | HBA_PxINTR_HBD | HBA_PxINTR_IF)
+
+#define HBA_NONFATAL (HBA_PxINTR_NIF | HBA_PxINTR_OF)
+
#define HBA_RGHC_ACHI_ENABLE (1 << 31)
#define HBA_RGHC_INTR_ENABLE (1 << 1)
#define HBA_RGHC_RESET 1
diff --git a/lunaix-os/includes/lunaix/buffer.h b/lunaix-os/includes/lunaix/buffer.h
index 851d568..07176cd 100644
--- a/lunaix-os/includes/lunaix/buffer.h
+++ b/lunaix-os/includes/lunaix/buffer.h
@@ -35,7 +35,7 @@ vbuf_free(struct vecbuf* vbuf);
* @return struct vecbuf*
*/
struct vecbuf*
-vbuf_alloc(struct vecbuf* vec, void* buf, size_t len);
+vbuf_alloc(struct vecbuf** vec, void* buf, size_t len);
static inline size_t
vbuf_size(struct vecbuf* vbuf)
diff --git a/lunaix-os/kernel/block/blkio.c b/lunaix-os/kernel/block/blkio.c
index 7cfc354..c5d688f 100644
--- a/lunaix-os/kernel/block/blkio.c
+++ b/lunaix-os/kernel/block/blkio.c
@@ -118,6 +118,4 @@ blkio_complete(struct blkio_req* req)
}
req->io_ctx->busy--;
-
- blkio_schedule(req->io_ctx);
}
\ No newline at end of file
diff --git a/lunaix-os/kernel/block/block.c b/lunaix-os/kernel/block/block.c
index dbdb95a..bcbc60f 100644
--- a/lunaix-os/kernel/block/block.c
+++ b/lunaix-os/kernel/block/block.c
@@ -55,19 +55,22 @@ __block_read(struct device* dev, void* buf, size_t offset, size_t len)
return 0;
}
- struct vecbuf* vbuf = vbuf_alloc(NULL, buf, len);
+ struct vecbuf* vbuf = NULL;
struct blkio_req* req;
- void* tmp_buf = NULL;
+ void *head_buf = NULL, *tail_buf = NULL;
- if (r) {
- tmp_buf = vzalloc(bsize);
+ // align the boundary
+ if (r || len < bsize) {
+ head_buf = valloc(bsize);
rd_size = MIN(len, bsize - r);
- vbuf->buf.size = bsize;
- vbuf->buf.buffer = tmp_buf;
+ vbuf_alloc(&vbuf, head_buf, bsize);
+ }
- if ((len - rd_size)) {
- vbuf_alloc(vbuf, buf + rd_size, len - rd_size);
- }
+ // align the length
+ if ((len - rd_size)) {
+ size_t llen = len - rd_size;
+ assert_msg(!(llen % bsize), "misalign block read");
+ vbuf_alloc(&vbuf, buf + rd_size, llen);
}
req = blkio_vrd(vbuf, rd_block, NULL, NULL, 0);
@@ -76,14 +79,14 @@ __block_read(struct device* dev, void* buf, size_t offset, size_t len)
pwait(&req->wait);
if (!(errno = req->errcode)) {
- memcpy(buf, tmp_buf + r, rd_size);
+ memcpy(buf, head_buf + r, rd_size);
errno = len;
} else {
errno = -errno;
}
- if (tmp_buf) {
- vfree(tmp_buf);
+ if (head_buf) {
+ vfree(head_buf);
}
blkio_free_req(req);
@@ -96,24 +99,28 @@ __block_write(struct device* dev, void* buf, size_t offset, size_t len)
{
struct block_dev* bdev = (struct block_dev*)dev->underlay;
size_t bsize = bdev->blk_size, rd_block = offset / bsize + bdev->start_lba,
- r = offset % bsize;
+ r = offset % bsize, wr_size = 0;
if (!(len = MIN(len, ((size_t)bdev->end_lba - rd_block + 1) * bsize))) {
return 0;
}
- struct vecbuf* vbuf = vbuf_alloc(NULL, buf, len);
+ struct vecbuf* vbuf = NULL;
struct blkio_req* req;
void* tmp_buf = NULL;
if (r) {
- size_t rd_size = MIN(len, bsize - r);
+ size_t wr_size = MIN(len, bsize - r);
tmp_buf = vzalloc(bsize);
- vbuf->buf.size = bsize;
- vbuf->buf.buffer = tmp_buf;
+ vbuf_alloc(&vbuf, tmp_buf, bsize);
+
+ memcpy(tmp_buf + r, buf, wr_size);
+ }
- memcpy(tmp_buf + r, buf, rd_size);
- vbuf_alloc(vbuf, buf + rd_size, len - rd_size);
+ if ((len - wr_size)) {
+ size_t llen = len - wr_size;
+ assert_msg(!(llen % bsize), "misalign block write");
+ vbuf_alloc(&vbuf, buf + wr_size, llen);
}
req = blkio_vwr(vbuf, rd_block, NULL, NULL, 0);
diff --git a/lunaix-os/kernel/ds/buffer.c b/lunaix-os/kernel/ds/buffer.c
index 7b277c8..c40ed51 100644
--- a/lunaix-os/kernel/ds/buffer.c
+++ b/lunaix-os/kernel/ds/buffer.c
@@ -2,17 +2,19 @@
#include
struct vecbuf*
-vbuf_alloc(struct vecbuf* vec, void* buf, size_t size)
+vbuf_alloc(struct vecbuf** vec, void* buf, size_t size)
{
struct vecbuf* vbuf = valloc(sizeof(struct vecbuf));
+ struct vecbuf* _vec = *vec;
*vbuf = (struct vecbuf){ .buf = { .buffer = buf, .size = size },
- .acc_sz = vbuf_size(vec) + size };
+ .acc_sz = vbuf_size(_vec) + size };
- if (vec) {
- llist_append(&vec->components, &vbuf->components);
+ if (_vec) {
+ llist_append(&_vec->components, &vbuf->components);
} else {
llist_init_head(&vbuf->components);
+ *vec = vbuf;
}
return vbuf;
--
2.27.0
From c6de44b989e9d7286337e4cbf4d82d919f9120e6 Mon Sep 17 00:00:00 2001
From: Minep
Date: Fri, 11 Nov 2022 19:50:54 +0000
Subject: [PATCH 02/16] feat: (iso9660) directory read support feat: (syslog)
syslog syscall for kernel-level logging in userspace feat: (blkio) block io
feature in sector level granularity refactor: (vfs) simplify some interfaces
refactor: (vfs) verbose logging on mount refactor: (lunaix) rename proc.h to
lunaix.h fix: regression chore: code clean up and add verbose logging message
---
lunaix-os/includes/lunaix/block.h | 11 ++
lunaix-os/includes/lunaix/clock.h | 9 +-
lunaix-os/includes/lunaix/fs.h | 1 +
lunaix-os/includes/lunaix/fs/iso9660.h | 143 ++++++++++++++---
.../includes/lunaix/{proc.h => lunaix.h} | 8 +-
lunaix-os/includes/lunaix/lxconsole.h | 3 +
lunaix-os/includes/lunaix/status.h | 1 +
lunaix-os/includes/lunaix/syscall.h | 3 +
lunaix-os/kernel/asm/x86/syscall.S | 1 +
lunaix-os/kernel/block/block.c | 69 +++++++-
lunaix-os/kernel/demos/dir_read.c | 2 +-
lunaix-os/kernel/demos/input_test.c | 2 +-
lunaix-os/kernel/demos/iotest.c | 2 +-
lunaix-os/kernel/demos/signal_demo.c | 2 +-
lunaix-os/kernel/demos/simple_sh.c | 2 +-
lunaix-os/kernel/fs/fs_setup.c | 2 +
lunaix-os/kernel/fs/fsm.c | 3 +
lunaix-os/kernel/fs/iso9660/directory.c | 148 ++++++++++++++++++
lunaix-os/kernel/fs/iso9660/file.c | 37 +++++
lunaix-os/kernel/fs/iso9660/inode.c | 72 +++++++++
lunaix-os/kernel/fs/iso9660/mount.c | 108 +++++++++++++
lunaix-os/kernel/fs/iso9660/utils.c | 24 +++
lunaix-os/kernel/fs/mount.c | 23 +++
lunaix-os/kernel/fs/ramfs/ramfs.c | 2 +-
lunaix-os/kernel/fs/twifs/twifs.c | 1 -
lunaix-os/kernel/k_init.c | 4 +-
lunaix-os/kernel/kprintf.c | 41 +++--
lunaix-os/kernel/lxinit.c | 2 +-
lunaix-os/kernel/proc0.c | 22 ++-
lunaix-os/kernel/tty/lxconsole.c | 4 +
30 files changed, 698 insertions(+), 54 deletions(-)
rename lunaix-os/includes/lunaix/{proc.h => lunaix.h} (60%)
create mode 100644 lunaix-os/kernel/fs/iso9660/directory.c
create mode 100644 lunaix-os/kernel/fs/iso9660/file.c
create mode 100644 lunaix-os/kernel/fs/iso9660/inode.c
create mode 100644 lunaix-os/kernel/fs/iso9660/mount.c
create mode 100644 lunaix-os/kernel/fs/iso9660/utils.c
diff --git a/lunaix-os/includes/lunaix/block.h b/lunaix-os/includes/lunaix/block.h
index 551bc22..c574dd3 100644
--- a/lunaix-os/includes/lunaix/block.h
+++ b/lunaix-os/includes/lunaix/block.h
@@ -9,6 +9,16 @@
#define PARTITION_NAME_SIZE 48
#define DEV_ID_SIZE 32
+struct block_dev;
+
+struct block_dev_ops
+{
+ int (*block_read)(struct block_dev*, void*, u64_t, size_t);
+ int (*block_write)(struct block_dev*, void*, u64_t, size_t);
+ void* (*block_alloc)(struct block_dev*);
+ void (*block_free)(struct block_dev*, void*);
+};
+
struct block_dev
{
struct llist_header parts;
@@ -20,6 +30,7 @@ struct block_dev
u64_t start_lba;
u64_t end_lba;
u32_t blk_size;
+ struct block_dev_ops ops;
};
// Lunaix Partition Table
diff --git a/lunaix-os/includes/lunaix/clock.h b/lunaix-os/includes/lunaix/clock.h
index 44dfa04..8c9ab15 100644
--- a/lunaix-os/includes/lunaix/clock.h
+++ b/lunaix-os/includes/lunaix/clock.h
@@ -1,7 +1,7 @@
#ifndef __LUNAIX_CLOCK_H
#define __LUNAIX_CLOCK_H
-#include
+#include
typedef uint32_t time_t;
@@ -44,4 +44,11 @@ clock_tounixtime(datetime_t* dt)
(dt->minute - 1) * 60u + dt->second;
}
+static inline time_t
+time_tounix(u32_t yyyy, u32_t mm, u32_t dd, u32_t hh, u32_t MM, u32_t ss)
+{
+ return (yyyy - 1970) * 31556926u + (mm - 1) * 2629743u + (dd - 1) * 86400u +
+ (hh - 1) * 3600u + (MM - 1) * 60u + ss;
+}
+
#endif /* __LUNAIX_CLOCK_H */
diff --git a/lunaix-os/includes/lunaix/fs.h b/lunaix-os/includes/lunaix/fs.h
index 4f57e08..cc850ad 100644
--- a/lunaix-os/includes/lunaix/fs.h
+++ b/lunaix-os/includes/lunaix/fs.h
@@ -97,6 +97,7 @@ struct v_superblock
struct filesystem* fs;
uint32_t iobuf_size;
struct hbucket* i_cache;
+ void* data;
struct
{
uint32_t (*read_capacity)(struct v_superblock* vsb);
diff --git a/lunaix-os/includes/lunaix/fs/iso9660.h b/lunaix-os/includes/lunaix/fs/iso9660.h
index edccadc..de433d4 100644
--- a/lunaix-os/includes/lunaix/fs/iso9660.h
+++ b/lunaix-os/includes/lunaix/fs/iso9660.h
@@ -12,8 +12,13 @@
#ifndef __LUNAIX_ISO9660_H
#define __LUNAIX_ISO9660_H
+#include
+#include
#include
+#define ISO_SIGNATURE_LO 0x30304443UL
+#define ISO_SIGNATURE_HI 0x31
+
// Volume Types
#define ISO_VOLBOOT 0 // Boot Record
#define ISO_VOLPRIM 1 // Primary
@@ -28,6 +33,9 @@
#define ISO_FPROTECT 0x10 // file being protected by access control
#define ISO_FEXTENTS 0x80 // the extent by this record is a file partial
+#define ISO9660_BLKSZ 2048
+#define ISO9660_IDLEN 256
+
// NOTES:
// Each Descriptor sized 1 logical block (2048 bytes in common cases)
// ISO9660 store number in both-byte order. That is, for a d-bits number, it
@@ -61,6 +69,20 @@ struct iso_datetime
u8_t gmt;
} PACKED;
+// 32bits both-byte-order integer
+typedef struct iso_bbo32
+{
+ u32_t le; // little-endian
+ u32_t be; // big-endian
+} PACKED iso_bbo32_t;
+
+// 16bits both-byte-order integer
+typedef struct iso_bbo16
+{
+ u16_t le; // little-endian
+ u16_t be; // big-endian
+} PACKED iso_bbo16_t;
+
// (8.4) Describe a primary volume space
struct iso_vol_primary
{
@@ -68,17 +90,15 @@ struct iso_vol_primary
u8_t reserved_1;
u8_t sys_id[32];
u8_t vol_id[32];
- u8_t reserved_2;
- u32_t sz_lo; // (8.4.8) only lower portion is valid.
- u32_t sz_hi;
- u8_t reserved_2;
- u32_t set_size;
- u32_t seq_num;
- u32_t lb_size;
- u32_t path_tbl_sz_lo; // lower partition - LE.
- u32_t path_tbl_sz_hi;
+ u8_t reserved_2[8];
+ iso_bbo32_t vol_size;
+ u8_t reserved_3[32];
+ iso_bbo16_t set_size;
+ iso_bbo16_t seq_num;
+ iso_bbo16_t lb_size;
+ iso_bbo32_t ptable_size;
u32_t lpath_tbl_ptr; // Type L Path table location (LBA)
- u32_t reserved_3[3]; // use type M if big endian machine.
+ u32_t reserved_4[3]; // use type M if big endian machine.
u8_t root_record[34];
u8_t set_id[128];
u8_t publisher_id[128];
@@ -104,10 +124,8 @@ struct iso_partition
u8_t reserved;
u8_t sys_id[32];
u8_t part_id[32];
- u32_t part_addr_lo; // (8.6.7) only lower portion is valid.
- u32_t part_addr_hi;
- u32_t part_sz_lo; // (8.6.8) only lower portion is valid.
- u32_t part_sz_hi;
+ iso_bbo32_t part_addr;
+ iso_bbo32_t part_size;
} PACKED;
// (6.10.4) MDU with variable record
@@ -121,14 +139,13 @@ struct iso_var_mdu
struct iso_drecord
{
u8_t xattr_len;
- u32_t extent_lo; // location of extent, lower 32 bits is valid.
- u32_t extent_hi;
- u32_t data_sz_lo; // size of extent, lower 32 bits is valid.
- u32_t data_sz_hi;
+ iso_bbo32_t extent_addr;
+ iso_bbo32_t data_size;
struct
{
u8_t year;
u8_t month;
+ u8_t day;
u8_t hour;
u8_t min;
u8_t sec;
@@ -137,11 +154,11 @@ struct iso_drecord
u8_t flags;
u8_t fu_sz; // size of file unit (FU)
u8_t gap_sz; // size of gap if FU is interleaved.
- u32_t vol_seq;
+ iso_bbo16_t vol_seq;
struct iso_var_mdu name;
} PACKED;
-// (9.4) Path Table Record. [Embedded into Variable MDU]
+// (9.4) L-Path Table Record. [Embedded into Variable MDU]
struct iso_precord
{
u8_t xattr_len;
@@ -152,8 +169,8 @@ struct iso_precord
struct iso_xattr
{
- u32_t owner;
- u32_t group;
+ iso_bbo16_t owner;
+ iso_bbo16_t group;
u16_t perm;
struct iso_datetime ctime;
struct iso_datetime mtime;
@@ -161,16 +178,94 @@ struct iso_xattr
struct iso_datetime ef_time;
u8_t record_fmt;
u8_t record_attr;
- u8_t record_len;
+ iso_bbo16_t record_len;
u32_t sys_id;
u8_t reserved1[64];
u8_t version;
u8_t len_esc;
u8_t reserved2[64];
- u32_t payload_sz;
+ iso_bbo16_t payload_sz;
u8_t payload[0];
// There is also a escape sequence after payload,
// It however marked as optional, hence we ignore it.
} PACKED;
+struct iso_ptable
+{
+ u32_t start_lba;
+ u32_t current_lba;
+ u32_t size;
+ u32_t range_lower;
+ u32_t range_upper;
+ void* ptable_part;
+};
+
+struct iso_inode
+{
+ time_t ctime;
+ time_t mtime;
+ u32_t record_fmt;
+ u32_t fu_size;
+ u32_t gap_size;
+ struct llist_header* drecaches;
+};
+
+struct iso_drecache
+{
+ struct llist_header caches;
+ u32_t extent_addr;
+ u32_t data_size;
+ u32_t xattr_len;
+ u32_t fu_size;
+ u32_t gap_size;
+ u32_t flags;
+ struct hstr name;
+ char name_val[ISO9660_IDLEN];
+};
+
+struct iso_superblock
+{
+ u32_t volume_size;
+ u32_t lb_size;
+};
+
+struct iso_drecord*
+iso9660_get_drecord(struct iso_var_mdu* drecord_mdu);
+
+int
+iso9660_fill_inode(struct v_inode* inode, struct iso_drecache* dir, int ino);
+
+time_t
+iso9660_dt2unix(struct iso_datetime* isodt);
+
+void
+iso9660_init();
+
+int
+iso9660_setup_dnode(struct v_dnode* dnode, struct v_inode* inode);
+
+void
+iso9660_fill_drecache(struct iso_drecache* cache, struct iso_drecord* drec);
+
+int
+iso9660_dir_lookup(struct v_inode* this, struct v_dnode* dnode);
+
+int
+iso9660_readdir(struct v_file* file, struct dir_context* dctx);
+
+int
+iso9660_open(struct v_inode* this, struct v_file* file);
+
+int
+iso9660_close(struct v_file* file);
+
+int
+iso9660_read(struct v_inode* inode, void* buffer, size_t len, size_t fpos);
+
+int
+iso9660_write(struct v_inode* inode, void* buffer, size_t len, size_t fpos);
+
+int
+iso9660_seek(struct v_inode* inode, size_t offset);
+
#endif /* __LUNAIX_ISO9660_H */
diff --git a/lunaix-os/includes/lunaix/proc.h b/lunaix-os/includes/lunaix/lunaix.h
similarity index 60%
rename from lunaix-os/includes/lunaix/proc.h
rename to lunaix-os/includes/lunaix/lunaix.h
index f4de8cf..adbde1d 100644
--- a/lunaix-os/includes/lunaix/proc.h
+++ b/lunaix-os/includes/lunaix/lunaix.h
@@ -1,5 +1,5 @@
-#ifndef __LUNAIX_SYS_H
-#define __LUNAIX_SYS_H
+#ifndef __LUNAIX_LUNAIX_H
+#define __LUNAIX_LUNAIX_H
#include
#include
@@ -12,4 +12,6 @@ __LXSYSCALL3(pid_t, waitpid, pid_t, pid, int*, status, int, options);
__LXSYSCALL(int, geterrno);
-#endif /* __LUNAIX_SYS_H */
+__LXSYSCALL2_VARG(void, syslog, int, level, const char*, fmt);
+
+#endif /* __LUNAIX_LUNAIX_H */
diff --git a/lunaix-os/includes/lunaix/lxconsole.h b/lunaix-os/includes/lunaix/lxconsole.h
index fbf59a4..197bc9e 100644
--- a/lunaix-os/includes/lunaix/lxconsole.h
+++ b/lunaix-os/includes/lunaix/lxconsole.h
@@ -11,6 +11,9 @@
void
lxconsole_init();
+void
+lxconsole_spawn_ttydev();
+
void
console_write_str(char* str);
diff --git a/lunaix-os/includes/lunaix/status.h b/lunaix-os/includes/lunaix/status.h
index cc105b4..f9b947d 100644
--- a/lunaix-os/includes/lunaix/status.h
+++ b/lunaix-os/includes/lunaix/status.h
@@ -29,5 +29,6 @@
#define ENOMEM LXOUTOFMEM
#define ENOTDEV -24
#define EOVERFLOW -25
+#define ENOTBLK -26
#endif /* __LUNAIX_CODE_H */
diff --git a/lunaix-os/includes/lunaix/syscall.h b/lunaix-os/includes/lunaix/syscall.h
index 6169781..82fc4b9 100644
--- a/lunaix-os/includes/lunaix/syscall.h
+++ b/lunaix-os/includes/lunaix/syscall.h
@@ -58,6 +58,8 @@
#define __SYSCALL_getpgid 49
#define __SYSCALL_setpgid 50
+#define __SYSCALL_syslog 51
+
#define __SYSCALL_MAX 0x100
#ifndef __ASM__
@@ -145,4 +147,5 @@ syscall_install();
___DOINT33(__SYSCALL_##name, rettype) \
}
#endif
+
#endif /* __LUNAIX_SYSCALL_H */
diff --git a/lunaix-os/kernel/asm/x86/syscall.S b/lunaix-os/kernel/asm/x86/syscall.S
index 34b4330..93ea1c7 100644
--- a/lunaix-os/kernel/asm/x86/syscall.S
+++ b/lunaix-os/kernel/asm/x86/syscall.S
@@ -58,6 +58,7 @@
.long __lxsys_ioctl
.long __lxsys_getpgid
.long __lxsys_setpgid /* 50 */
+ .long __lxsys_syslog
2:
.rept __SYSCALL_MAX - (2b - 1b)/4
.long 0
diff --git a/lunaix-os/kernel/block/block.c b/lunaix-os/kernel/block/block.c
index bcbc60f..494a977 100644
--- a/lunaix-os/kernel/block/block.c
+++ b/lunaix-os/kernel/block/block.c
@@ -98,10 +98,10 @@ int
__block_write(struct device* dev, void* buf, size_t offset, size_t len)
{
struct block_dev* bdev = (struct block_dev*)dev->underlay;
- size_t bsize = bdev->blk_size, rd_block = offset / bsize + bdev->start_lba,
+ size_t bsize = bdev->blk_size, wr_block = offset / bsize + bdev->start_lba,
r = offset % bsize, wr_size = 0;
- if (!(len = MIN(len, ((size_t)bdev->end_lba - rd_block + 1) * bsize))) {
+ if (!(len = MIN(len, ((size_t)bdev->end_lba - wr_block + 1) * bsize))) {
return 0;
}
@@ -123,7 +123,10 @@ __block_write(struct device* dev, void* buf, size_t offset, size_t len)
vbuf_alloc(&vbuf, buf + wr_size, llen);
}
- req = blkio_vwr(vbuf, rd_block, NULL, NULL, 0);
+ // FIXME race condition between blkio_commit and pwait.
+ // Consider: what if scheduler complete the request before process enter
+ // wait state?
+ req = blkio_vwr(vbuf, wr_block, NULL, NULL, 0);
blkio_commit(bdev->blkio, req);
pwait(&req->wait);
@@ -144,6 +147,64 @@ __block_write(struct device* dev, void* buf, size_t offset, size_t len)
return errno;
}
+int
+__block_rd_lb(struct block_dev* bdev, void* buf, u64_t start, size_t count)
+{
+ struct vecbuf* vbuf = NULL;
+ vbuf_alloc(&vbuf, buf, bdev->blk_size * count);
+
+ struct blkio_req* req = blkio_vrd(vbuf, start, NULL, NULL, 0);
+ blkio_commit(bdev->blkio, req);
+ pwait(&req->wait);
+
+ int errno = req->errcode;
+ if (!errno) {
+ errno = count;
+ } else {
+ errno = -errno;
+ }
+
+ blkio_free_req(req);
+ vbuf_free(vbuf);
+
+ return errno;
+}
+
+int
+__block_wr_lb(struct block_dev* bdev, void* buf, u64_t start, size_t count)
+{
+ struct vecbuf* vbuf = NULL;
+ vbuf_alloc(&vbuf, buf, bdev->blk_size * count);
+
+ struct blkio_req* req = blkio_vwr(vbuf, start, NULL, NULL, 0);
+ blkio_commit(bdev->blkio, req);
+ pwait(&req->wait);
+
+ int errno = req->errcode;
+ if (!errno) {
+ errno = count;
+ } else {
+ errno = -errno;
+ }
+
+ blkio_free_req(req);
+ vbuf_free(vbuf);
+
+ return errno;
+}
+
+void*
+block_alloc_buf(struct block_dev* bdev)
+{
+ return valloc(bdev->blk_size);
+}
+
+void
+block_free_buf(struct block_dev* bdev, void* buf)
+{
+ return vfree(buf);
+}
+
struct block_dev*
block_alloc_dev(const char* blk_id, void* driver, req_handler ioreq_handler)
{
@@ -155,6 +216,8 @@ block_alloc_dev(const char* blk_id, void* driver, req_handler ioreq_handler)
bdev->blkio = blkio_newctx(ioreq_handler);
bdev->driver = driver;
bdev->blkio->driver = driver;
+ bdev->ops = (struct block_dev_ops){ .block_read = __block_rd_lb,
+ .block_write = __block_wr_lb };
return bdev;
}
diff --git a/lunaix-os/kernel/demos/dir_read.c b/lunaix-os/kernel/demos/dir_read.c
index 54ffd97..e1ef7a2 100644
--- a/lunaix-os/kernel/demos/dir_read.c
+++ b/lunaix-os/kernel/demos/dir_read.c
@@ -1,7 +1,7 @@
#include
#include
+#include
#include
-#include
void
_readdir_main()
diff --git a/lunaix-os/kernel/demos/input_test.c b/lunaix-os/kernel/demos/input_test.c
index 5f2a904..47e0081 100644
--- a/lunaix-os/kernel/demos/input_test.c
+++ b/lunaix-os/kernel/demos/input_test.c
@@ -1,8 +1,8 @@
#include
#include
#include
+#include
#include
-#include
#include
#define STDIN 1
diff --git a/lunaix-os/kernel/demos/iotest.c b/lunaix-os/kernel/demos/iotest.c
index 643452c..cba8962 100644
--- a/lunaix-os/kernel/demos/iotest.c
+++ b/lunaix-os/kernel/demos/iotest.c
@@ -1,7 +1,7 @@
#include
#include
+#include
#include
-#include
#include
void
diff --git a/lunaix-os/kernel/demos/signal_demo.c b/lunaix-os/kernel/demos/signal_demo.c
index 27d7814..3878a18 100644
--- a/lunaix-os/kernel/demos/signal_demo.c
+++ b/lunaix-os/kernel/demos/signal_demo.c
@@ -1,5 +1,5 @@
+#include
#include
-#include
#include
#include
#include
diff --git a/lunaix-os/kernel/demos/simple_sh.c b/lunaix-os/kernel/demos/simple_sh.c
index 3a1e1bb..acebe21 100644
--- a/lunaix-os/kernel/demos/simple_sh.c
+++ b/lunaix-os/kernel/demos/simple_sh.c
@@ -1,8 +1,8 @@
#include
#include
#include
+#include
#include
-#include
#include
#include
diff --git a/lunaix-os/kernel/fs/fs_setup.c b/lunaix-os/kernel/fs/fs_setup.c
index 5b3a35d..0966222 100644
--- a/lunaix-os/kernel/fs/fs_setup.c
+++ b/lunaix-os/kernel/fs/fs_setup.c
@@ -1,5 +1,6 @@
#include
#include
+#include
#include
#include
#include
@@ -11,6 +12,7 @@ fsm_register_all()
twifs_init();
devfs_init();
taskfs_init();
+ iso9660_init();
// ... more fs implementation
}
\ No newline at end of file
diff --git a/lunaix-os/kernel/fs/fsm.c b/lunaix-os/kernel/fs/fsm.c
index bce983a..a6a2be6 100644
--- a/lunaix-os/kernel/fs/fsm.c
+++ b/lunaix-os/kernel/fs/fsm.c
@@ -54,6 +54,9 @@ struct filesystem*
fsm_new_fs(char* name, size_t name_len)
{
struct filesystem* fs = vzalloc(sizeof(*fs));
+ if (name_len == (size_t)-1) {
+ name_len = strlen(name);
+ }
fs->fs_name = HHSTR(name, name_len, 0);
return fs;
}
\ No newline at end of file
diff --git a/lunaix-os/kernel/fs/iso9660/directory.c b/lunaix-os/kernel/fs/iso9660/directory.c
new file mode 100644
index 0000000..d240e1f
--- /dev/null
+++ b/lunaix-os/kernel/fs/iso9660/directory.c
@@ -0,0 +1,148 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+extern struct cake_pile* drec_cache_pile;
+
+void
+iso9660_fill_drecache(struct iso_drecache* cache, struct iso_drecord* drec)
+{
+ *cache = (struct iso_drecache){ .data_size = drec->data_size.le,
+ .extent_addr = drec->extent_addr.le,
+ .flags = drec->flags,
+ .fu_size = drec->fu_sz ? drec->fu_sz : 1,
+ .gap_size = drec->gap_sz,
+ .xattr_len = drec->xattr_len };
+ u32_t l = drec->name.len;
+ while (l < (u32_t)-1 && drec->name.content[l--] != ';')
+ ;
+ l = (l + 1) ? l : drec->name.len;
+ l = MIN(l, ISO9660_IDLEN);
+ strncpy(cache->name_val, drec->name.content, l);
+ cache->name = HSTR(cache->name_val, l);
+ hstr_rehash(&cache->name, HSTR_FULL_HASH);
+}
+
+int
+iso9660_setup_dnode(struct v_dnode* dnode, struct v_inode* inode)
+{
+ if (!(inode->itype & VFS_IFDIR)) {
+ return;
+ }
+
+ int errno = 0;
+ struct device* dev = dnode->super_block->dev;
+ struct iso_inode* isoino = inode->data;
+ struct llist_header* lead = valloc(sizeof(*lead));
+ void* records = valloc(ISO9660_BLKSZ);
+ u32_t current_pos = -ISO9660_BLKSZ, max_pos = inode->fsize,
+ blk = inode->lb_addr * ISO9660_BLKSZ, blk_offset = (u32_t)-1;
+
+ llist_init_head(lead);
+
+ // As per 6.8.1, Directory structure shall NOT recorded in interleave mode.
+ do {
+ if (blk_offset >= ISO9660_BLKSZ - sizeof(struct iso_drecord)) {
+ current_pos += ISO9660_BLKSZ;
+ errno = dev->read(dev, records, blk + current_pos, ISO9660_BLKSZ);
+ if (errno < 0) {
+ errno = EIO;
+ goto done;
+ }
+ blk_offset = 0;
+ }
+
+ struct iso_drecord* drec;
+ struct iso_var_mdu* mdu = (struct iso_var_mdu*)(records + blk_offset);
+
+ if (!(drec = iso9660_get_drecord(mdu))) {
+ break;
+ }
+
+ // ignore the '.', '..' as we have built-in support
+ if (drec->name.len == 1) {
+ goto cont;
+ }
+
+ struct iso_drecache* cache = cake_grab(drec_cache_pile);
+
+ iso9660_fill_drecache(cache, drec);
+ llist_append(lead, &cache->caches);
+ cont:
+ blk_offset += mdu->len;
+ } while (current_pos + blk_offset < max_pos);
+
+ dnode->data = lead;
+ isoino->drecaches = lead;
+
+ vfs_assign_inode(dnode, inode);
+
+ errno = 0;
+
+done:
+ vfree(records);
+ return errno;
+}
+
+int
+iso9660_dir_lookup(struct v_inode* this, struct v_dnode* dnode)
+{
+ struct iso_inode* isoino = this->data;
+ struct llist_header* lead = isoino->drecaches;
+ struct iso_drecache *pos, *n;
+
+ llist_for_each(pos, n, lead, caches)
+ {
+ if (HSTR_EQ(&dnode->name, &pos->name)) {
+ goto found;
+ }
+ }
+
+ return ENOENT;
+found:
+ struct v_inode* inode = vfs_i_find(dnode->super_block, pos->extent_addr);
+
+ if (!inode) {
+ inode = vfs_i_alloc(dnode->super_block);
+ iso9660_fill_inode(inode, pos, pos->extent_addr);
+ vfs_i_addhash(inode);
+ }
+
+ iso9660_setup_dnode(dnode, inode);
+
+ return 0;
+}
+
+static int
+__get_dtype(struct iso_drecache* pos)
+{
+ if ((pos->flags & ISO_FDIR)) {
+ return DT_DIR;
+ } else {
+ return DT_FILE;
+ }
+}
+
+int
+iso9660_readdir(struct v_file* file, struct dir_context* dctx)
+{
+ struct llist_header* lead = file->dnode->data;
+ struct iso_drecache *pos, *n;
+ u32_t counter = dctx->index - 1;
+
+ llist_for_each(pos, n, lead, caches)
+ {
+ if (counter == (u32_t)-1 && !(pos->flags & ISO_FHIDDEN)) {
+ dctx->read_complete_callback(
+ dctx, pos->name_val, pos->name.len, __get_dtype(pos));
+ return 1;
+ }
+ counter--;
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/lunaix-os/kernel/fs/iso9660/file.c b/lunaix-os/kernel/fs/iso9660/file.c
new file mode 100644
index 0000000..6813c22
--- /dev/null
+++ b/lunaix-os/kernel/fs/iso9660/file.c
@@ -0,0 +1,37 @@
+#include
+#include
+
+int
+iso9660_open(struct v_inode* this, struct v_file* file)
+{
+ // TODO
+ return 0;
+}
+
+int
+iso9660_close(struct v_file* file)
+{
+ // TODO
+ return 0;
+}
+
+int
+iso9660_read(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
+{
+ // TODO
+ return 0;
+}
+
+int
+iso9660_write(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
+{
+ // TODO
+ return EROFS;
+}
+
+int
+iso9660_seek(struct v_inode* inode, size_t offset)
+{
+ // TODO
+ return 0;
+}
\ No newline at end of file
diff --git a/lunaix-os/kernel/fs/iso9660/inode.c b/lunaix-os/kernel/fs/iso9660/inode.c
new file mode 100644
index 0000000..31f85ce
--- /dev/null
+++ b/lunaix-os/kernel/fs/iso9660/inode.c
@@ -0,0 +1,72 @@
+#include
+#include
+#include
+#include
+#include
+
+static struct v_inode_ops iso_inode_ops = {
+ .dir_lookup = iso9660_dir_lookup,
+ .open = iso9660_open,
+};
+
+static struct v_file_ops iso_file_ops = { .close = iso9660_close,
+ .read = iso9660_read,
+ .write = iso9660_write,
+ .seek = iso9660_seek,
+ .readdir = iso9660_readdir };
+
+void
+iso9660_init_inode(struct v_superblock* vsb, struct v_inode* inode)
+{
+ inode->data = vzalloc(sizeof(struct iso_inode));
+}
+
+int
+iso9660_fill_inode(struct v_inode* inode, struct iso_drecache* dir, int ino)
+{
+ int errno = 0;
+ struct device* dev = inode->sb->dev;
+ struct iso_inode* isoino = (struct iso_inode*)inode->data;
+
+ // In the spec, there is a differentiation in how file section organized
+ // between interleaving and non-interleaving mode. To simplify this, we
+ // treat the non-interleaving as an interleaving with gap size = 0 and fu
+ // size = 1
+ isoino->fu_size = dir->fu_size;
+ isoino->gap_size = dir->gap_size;
+
+ u32_t fu_len = isoino->fu_size * ISO9660_BLKSZ;
+
+ inode->id = ino;
+ inode->lb_addr = dir->extent_addr;
+ inode->ops = &iso_inode_ops;
+ inode->default_fops = &iso_file_ops;
+
+ // xattr_len is in unit of FU. Each FU comprise block(s).
+ inode->fsize = dir->data_size - dir->xattr_len * fu_len;
+
+ if ((dir->flags & ISO_FDIR)) {
+ inode->itype = VFS_IFDIR;
+ } else {
+ inode->itype = VFS_IFFILE;
+ }
+
+ if (dir->xattr_len) {
+ struct iso_xattr* xattr = (struct iso_xattr*)valloc(ISO9660_BLKSZ);
+ // Only bring in single FU, as we only care about the attributes.
+ errno =
+ dev->read(dev, xattr, ISO9660_BLKSZ * inode->lb_addr, ISO9660_BLKSZ);
+ if (errno < 0) {
+ return EIO;
+ }
+ isoino->record_fmt = xattr->record_fmt;
+ isoino->ctime = iso9660_dt2unix(&xattr->ctime);
+ isoino->mtime = iso9660_dt2unix(&xattr->mtime);
+
+ inode->lb_addr += dir->xattr_len * dir->fu_size;
+
+ vfree(xattr);
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/lunaix-os/kernel/fs/iso9660/mount.c b/lunaix-os/kernel/fs/iso9660/mount.c
new file mode 100644
index 0000000..f6bb2fc
--- /dev/null
+++ b/lunaix-os/kernel/fs/iso9660/mount.c
@@ -0,0 +1,108 @@
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+struct cake_pile* drec_cache_pile;
+
+extern void
+iso9660_init_inode(struct v_superblock* vsb, struct v_inode* inode);
+
+uint32_t
+iso9660_rd_capacity(struct v_superblock* vsb)
+{
+ struct iso_superblock* isovsb = (struct iso_superblock*)vsb->data;
+ return isovsb->volume_size;
+}
+
+int
+iso9660_mount(struct v_superblock* vsb, struct v_dnode* mount_point)
+{
+ struct device* dev = vsb->dev;
+ struct iso_vol* vdesc = (struct iso_vol*)valloc(ISO9660_BLKSZ);
+ struct iso_vol_primary* vprim = NULL;
+ int errno = 0;
+ do {
+ errno = dev->read(dev, vdesc, ISO9660_BLKSZ * 16, ISO9660_BLKSZ);
+ if (errno < 0) {
+ errno = EIO;
+ goto done;
+ }
+ if (*(u32_t*)vdesc->std_id != ISO_SIGNATURE_LO) {
+ errno = ENODEV;
+ goto done;
+ }
+ if (vdesc->type == ISO_VOLPRIM) {
+ vprim = (struct iso_vol_primary*)vdesc;
+ break;
+ }
+
+ } while (vdesc->type != ISO_VOLTERM);
+
+ if (!vprim) {
+ errno = EINVAL;
+ goto done;
+ }
+
+ struct iso_superblock* isovsb = valloc(sizeof(*isovsb));
+ isovsb->lb_size = vprim->lb_size.le;
+ isovsb->volume_size = vprim->vol_size.le * isovsb->lb_size;
+
+ vsb->data = isovsb;
+ vsb->ops.init_inode = iso9660_init_inode;
+ vsb->ops.read_capacity = iso9660_rd_capacity;
+
+ struct v_inode* rootino = vfs_i_alloc(vsb);
+ struct iso_drecord* dir =
+ iso9660_get_drecord((struct iso_var_mdu*)vprim->root_record);
+
+ if (!dir) {
+ vfree(isovsb);
+ errno = EINVAL;
+ goto done;
+ }
+
+ struct iso_drecache drecache;
+ iso9660_fill_drecache(&drecache, dir);
+
+ if ((errno = iso9660_fill_inode(rootino, &drecache, 0)) < 0) {
+ vfree(isovsb);
+ errno = EINVAL;
+ goto done;
+ }
+
+ if ((errno = iso9660_setup_dnode(mount_point, rootino)) < 0) {
+ vfree(isovsb);
+ errno = EINVAL;
+ goto done;
+ }
+
+ vfs_i_addhash(rootino);
+
+done:
+ vfree(vdesc);
+ return 0;
+}
+
+int
+iso9660_unmount(struct v_superblock* vsb)
+{
+ // TODO clean up
+}
+
+void
+iso9660_init()
+{
+ drec_cache_pile =
+ cake_new_pile("iso_drec", sizeof(struct iso_drecache), 1, 0);
+
+ struct filesystem* fs = fsm_new_fs("iso9660", -1);
+ fs->types |= FSTYPE_ROFS;
+ fs->mount = iso9660_mount;
+ fs->unmount = iso9660_unmount;
+
+ fsm_register(fs);
+}
\ No newline at end of file
diff --git a/lunaix-os/kernel/fs/iso9660/utils.c b/lunaix-os/kernel/fs/iso9660/utils.c
new file mode 100644
index 0000000..748077d
--- /dev/null
+++ b/lunaix-os/kernel/fs/iso9660/utils.c
@@ -0,0 +1,24 @@
+#include
+
+struct iso_drecord*
+iso9660_get_drecord(struct iso_var_mdu* drecord_mdu)
+{
+ if (drecord_mdu->len <= sizeof(struct iso_drecord)) {
+ return NULL;
+ }
+ return (struct iso_drecord*)drecord_mdu->content;
+}
+
+#define FOUR_DIGIT(x) (x[0] + x[1] * 10 + x[2] * 100 + x[3] * 1000)
+#define TWO_DIGIT(x) (x[0] + x[1] * 10)
+
+time_t
+iso9660_dt2unix(struct iso_datetime* isodt)
+{
+ return time_tounix(FOUR_DIGIT(isodt->year),
+ TWO_DIGIT(isodt->month),
+ TWO_DIGIT(isodt->day),
+ TWO_DIGIT(isodt->hour),
+ TWO_DIGIT(isodt->min),
+ TWO_DIGIT(isodt->sec));
+}
diff --git a/lunaix-os/kernel/fs/mount.c b/lunaix-os/kernel/fs/mount.c
index ca80238..7db9513 100644
--- a/lunaix-os/kernel/fs/mount.c
+++ b/lunaix-os/kernel/fs/mount.c
@@ -3,8 +3,11 @@
#include
#include
#include
+#include
#include
+LOG_MODULE("fs")
+
struct llist_header all_mnts = { .next = &all_mnts, .prev = &all_mnts };
struct v_mount*
@@ -129,6 +132,10 @@ vfs_mount_at(const char* fs_name,
struct v_dnode* mnt_point,
int options)
{
+ if (device && device->dev_type != DEV_IFVOL) {
+ return ENOTBLK;
+ }
+
if (mnt_point->inode && !(mnt_point->inode->itype & VFS_IFDIR)) {
return ENOTDIR;
}
@@ -138,11 +145,20 @@ vfs_mount_at(const char* fs_name,
return ENODEV;
}
+ if (fs->types == FSTYPE_ROFS) {
+ options |= MNT_RO;
+ }
+
+ char* dev_name = "sys";
struct v_mount* parent_mnt = mnt_point->mnt;
struct v_superblock *sb = vfs_sb_alloc(), *old_sb = mnt_point->super_block;
sb->dev = device;
mnt_point->super_block = sb;
+ if (device) {
+ dev_name = device->name_val;
+ }
+
int errno = 0;
if (!(errno = fs->mount(sb, mnt_point))) {
sb->fs = fs;
@@ -153,6 +169,8 @@ vfs_mount_at(const char* fs_name,
goto cleanup;
}
+ kprintf("mount: dev=%s, fs=%s, mode=%d\n", dev_name, fs_name, options);
+
mnt_point->mnt->flags = options;
} else {
goto cleanup;
@@ -161,6 +179,11 @@ vfs_mount_at(const char* fs_name,
return errno;
cleanup:
+ kprintf(KERROR "mount: dev=%s, fs=%s, mode=%d, err=%d\n",
+ dev_name,
+ fs_name,
+ options,
+ errno);
mnt_point->super_block = old_sb;
vfs_sb_free(sb);
return errno;
diff --git a/lunaix-os/kernel/fs/ramfs/ramfs.c b/lunaix-os/kernel/fs/ramfs/ramfs.c
index a1018da..f33d330 100644
--- a/lunaix-os/kernel/fs/ramfs/ramfs.c
+++ b/lunaix-os/kernel/fs/ramfs/ramfs.c
@@ -117,7 +117,7 @@ ramfs_unmount(struct v_superblock* vsb)
void
ramfs_init()
{
- struct filesystem* ramfs = fsm_new_fs("ramfs", 5);
+ struct filesystem* ramfs = fsm_new_fs("ramfs", -1);
ramfs->mount = ramfs_mount;
ramfs->unmount = ramfs_unmount;
diff --git a/lunaix-os/kernel/fs/twifs/twifs.c b/lunaix-os/kernel/fs/twifs/twifs.c
index 437ab0b..312c0a4 100644
--- a/lunaix-os/kernel/fs/twifs/twifs.c
+++ b/lunaix-os/kernel/fs/twifs/twifs.c
@@ -61,7 +61,6 @@ __twifs_init_inode(struct v_superblock* vsb, struct v_inode* inode)
int
__twifs_mount(struct v_superblock* vsb, struct v_dnode* mount_point)
{
- vsb->dev = 1;
vsb->ops.init_inode = __twifs_init_inode;
struct v_inode* inode = vfs_i_alloc(vsb);
diff --git a/lunaix-os/kernel/k_init.c b/lunaix-os/kernel/k_init.c
index 53027c9..6274dd8 100644
--- a/lunaix-os/kernel/k_init.c
+++ b/lunaix-os/kernel/k_init.c
@@ -82,6 +82,8 @@ _kernel_init()
vfs_export_attributes();
+ lxconsole_init();
+
if ((errno = vfs_mount_root("ramfs", NULL))) {
panickf("Fail to mount root. (errno=%d)", errno);
}
@@ -90,7 +92,7 @@ _kernel_init()
vfs_mount("/sys", "twifs", NULL, MNT_RO);
vfs_mount("/task", "taskfs", NULL, MNT_RO);
- lxconsole_init();
+ lxconsole_spawn_ttydev();
syscall_install();
diff --git a/lunaix-os/kernel/kprintf.c b/lunaix-os/kernel/kprintf.c
index a9a256d..91646e9 100644
--- a/lunaix-os/kernel/kprintf.c
+++ b/lunaix-os/kernel/kprintf.c
@@ -1,5 +1,7 @@
#include
#include
+#include
+#include
#include
#include
@@ -7,21 +9,16 @@
#define MAX_XFMT_SIZE 512
void
-__kprintf(const char* component, const char* fmt, va_list args)
+__kprintf_internal(const char* component,
+ int log_level,
+ const char* fmt,
+ va_list args)
{
char buf[MAX_KPRINTF_BUF_SIZE];
- if (!fmt)
- return;
- char log_level = '0';
char expanded_fmt[MAX_XFMT_SIZE];
- if (*fmt == '\x1b') {
- log_level = *(++fmt);
- fmt++;
- }
-
switch (log_level) {
- case '1':
+ case 1:
// tty_set_theme(VGA_COLOR_BROWN, current_theme >> 12);
ksnprintf(expanded_fmt,
MAX_XFMT_SIZE,
@@ -29,7 +26,7 @@ __kprintf(const char* component, const char* fmt, va_list args)
component,
fmt);
break;
- case '2':
+ case 2:
// tty_set_theme(VGA_COLOR_LIGHT_RED, current_theme >> 12);
ksnprintf(expanded_fmt,
MAX_XFMT_SIZE,
@@ -37,7 +34,7 @@ __kprintf(const char* component, const char* fmt, va_list args)
component,
fmt);
break;
- case '3':
+ case 3:
// tty_set_theme(VGA_COLOR_LIGHT_BLUE, current_theme >> 12);
ksnprintf(expanded_fmt,
MAX_XFMT_SIZE,
@@ -54,6 +51,21 @@ __kprintf(const char* component, const char* fmt, va_list args)
console_write_str(buf);
}
+void
+__kprintf(const char* component, const char* fmt, va_list args)
+{
+ if (!fmt)
+ return;
+ char log_level = '0';
+
+ if (*fmt == '\x1b') {
+ log_level = *(++fmt);
+ fmt++;
+ }
+
+ __kprintf_internal(component, log_level - '0', fmt, args);
+}
+
void
kprint_panic(const char* fmt, ...)
{
@@ -112,4 +124,9 @@ kprint_hex(const void* buffer, unsigned int size)
console_write_str(ch_cache);
console_write_char('\n');
}
+}
+
+__DEFINE_LXSYSCALL3(void, syslog, int, level, const char*, fmt, va_list, args)
+{
+ __kprintf_internal("syslog", level, fmt, args);
}
\ No newline at end of file
diff --git a/lunaix-os/kernel/lxinit.c b/lunaix-os/kernel/lxinit.c
index 4744cc1..f53405a 100644
--- a/lunaix-os/kernel/lxinit.c
+++ b/lunaix-os/kernel/lxinit.c
@@ -1,10 +1,10 @@
#include
#include
+#include
#include
#include
#include
#include
-#include
#include
#include
#include
diff --git a/lunaix-os/kernel/proc0.c b/lunaix-os/kernel/proc0.c
index 1e54e59..19f04dc 100644
--- a/lunaix-os/kernel/proc0.c
+++ b/lunaix-os/kernel/proc0.c
@@ -1,8 +1,10 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -11,11 +13,11 @@
#include
#include
#include
-#include
#include
#include
#include
#include
+
#include
#include
@@ -76,6 +78,18 @@ input_test();
extern void
sh_main();
+void __USER__
+__setup_dir()
+{
+ int errno;
+ mkdir("/mnt");
+ mkdir("/mnt/lunaix-os");
+
+ if ((errno = mount("/dev/sdb", "/mnt/lunaix-os", "iso9660", 0))) {
+ syslog(2, "fail mounting boot medium. (%d)\n", errno);
+ }
+}
+
void __USER__
__proc0_usr()
{
@@ -85,6 +99,8 @@ __proc0_usr()
int fdstdout = open("/dev/tty", 0);
int fdstdin = dup2(stdout, 1);
+ __setup_dir();
+
pid_t p;
if (!(p = fork())) {
@@ -154,7 +170,9 @@ extern multiboot_info_t* _k_init_mb_info; /* k_init.c */
void
init_platform()
{
- kprintf(KINFO "\033[11;0mLunaixOS \033[39;49m\n");
+ kprintf(KINFO "\033[11;0mLunaixOS (gcc v%s, %s)\033[39;49m\n",
+ __VERSION__,
+ __TIME__);
// é宿æç³»ç»é¢ç页ï¼å
åæ å°IOï¼ACPIä¹ç±»çï¼ï¼å¹¶ä¸è¿è¡1:1æ å°
lock_reserved_memory();
diff --git a/lunaix-os/kernel/tty/lxconsole.c b/lunaix-os/kernel/tty/lxconsole.c
index 31afa64..d555175 100644
--- a/lunaix-os/kernel/tty/lxconsole.c
+++ b/lunaix-os/kernel/tty/lxconsole.c
@@ -119,7 +119,11 @@ lxconsole_init()
fifo_init(&lx_console.input, valloc(4096), 4096, 0);
lx_console.flush_timer = NULL;
+}
+void
+lxconsole_spawn_ttydev()
+{
struct device* tty_dev = device_addseq(NULL, &lx_console, "tty");
tty_dev->write = __tty_write;
tty_dev->read = __tty_read;
--
2.27.0
From 8ce769cc52e91ff3cdb8eda4b1f5d8fe58241688 Mon Sep 17 00:00:00 2001
From: Minep
Date: Fri, 11 Nov 2022 21:45:16 +0000
Subject: [PATCH 03/16] feat: (iso9660) implement file read (for both
interleaved and non-interleaved mode) fix: (blkio) race condition between
blkio_commit and pwait fix: regression chore: clean up
---
lunaix-os/includes/lunaix/blkio.h | 4 +-
lunaix-os/includes/lunaix/fs.h | 3 +-
lunaix-os/kernel/block/blkio.c | 22 ++++++++-
lunaix-os/kernel/block/block.c | 17 ++-----
lunaix-os/kernel/fs/iso9660/directory.c | 7 ++-
lunaix-os/kernel/fs/iso9660/file.c | 63 +++++++++++++++++++++++--
6 files changed, 92 insertions(+), 24 deletions(-)
diff --git a/lunaix-os/includes/lunaix/blkio.h b/lunaix-os/includes/lunaix/blkio.h
index 07b53ac..a189a4d 100644
--- a/lunaix-os/includes/lunaix/blkio.h
+++ b/lunaix-os/includes/lunaix/blkio.h
@@ -12,6 +12,8 @@
#define BLKIO_BUSY 0x4
#define BLKIO_PENDING 0x8
+#define BLKIO_WAIT 0x1
+
// Free on complete
#define BLKIO_FOC 0x10
@@ -96,7 +98,7 @@ blkio_free_req(struct blkio_req* req);
* @param req
*/
void
-blkio_commit(struct blkio_context* ctx, struct blkio_req* req);
+blkio_commit(struct blkio_context* ctx, struct blkio_req* req, int options);
/**
* @brief Schedule an IO request to be handled.
diff --git a/lunaix-os/includes/lunaix/fs.h b/lunaix-os/includes/lunaix/fs.h
index cc850ad..f6a2e62 100644
--- a/lunaix-os/includes/lunaix/fs.h
+++ b/lunaix-os/includes/lunaix/fs.h
@@ -173,7 +173,8 @@ struct v_fd
int flags;
};
-// FIXME how do we invalidate corresponding v_dnodes given the v_inode?
+// [v_inode::aka_nodes]
+// how do we invalidate corresponding v_dnodes given the v_inode?
/*
Consider taskfs, which is Lunaix's speak of Linux's procfs, that allow
info of every process being accessible via file system. Each process's
diff --git a/lunaix-os/kernel/block/blkio.c b/lunaix-os/kernel/block/blkio.c
index c5d688f..0427622 100644
--- a/lunaix-os/kernel/block/blkio.c
+++ b/lunaix-os/kernel/block/blkio.c
@@ -2,6 +2,8 @@
#include
#include
+#include
+
static struct cake_pile* blkio_reqpile;
void
@@ -70,7 +72,7 @@ blkio_newctx(req_handler handler)
}
void
-blkio_commit(struct blkio_context* ctx, struct blkio_req* req)
+blkio_commit(struct blkio_context* ctx, struct blkio_req* req, int options)
{
req->flags |= BLKIO_PENDING;
req->io_ctx = ctx;
@@ -78,8 +80,25 @@ blkio_commit(struct blkio_context* ctx, struct blkio_req* req)
// if the pipeline is not running (e.g., stalling). Then we should schedule
// one immediately and kick it started.
+ // NOTE: Possible race condition between blkio_commit and pwait.
+ // Consider: what if scheduler complete the request before pwait even get
+ // called?
+ // Two possible work around:
+ // #1. we disable the interrupt before schedule the request.
+ // #2. we do scheduling within interrupt context (e.g., attach a timer)
+ // As we don't want to overwhelming the interrupt context and also keep the
+ // request RTT as small as possible, hence #1 is preferred.
+
if (!ctx->busy) {
+ if ((options & BLKIO_WAIT)) {
+ cpu_disable_interrupt();
+ blkio_schedule(ctx);
+ pwait(&req->wait);
+ return;
+ }
blkio_schedule(ctx);
+ } else if ((options & BLKIO_WAIT)) {
+ pwait(&req->wait);
}
}
@@ -108,7 +127,6 @@ blkio_complete(struct blkio_req* req)
req->completed(req);
}
- // FIXME Not working in first process! Need a dummy process.
// Wake all blocked processes on completion,
// albeit should be no more than one process in everycase (by design)
pwake_all(&req->wait);
diff --git a/lunaix-os/kernel/block/block.c b/lunaix-os/kernel/block/block.c
index 494a977..6203fc7 100644
--- a/lunaix-os/kernel/block/block.c
+++ b/lunaix-os/kernel/block/block.c
@@ -74,9 +74,7 @@ __block_read(struct device* dev, void* buf, size_t offset, size_t len)
}
req = blkio_vrd(vbuf, rd_block, NULL, NULL, 0);
- blkio_commit(bdev->blkio, req);
-
- pwait(&req->wait);
+ blkio_commit(bdev->blkio, req, BLKIO_WAIT);
if (!(errno = req->errcode)) {
memcpy(buf, head_buf + r, rd_size);
@@ -123,13 +121,8 @@ __block_write(struct device* dev, void* buf, size_t offset, size_t len)
vbuf_alloc(&vbuf, buf + wr_size, llen);
}
- // FIXME race condition between blkio_commit and pwait.
- // Consider: what if scheduler complete the request before process enter
- // wait state?
req = blkio_vwr(vbuf, wr_block, NULL, NULL, 0);
- blkio_commit(bdev->blkio, req);
-
- pwait(&req->wait);
+ blkio_commit(bdev->blkio, req, BLKIO_WAIT);
int errno = req->errcode;
if (!errno) {
@@ -154,8 +147,7 @@ __block_rd_lb(struct block_dev* bdev, void* buf, u64_t start, size_t count)
vbuf_alloc(&vbuf, buf, bdev->blk_size * count);
struct blkio_req* req = blkio_vrd(vbuf, start, NULL, NULL, 0);
- blkio_commit(bdev->blkio, req);
- pwait(&req->wait);
+ blkio_commit(bdev->blkio, req, BLKIO_WAIT);
int errno = req->errcode;
if (!errno) {
@@ -177,8 +169,7 @@ __block_wr_lb(struct block_dev* bdev, void* buf, u64_t start, size_t count)
vbuf_alloc(&vbuf, buf, bdev->blk_size * count);
struct blkio_req* req = blkio_vwr(vbuf, start, NULL, NULL, 0);
- blkio_commit(bdev->blkio, req);
- pwait(&req->wait);
+ blkio_commit(bdev->blkio, req, BLKIO_WAIT);
int errno = req->errcode;
if (!errno) {
diff --git a/lunaix-os/kernel/fs/iso9660/directory.c b/lunaix-os/kernel/fs/iso9660/directory.c
index d240e1f..71244a9 100644
--- a/lunaix-os/kernel/fs/iso9660/directory.c
+++ b/lunaix-os/kernel/fs/iso9660/directory.c
@@ -32,7 +32,8 @@ int
iso9660_setup_dnode(struct v_dnode* dnode, struct v_inode* inode)
{
if (!(inode->itype & VFS_IFDIR)) {
- return;
+ vfs_assign_inode(dnode, inode);
+ return 0;
}
int errno = 0;
@@ -113,9 +114,7 @@ found:
vfs_i_addhash(inode);
}
- iso9660_setup_dnode(dnode, inode);
-
- return 0;
+ return iso9660_setup_dnode(dnode, inode);
}
static int
diff --git a/lunaix-os/kernel/fs/iso9660/file.c b/lunaix-os/kernel/fs/iso9660/file.c
index 6813c22..34d3b47 100644
--- a/lunaix-os/kernel/fs/iso9660/file.c
+++ b/lunaix-os/kernel/fs/iso9660/file.c
@@ -1,5 +1,9 @@
#include
#include
+#include
+#include
+
+#include
int
iso9660_open(struct v_inode* this, struct v_file* file)
@@ -18,15 +22,68 @@ iso9660_close(struct v_file* file)
int
iso9660_read(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
{
- // TODO
- return 0;
+ // This read implementation handle both interleaved and non-interleaved
+ // structuring
+
+ struct iso_inode* isoino = inode->data;
+ struct device* bdev = inode->sb->dev;
+
+ len = MIN(fpos + len, inode->fsize);
+ if (len <= fpos) {
+ return 0;
+ }
+
+ len -= fpos;
+
+ size_t fu_len = isoino->fu_size * ISO9660_BLKSZ;
+ // if fpos is not FU aligned, then we must do an extra read.
+ size_t fu_to_read =
+ ICEIL(len, fu_len) + (fpos > fu_len && (fpos % fu_len) != 0);
+ size_t sec = (fpos % fu_len) / ISO9660_BLKSZ;
+ size_t wd_start = fpos % ISO9660_BLKSZ,
+ wd_len = MIN(len, ISO9660_BLKSZ - wd_start), i = 0;
+
+ // how many blocks (file unit + gaps) before of our current read position
+ size_t true_offset = (fpos / fu_len);
+ true_offset = true_offset * (isoino->fu_size + isoino->gap_size);
+
+ void* rd_buffer = valloc(ISO9660_BLKSZ);
+
+ true_offset += sec + inode->lb_addr;
+
+ int errno = 0;
+ while (fu_to_read) {
+ for (; sec < isoino->fu_size && i < len; sec++) {
+ errno = bdev->read(
+ bdev, rd_buffer, true_offset * ISO9660_BLKSZ, ISO9660_BLKSZ);
+
+ if (errno < 0) {
+ errno = EIO;
+ goto done;
+ }
+
+ memcpy(buffer + i, rd_buffer + wd_start, wd_len);
+
+ i += wd_len;
+ true_offset++;
+ wd_start = 0;
+ wd_len = MIN(len - i, ISO9660_BLKSZ);
+ }
+ sec = 0;
+ fu_to_read--;
+ }
+ errno = i;
+
+done:
+ vfree(rd_buffer);
+ return errno;
}
int
iso9660_write(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
{
// TODO
- return EROFS;
+ return ENOTSUP;
}
int
--
2.27.0
From a36758a5018f6a3792c164cd2a313d4f61b7111e Mon Sep 17 00:00:00 2001
From: Minep
Date: Fri, 11 Nov 2022 23:45:51 +0000
Subject: [PATCH 04/16] feat: (vfs) write_page and read_page file operations
fix: (iso9660) infinite loop when scanning iso9660 volume descriptors chore:
clean up
---
lunaix-os/includes/lunaix/common.h | 4 ++
lunaix-os/includes/lunaix/device.h | 2 +
lunaix-os/includes/lunaix/fs.h | 10 ++++
lunaix-os/includes/lunaix/mm/page.h | 4 --
lunaix-os/kernel/block/block.c | 70 +++++++++++++++++++++++++
lunaix-os/kernel/device/devfs.c | 30 +++++++++++
lunaix-os/kernel/device/input.c | 7 +++
lunaix-os/kernel/fs/iso9660/directory.c | 6 ++-
lunaix-os/kernel/fs/iso9660/inode.c | 2 +
lunaix-os/kernel/fs/iso9660/mount.c | 8 +--
lunaix-os/kernel/fs/pcache.c | 10 ++--
lunaix-os/kernel/fs/ramfs/ramfs.c | 2 +
lunaix-os/kernel/fs/twifs/twifs.c | 2 +
lunaix-os/kernel/process/taskfs.c | 2 +
lunaix-os/kernel/tty/lxconsole.c | 14 +++++
15 files changed, 160 insertions(+), 13 deletions(-)
diff --git a/lunaix-os/includes/lunaix/common.h b/lunaix-os/includes/lunaix/common.h
index f5a24a7..1ebf158 100644
--- a/lunaix-os/includes/lunaix/common.h
+++ b/lunaix-os/includes/lunaix/common.h
@@ -1,6 +1,10 @@
#ifndef __LUNAIX_CONSTANTS_H
#define __LUNAIX_CONSTANTS_H
+#define PG_SIZE_BITS 12
+#define PG_SIZE (1 << PG_SIZE_BITS)
+#define PG_INDEX_BITS 10
+
#define MEM_1MB 0x100000
#define MEM_4MB 0x400000
diff --git a/lunaix-os/includes/lunaix/device.h b/lunaix-os/includes/lunaix/device.h
index c456df4..fd234f0 100644
--- a/lunaix-os/includes/lunaix/device.h
+++ b/lunaix-os/includes/lunaix/device.h
@@ -30,6 +30,8 @@ struct device
void* underlay;
int (*read)(struct device* dev, void* buf, size_t offset, size_t len);
int (*write)(struct device* dev, void* buf, size_t offset, size_t len);
+ int (*read_page)(struct device* dev, void* buf, size_t offset);
+ int (*write_page)(struct device* dev, void* buf, size_t offset);
int (*exec_cmd)(struct device* dev, uint32_t req, va_list args);
};
diff --git a/lunaix-os/includes/lunaix/fs.h b/lunaix-os/includes/lunaix/fs.h
index f6a2e62..024b4ea 100644
--- a/lunaix-os/includes/lunaix/fs.h
+++ b/lunaix-os/includes/lunaix/fs.h
@@ -120,6 +120,16 @@ struct v_file_ops
{
int (*write)(struct v_inode* inode, void* buffer, size_t len, size_t fpos);
int (*read)(struct v_inode* inode, void* buffer, size_t len, size_t fpos);
+
+ // for operatiosn {write|read}_page, following are true:
+ // + `len` always equals to PG_SIZE
+ // + `fpos` always PG_SIZE aligned.
+ // These additional operations allow underlying fs to use more specialized
+ // and optimized code.
+
+ int (*write_page)(struct v_inode* inode, void* pg, size_t len, size_t fpos);
+ int (*read_page)(struct v_inode* inode, void* pg, size_t len, size_t fpos);
+
int (*readdir)(struct v_file* file, struct dir_context* dctx);
int (*seek)(struct v_inode* inode, size_t offset); // optional
int (*close)(struct v_file* file);
diff --git a/lunaix-os/includes/lunaix/mm/page.h b/lunaix-os/includes/lunaix/mm/page.h
index 9458277..781a4ce 100644
--- a/lunaix-os/includes/lunaix/mm/page.h
+++ b/lunaix-os/includes/lunaix/mm/page.h
@@ -3,10 +3,6 @@
#include
#include
-#define PG_SIZE_BITS 12
-#define PG_SIZE (1 << PG_SIZE_BITS)
-#define PG_INDEX_BITS 10
-
#define PG_MAX_ENTRIES 1024U
#define PG_LAST_TABLE PG_MAX_ENTRIES - 1
#define PG_FIRST_TABLE 0
diff --git a/lunaix-os/kernel/block/block.c b/lunaix-os/kernel/block/block.c
index 6203fc7..1cade0c 100644
--- a/lunaix-os/kernel/block/block.c
+++ b/lunaix-os/kernel/block/block.c
@@ -140,6 +140,72 @@ __block_write(struct device* dev, void* buf, size_t offset, size_t len)
return errno;
}
+int
+__block_read_page(struct device* dev, void* buf, size_t offset)
+{
+ struct vecbuf* vbuf = NULL;
+ struct block_dev* bdev = (struct block_dev*)dev->underlay;
+
+ u32_t lba = offset / bdev->blk_size + bdev->start_lba;
+ u32_t rd_lba = MIN(lba + PG_SIZE / bdev->blk_size, bdev->end_lba);
+
+ if (rd_lba <= lba) {
+ return 0;
+ }
+
+ rd_lba -= lba;
+
+ vbuf_alloc(&vbuf, buf, rd_lba * bdev->blk_size);
+
+ struct blkio_req* req = blkio_vrd(vbuf, lba, NULL, NULL, 0);
+
+ blkio_commit(bdev->blkio, req, BLKIO_WAIT);
+
+ int errno = req->errcode;
+ if (!errno) {
+ errno = rd_lba * bdev->blk_size;
+ } else {
+ errno = -errno;
+ }
+
+ blkio_free_req(req);
+ vbuf_free(vbuf);
+ return errno;
+}
+
+int
+__block_write_page(struct device* dev, void* buf, size_t offset)
+{
+ struct vecbuf* vbuf = NULL;
+ struct block_dev* bdev = (struct block_dev*)dev->underlay;
+
+ u32_t lba = offset / bdev->blk_size + bdev->start_lba;
+ u32_t rd_lba = MIN(lba + PG_SIZE / bdev->blk_size, bdev->end_lba);
+
+ if (rd_lba <= lba) {
+ return 0;
+ }
+
+ rd_lba -= lba;
+
+ vbuf_alloc(&vbuf, buf, rd_lba * bdev->blk_size);
+
+ struct blkio_req* req = blkio_vwr(vbuf, lba, NULL, NULL, 0);
+
+ blkio_commit(bdev->blkio, req, BLKIO_WAIT);
+
+ int errno = req->errcode;
+ if (!errno) {
+ errno = rd_lba * bdev->blk_size;
+ } else {
+ errno = -errno;
+ }
+
+ blkio_free_req(req);
+ vbuf_free(vbuf);
+ return errno;
+}
+
int
__block_rd_lb(struct block_dev* bdev, void* buf, u64_t start, size_t count)
{
@@ -250,7 +316,9 @@ __block_register(struct block_dev* bdev)
struct device* dev = device_addvol(NULL, bdev, "sd%c", 'a' + free_slot);
dev->write = __block_write;
+ dev->write_page = __block_write_page;
dev->read = __block_read;
+ dev->read_page = __block_read_page;
bdev->dev = dev;
strcpy(bdev->bdev_id, dev->name_val);
@@ -271,7 +339,9 @@ blk_mount_part(struct block_dev* bdev,
struct device* dev =
device_addvol(NULL, pbdev, "%sp%d", bdev->bdev_id, index + 1);
dev->write = __block_write;
+ dev->write_page = __block_write_page;
dev->read = __block_read;
+ dev->read_page = __block_read_page;
pbdev->start_lba = start_lba;
pbdev->end_lba = end_lba;
diff --git a/lunaix-os/kernel/device/devfs.c b/lunaix-os/kernel/device/devfs.c
index 012f919..c265c4b 100644
--- a/lunaix-os/kernel/device/devfs.c
+++ b/lunaix-os/kernel/device/devfs.c
@@ -35,6 +35,34 @@ devfs_write(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
return dev->write(dev, buffer, fpos, len);
}
+int
+devfs_read_page(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
+{
+ assert(inode->data);
+
+ struct device* dev = (struct device*)inode->data;
+
+ if (!dev->read_page) {
+ return ENOTSUP;
+ }
+
+ return dev->read_page(dev, buffer, fpos);
+}
+
+int
+devfs_write_page(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
+{
+ assert(inode->data);
+
+ struct device* dev = (struct device*)inode->data;
+
+ if (!dev->read_page) {
+ return ENOTSUP;
+ }
+
+ return dev->read_page(dev, buffer, fpos);
+}
+
int
devfs_get_itype(struct device* dev)
{
@@ -157,6 +185,8 @@ struct v_inode_ops devfs_inode_ops = { .dir_lookup = devfs_dirlookup,
struct v_file_ops devfs_file_ops = { .close = default_file_close,
.read = devfs_read,
+ .read_page = devfs_read_page,
.write = devfs_write,
+ .write_page = devfs_write_page,
.seek = default_file_seek,
.readdir = devfs_readdir };
\ No newline at end of file
diff --git a/lunaix-os/kernel/device/input.c b/lunaix-os/kernel/device/input.c
index 5ddf6a4..a96cb00 100644
--- a/lunaix-os/kernel/device/input.c
+++ b/lunaix-os/kernel/device/input.c
@@ -62,6 +62,12 @@ __input_dev_read(struct device* dev, void* buf, size_t offset, size_t len)
return sizeof(struct input_evt_pkt);
}
+int
+__input_dev_read_pg(struct device* dev, void* buf, size_t offset)
+{
+ return __input_dev_read(dev, buf, offset, PG_SIZE);
+}
+
struct input_device*
input_add_device(char* name_fmt, ...)
{
@@ -78,6 +84,7 @@ input_add_device(char* name_fmt, ...)
idev->dev_if = dev;
dev->read = __input_dev_read;
+ dev->read_page = __input_dev_read_pg;
va_end(args);
diff --git a/lunaix-os/kernel/fs/iso9660/directory.c b/lunaix-os/kernel/fs/iso9660/directory.c
index 71244a9..816f6eb 100644
--- a/lunaix-os/kernel/fs/iso9660/directory.c
+++ b/lunaix-os/kernel/fs/iso9660/directory.c
@@ -21,8 +21,10 @@ iso9660_fill_drecache(struct iso_drecache* cache, struct iso_drecord* drec)
u32_t l = drec->name.len;
while (l < (u32_t)-1 && drec->name.content[l--] != ';')
;
- l = (l + 1) ? l : drec->name.len;
- l = MIN(l, ISO9660_IDLEN);
+ l++;
+ l = l ? l : drec->name.len;
+ l = MIN(l, ISO9660_IDLEN - 1);
+
strncpy(cache->name_val, drec->name.content, l);
cache->name = HSTR(cache->name_val, l);
hstr_rehash(&cache->name, HSTR_FULL_HASH);
diff --git a/lunaix-os/kernel/fs/iso9660/inode.c b/lunaix-os/kernel/fs/iso9660/inode.c
index 31f85ce..f88eee5 100644
--- a/lunaix-os/kernel/fs/iso9660/inode.c
+++ b/lunaix-os/kernel/fs/iso9660/inode.c
@@ -11,7 +11,9 @@ static struct v_inode_ops iso_inode_ops = {
static struct v_file_ops iso_file_ops = { .close = iso9660_close,
.read = iso9660_read,
+ .read_page = iso9660_read,
.write = iso9660_write,
+ .write_page = iso9660_write,
.seek = iso9660_seek,
.readdir = iso9660_readdir };
diff --git a/lunaix-os/kernel/fs/iso9660/mount.c b/lunaix-os/kernel/fs/iso9660/mount.c
index f6bb2fc..5d7bb81 100644
--- a/lunaix-os/kernel/fs/iso9660/mount.c
+++ b/lunaix-os/kernel/fs/iso9660/mount.c
@@ -24,9 +24,10 @@ iso9660_mount(struct v_superblock* vsb, struct v_dnode* mount_point)
struct device* dev = vsb->dev;
struct iso_vol* vdesc = (struct iso_vol*)valloc(ISO9660_BLKSZ);
struct iso_vol_primary* vprim = NULL;
+ u32_t lba = 16;
int errno = 0;
do {
- errno = dev->read(dev, vdesc, ISO9660_BLKSZ * 16, ISO9660_BLKSZ);
+ errno = dev->read(dev, vdesc, ISO9660_BLKSZ * lba, ISO9660_BLKSZ);
if (errno < 0) {
errno = EIO;
goto done;
@@ -39,7 +40,7 @@ iso9660_mount(struct v_superblock* vsb, struct v_dnode* mount_point)
vprim = (struct iso_vol_primary*)vdesc;
break;
}
-
+ lba++;
} while (vdesc->type != ISO_VOLTERM);
if (!vprim) {
@@ -81,10 +82,11 @@ iso9660_mount(struct v_superblock* vsb, struct v_dnode* mount_point)
}
vfs_i_addhash(rootino);
+ return 0;
done:
vfree(vdesc);
- return 0;
+ return errno;
}
int
diff --git a/lunaix-os/kernel/fs/pcache.c b/lunaix-os/kernel/fs/pcache.c
index c27f78b..317e491 100644
--- a/lunaix-os/kernel/fs/pcache.c
+++ b/lunaix-os/kernel/fs/pcache.c
@@ -84,9 +84,10 @@ pcache_get_page(struct pcache* pcache,
{
struct pcache_pg* pg = btrie_get(&pcache->tree, index);
int is_new = 0;
- *offset = index & ((1 << pcache->tree.truncated) - 1);
+ u32_t mask = ((1 << pcache->tree.truncated) - 1);
+ *offset = index & mask;
if (!pg && (pg = pcache_new_page(pcache, index))) {
- pg->fpos = index - *offset;
+ pg->fpos = index & ~mask;
pcache->n_pages++;
is_new = 1;
}
@@ -137,7 +138,8 @@ pcache_read(struct v_inode* inode, void* data, uint32_t len, uint32_t fpos)
}
// Filling up the page
- errno = inode->default_fops->read(inode, pg->pg, PG_SIZE, pg->fpos);
+ errno =
+ inode->default_fops->read_page(inode, pg->pg, PG_SIZE, pg->fpos);
if (errno >= 0 && errno < PG_SIZE) {
// EOF
len = MIN(len, buf_off + errno);
@@ -176,7 +178,7 @@ pcache_commit(struct v_inode* inode, struct pcache_pg* page)
}
int errno =
- inode->default_fops->write(inode, page->pg, PG_SIZE, page->fpos);
+ inode->default_fops->write_page(inode, page->pg, PG_SIZE, page->fpos);
if (!errno) {
page->flags &= ~PCACHE_DIRTY;
diff --git a/lunaix-os/kernel/fs/ramfs/ramfs.c b/lunaix-os/kernel/fs/ramfs/ramfs.c
index f33d330..bcffa16 100644
--- a/lunaix-os/kernel/fs/ramfs/ramfs.c
+++ b/lunaix-os/kernel/fs/ramfs/ramfs.c
@@ -135,5 +135,7 @@ const struct v_inode_ops ramfs_inode_ops = { .mkdir = ramfs_mkdir,
const struct v_file_ops ramfs_file_ops = { .readdir = ramfs_readdir,
.close = default_file_close,
.read = default_file_read,
+ .read_page = default_file_read,
.write = default_file_write,
+ .write_page = default_file_write,
.seek = default_file_seek };
\ No newline at end of file
diff --git a/lunaix-os/kernel/fs/twifs/twifs.c b/lunaix-os/kernel/fs/twifs/twifs.c
index 312c0a4..fcadafd 100644
--- a/lunaix-os/kernel/fs/twifs/twifs.c
+++ b/lunaix-os/kernel/fs/twifs/twifs.c
@@ -268,7 +268,9 @@ twifs_mapping(struct twifs_node* parent, void* data, const char* fmt, ...)
const struct v_file_ops twifs_file_ops = { .close = default_file_close,
.read = __twifs_fread,
+ .read_page = __twifs_fread,
.write = __twifs_fwrite,
+ .write_page = __twifs_fwrite,
.readdir = __twifs_iterate_dir };
const struct v_inode_ops twifs_inode_ops = { .dir_lookup = __twifs_dirlookup,
diff --git a/lunaix-os/kernel/process/taskfs.c b/lunaix-os/kernel/process/taskfs.c
index 28c3713..7534703 100644
--- a/lunaix-os/kernel/process/taskfs.c
+++ b/lunaix-os/kernel/process/taskfs.c
@@ -133,7 +133,9 @@ taskfs_dirlookup(struct v_inode* this, struct v_dnode* dnode)
static struct v_file_ops taskfs_file_ops = { .close = default_file_close,
.read = default_file_read,
+ .read_page = default_file_read,
.write = default_file_write,
+ .write_page = default_file_write,
.readdir = taskfs_readdir,
.seek = default_file_seek };
static struct v_inode_ops taskfs_inode_ops = { .dir_lookup = taskfs_dirlookup,
diff --git a/lunaix-os/kernel/tty/lxconsole.c b/lunaix-os/kernel/tty/lxconsole.c
index d555175..80a7be6 100644
--- a/lunaix-os/kernel/tty/lxconsole.c
+++ b/lunaix-os/kernel/tty/lxconsole.c
@@ -121,12 +121,26 @@ lxconsole_init()
lx_console.flush_timer = NULL;
}
+int
+__tty_write_pg(struct device* dev, void* buf, size_t offset)
+{
+ return __tty_write(dev, buf, offset, PG_SIZE);
+}
+
+int
+__tty_read_pg(struct device* dev, void* buf, size_t offset)
+{
+ return __tty_read(dev, buf, offset, PG_SIZE);
+}
+
void
lxconsole_spawn_ttydev()
{
struct device* tty_dev = device_addseq(NULL, &lx_console, "tty");
tty_dev->write = __tty_write;
+ tty_dev->write_page = __tty_write_pg;
tty_dev->read = __tty_read;
+ tty_dev->read_page = __tty_read_pg;
tty_dev->exec_cmd = __tty_exec_cmd;
waitq_init(&lx_reader);
--
2.27.0
From 1e54f1850eda97e3b41c302b72c07a1f9cc4d637 Mon Sep 17 00:00:00 2001
From: Minep
Date: Sat, 12 Nov 2022 20:07:19 +0000
Subject: [PATCH 05/16] update readme add materials related to ISO9660 file
system
---
README.md | 76 ++++++++++++------
docs/README_en.md | 27 ++++---
.../ECMA-119_4th_edition_june_2019.pdf | Bin 0 -> 1397281 bytes
reference-material/Rockridge.pdf | Bin 0 -> 128415 bytes
reference-material/SUSP-112.pdf | Bin 0 -> 65705 bytes
5 files changed, 68 insertions(+), 35 deletions(-)
create mode 100644 reference-material/ECMA-119_4th_edition_june_2019.pdf
create mode 100644 reference-material/Rockridge.pdf
create mode 100644 reference-material/SUSP-112.pdf
diff --git a/README.md b/README.md
index cdeecbe..da02130 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,8 @@
ç®ä½ä¸æ | English
-# LunaixOS Project
+# LunaixOS Project
+
LunaixOS - ä¸ä¸ªç®åçï¼è¯¦ç»çï¼POSIXå
¼å®¹çï¼ä½æ¿ï¼ï¼ï¼å¸¦ææµéä¸ªäººé£æ ¼çæä½ç³»ç»ãå¼åè¿ç¨ä»¥è§é¢æç¨å½¢å¼å¨Bilibiliåç°ï¼[ãä»é¶å¼å§èªå¶æä½ç³»ç»ç³»åã](https://space.bilibili.com/12995787/channel/collectiondetail?sid=196337)ã
## å½åè¿åº¦ä»¥åæ¯æçåè½
@@ -28,7 +29,11 @@ LunaixOS - ä¸ä¸ªç®åçï¼è¯¦ç»çï¼POSIXå
¼å®¹çï¼ä½æ¿ï¼ï¼ï¼å¸¦æ
+ PCI 3.0
+ PCIe 1.1 (WIP)
+ Serial ATA AHCI
-+ æä»¶ç³»ç» (WIP)
++ æä»¶ç³»ç»
+ + èææä»¶ç³»ç»
+ + ISO9660
+ + åç
+ + Rock Ridgeæå± (WIP)
+ è¿ç¨GDB串å£è°è¯ (COM1@9600Bd)
å·²ç»æµè¯è¿çç¯å¢ï¼
@@ -40,11 +45,11 @@ LunaixOS - ä¸ä¸ªç®åçï¼è¯¦ç»çï¼POSIXå
¼å®¹çï¼ä½æ¿ï¼ï¼ï¼å¸¦æ
## ç®å½ç»æ
-| | |
-|-----|------|
-| [lunaix-os](lunaix-os/) | LunaixOSæºä»£ç |
-| [slides](slides/) | è§é¢ä¸æç¨çå¹»ç¯çåè¡¥å
ææ |
-| [reference-material](reference-material/)| æ åï¼ææ¯ææ¡£ååèæç® |
+| | |
+| ----------------------------------------- | ---------------------------- |
+| [lunaix-os](lunaix-os/) | LunaixOSæºä»£ç |
+| [slides](slides/) | è§é¢ä¸æç¨çå¹»ç¯çåè¡¥å
ææ |
+| [reference-material](reference-material/) | æ åï¼ææ¯ææ¡£ååèæç® |
## ç¼è¯ä¸æå»º
@@ -61,16 +66,16 @@ LunaixOS - ä¸ä¸ªç®åçï¼è¯¦ç»çï¼POSIXå
¼å®¹çï¼ä½æ¿ï¼ï¼ï¼å¸¦æ
åè¥æ¡ä»¶æ»¡è¶³ï¼é£ä¹å¯ä»¥ç´æ¥æ§è¡`make all`è¿è¡æå»ºï¼å®æåå¯å¨çæç`build`ç®å½ä¸æ¾å°å¯å¼å¯¼çisoã
æ¬é¡¹ç®æ¯æçmakeå½ä»¤ï¼
-| å½ä»¤ | ç¨é |
-|---|---|
-| `make all` | æå»ºéåï¼`-O2`ï¼ä½ç¦ç¨CSEç¸å
³çä¼å项 **â»** ï¼ |
-| `make instable` | æå»ºéåï¼`-O2`ï¼å¼å¯CSEç¸å
³ä¼åï¼ |
-| `make all-debug` | æå»ºéåè°è¯ç¨çéåï¼`-Og`ï¼ |
-| `make run` | 使ç¨QEMUè¿è¡buildç®å½ä¸çéå|
-| `make debug-qemu` | æå»ºå¹¶ä½¿ç¨QEMUè¿è¡è°è¯ |
-| `make debug-bochs` | æå»ºå¹¶ä½¿ç¨Bochsè¿è¡è°è¯ |
-| `make debug-qemu-vscode` | ç¨äºvscodeæ´å |
-| `make clean` | å é¤buildç®å½ |
+| å½ä»¤ | ç¨é |
+| ------------------------ | ----------------------------------------------- |
+| `make all` | æå»ºéåï¼`-O2`ï¼ä½ç¦ç¨CSEç¸å
³çä¼å项 **â»** ï¼ |
+| `make instable` | æå»ºéåï¼`-O2`ï¼å¼å¯CSEç¸å
³ä¼åï¼ |
+| `make all-debug` | æå»ºéåè°è¯ç¨çéåï¼`-Og`ï¼ |
+| `make run` | 使ç¨QEMUè¿è¡buildç®å½ä¸çéå |
+| `make debug-qemu` | æå»ºå¹¶ä½¿ç¨QEMUè¿è¡è°è¯ |
+| `make debug-bochs` | æå»ºå¹¶ä½¿ç¨Bochsè¿è¡è°è¯ |
+| `make debug-qemu-vscode` | ç¨äºvscodeæ´å |
+| `make clean` | å é¤buildç®å½ |
**â»ï¼ç±äºå¨`-O2`模å¼ä¸ï¼GCCä¼è¿è¡CSEä¼åï¼è¿å¯¼è´LunaixOSä¼åºç°ä¸äºé叏奿ªã离谱çbugï¼ä»èå½±åå°åºæ¬è¿è¡ãå
·ä½åå æå¾
è°æ¥ã**
@@ -85,6 +90,7 @@ qemu-img create -f vdi machine/disk0.vdi 128M
å¦æä½ æ³è¦ä½¿ç¨å«çç£çéåï¼éè¦ä¿®æ¹`configs/make-debug-tool`
æ¾å°è¿ä¸è¡ï¼
+
```
-drive id=disk,file="machine/disk0.vdi",if=none \
```
@@ -97,11 +103,11 @@ qemu-img create -f vdi machine/disk0.vdi 128M
ä¸é¢ååºä¸äºå¯è½ä¼åºç°çé®é¢ã
-#### é®é¢#1ï¼ QEMUä¸8042æ§å¶å¨æç¤ºæ¾ä¸å°ã
+#### é®é¢#1ï¼ QEMUä¸8042æ§å¶å¨æç¤ºæ¾ä¸å°
è¿æ¯QEMUé
ç½®ACPIæ¶çä¸ä¸ªbugï¼å¨7.0.0çä¸ä¿®å¤äºã
-#### é®é¢#2ï¼å¤è¿ç¨è¿è¡æ¶ï¼å¶å°ä¼åºç°General Protectioné误ã
+#### é®é¢#2ï¼å¤è¿ç¨è¿è¡æ¶ï¼å¶å°ä¼åºç°General Protectioné误
è¿å¾å¤§æ¦çæ¯åºç°äºç«ææ¡ä»¶ãè½ç¶æ¯ç¸å½ä¸å¯è½çãä½å¦æåºç°äºï¼è¿æ¯è¯·æissueã
@@ -120,6 +126,7 @@ qemu-img create -f vdi machine/disk0.vdi 128M
å½ç¶ï¼æ¨ä¹å¯ä»¥åè以ä¸å表æ¥äºè§£ç°é¶æ®µçLunaixOSé½ä½¿ç¨äºåªäºèµæï¼æ¬å表ä¼éçå¼åè¿åº¦æ´æ°ï¼ï¼
#### æåï¼æ åï¼ææ¯ææ¡£
+
+ [Intel 64 and IA-32 Architecture Software Developer's Manual (Full Volume Bundle)](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html)
+ [ACPI Specification (version 6.4)](https://uefi.org/sites/default/files/resources/ACPI_Spec_6_4_Jan22.pdf)
+ IBM PC/AT Technical Reference
@@ -131,31 +138,37 @@ qemu-img create -f vdi machine/disk0.vdi 128M
+ PCI Express Base Specification, Revision 1.1
+ PCI Firmware Specification, Revision 3.0
+ Serial ATA - Advanced Host Controller Interface (AHCI), Revision 1.3.1
-+ Serial ATA: HIgh Speed Serialized AT Attachment, Revision 3.2
++ Serial ATA: High Speed Serialized AT Attachment, Revision 3.2
+ SCSI Command Reference Manual
+ ATA/ATAPI Command Set - 3 (ACS-3)
++ [ECMA-119 (ISO9660)](https://www.ecma-international.org/publications-and-standards/standards/ecma-119/)
++ Rock Ridge Interchange Protocol (RRIP: IEEE P1282)
++ System Use Sharing Protocol (SUSP: IEEE P1281)
**å
责声æï¼PCIç¸å
³çæ åæç»è§£éæå½PCI-SIGææãæ¤å¤æä¾ç坿¬ä»
ä¾ä¸ªäººå¦ä¹ 使ç¨ãä»»ä½åç¨ç®çé¡»åPCI-SIGè´ä¹°ã**
#### ç论书ç±
+
+ *Computer System - A Programmer's Perspective Third Edition (CS:APP)* (Bryant, R & O'Hallaron, D)
+ *Modern Operating System* (Tanenbaum, A)
+ ãæ±ç¼è¯è¨ãï¼çç½ï¼ - ç¨äºå
¥é¨Intelè¯æ³çx86æ±ç¼ï¼å¯¹äºAT&Tè¯æ³ï¼æ¨èé
读CS:APPï¼
+ ~~ãå¾®æºåç䏿¥å£ææ¯ã - ç¨äºå¤§è´äºè§£x86æ¶æçå¾®æºä½ç³»ï¼æ´å ç»è´çäºè§£å¯ä»¥é
读Intel Manualï¼~~ ï¼å·²è¿æ¶ï¼æ¨èé
读CS:APPï¼
#### ç½ç«
+
+ [OSDev](https://wiki.osdev.org/Main_Page) - æä¸æå
«çåèï¼å¾å¤è¿æ¥äººçç»éªãä½è
主è¦ç¨äºä¸å¤èµææ¥è¯¢ä»¥åæ¶éï¼ææ¯æç®ï¼æåï¼æ åçç²ç¥æ»ç»ï¼ä»¥åå¼åç¯å¢/å·¥å
·é¾çæå»ºã
+ [FreeVGA](http://www.osdever.net/FreeVGA/home.htm) - 98å¹´çèµæºï¼å
³äºVGAç¼ç¨ææ¯çå®èç½ç«ã
+ GNU CC å GNU LD ç宿¹ææ¡£ã
+ [PCI Lookup](https://www.pcilookup.com/) - PCI设å¤ç¼å·æ¥è¯¢
#### å
¶ä»
-+ Linux Manual - ç¨äºæ¥è¯¢*nix APIçä¸äºå
·ä½è¡ä¸ºã
++ Linux Manual - ç¨äºæ¥è¯¢*nix APIçä¸äºå
·ä½è¡ä¸ºã
## éå½1ï¼æ¯æçç³»ç»è°ç¨
**Unix/Linux/POSIX**
+
1. `sleep(3)`
1. `wait(2)`
1. `waitpid(2)`
@@ -200,7 +213,7 @@ qemu-img create -f vdi machine/disk0.vdi 128M
2. `setxattr(2)`â»
2. `fgetxattr(2)`â»
2. `fsetxattr(2)`â»
-2. `ioctl(2)`â»
+2. `ioctl(2)`
2. `getpgid(2)`
2. `setpgid(2)`
@@ -209,6 +222,7 @@ qemu-img create -f vdi machine/disk0.vdi 128M
1. `yield`
2. `geterrno`
3. `realpathat`
+4. `syslog`
( **â»**ï¼è¯¥ç³»ç»è°ç¨ææªç»è¿æµè¯ )
@@ -216,9 +230,10 @@ qemu-img create -f vdi machine/disk0.vdi 128M
注æï¼gccéè¦ä»æºç æå»ºï¼å¹¶é
置为交åç¼è¯å¨ï¼å³ç®æ å¹³å°ä¸º`i686-elf`ãä½ å¯ä»¥ä½¿ç¨æ¬é¡¹ç®æä¾ç[èªå¨åèæ¬](slides/c0-workspace/gcc-build.sh)ï¼è¿å°ä¼æ¶µçgccåbinutilsæºç çä¸è½½ï¼é
ç½®åç¼è¯ï¼æ²¡ä»ä¹æ¶é´å»æç£¨èæ¬ï¼ç®ååªç¥éå¨ç¬è
çUbuntuç³»ç»ä¸å¯ä»¥è¿è¡ï¼ã
-**æ¨è**æå¨ç¼è¯ã以ä¸ç¼è¯æ¥éª¤æ¬è¿èªï¼https://wiki.osdev.org/GCC_Cross-Compiler
+**æ¨è**æå¨ç¼è¯ã以ä¸ç¼è¯æ¥éª¤æ¬è¿èªï¼
**é¦å
å®è£
æå»ºä¾èµé¡¹ï¼**
+
```bash
sudo apt update &&\
apt install -y \
@@ -232,10 +247,12 @@ sudo apt update &&\
```
**å¼å§ç¼è¯ï¼**
+
1. è·å[gcc](https://ftp.gnu.org/gnu/gcc/)å[binutils](https://ftp.gnu.org/gnu/binutils)æºç
2. è§£åï¼å¹¶å¨å级ç®å½ä¸ºgccåbinutilæ°å»ºä¸é¨çbuildæä»¶å¤¹
ç°å¨åè®¾ä½ çç®å½ç»æå¦ä¸ï¼
+
```
+ folder
+ gcc-src
@@ -247,25 +264,31 @@ sudo apt update &&\
3. ç¡®å®gccåbinutilå®è£
çä½ç½®ï¼å¹¶è®¾ç½®ç¯å¢åéï¼`export PREFIX=<å®è£
è·¯å¾>` ç¶å设置PATHï¼ `export PATH="$PREFIX/bin:$PATH"`
4. è®¾ç½®ç®æ å¹³å°ï¼`export TARGET=i686-elf`
5. è¿å
¥`binutils-build`è¿è¡é
ç½®
+
```bash
../binutils-src/configure --target="$TARGET" --prefix="$PREFIX" \
- --with-sysroot --disable-nls --disable-werror
+ --with-sysroot --disable-nls --disable-werror
```
+
ç¶å `make && make install`
6. ç¡®ä¿ä¸è¿°ç`binutils`å·²ç»æ£å¸¸å®è£
ï¼æ§è¡ï¼`which i686-elf-as`ï¼åºè¯¥ä¼ç»åºä¸ä¸ªä½äºä½ å®è£
ç®å½ä¸çè·¯å¾ã
6. è¿å
¥`gcc-build`è¿è¡é
ç½®
+
```bash
../gcc-src/configure --target="$TARGET" --prefix="$PREFIX" \
- --disable-nls --enable-languages=c,c++ --without-headers
+ --disable-nls --enable-languages=c,c++ --without-headers
```
+
ç¶åç¼è¯å®è£
ï¼å峿§è½ï¼å¤§çº¦10~20åéï¼ï¼
+
```bash
make all-gcc &&\
make all-target-libgcc &&\
make install-gcc &&\
make install-target-libgcc
```
+
8. éªè¯å®è£
ï¼æ§è¡`i686-elf-gcc -dumpmachine`ï¼è¾åºåºè¯¥ä¸ºï¼`i686-elf`
**å°æ°ç¼è¯å¥½çGCCæ°¸ä¹
æ·»å å°`PATH`ç¯å¢åé**
@@ -283,6 +306,7 @@ make all-gcc &&\
ç±äºç®åLunaixOS没æä¸ä¸ªå®å强大çå
æ ¸è¿½è¸ªåè½ãåè¥Lunaixçè¿è¡åºç°ä»»ä½é®é¢ï¼è¿è¯·æç
§ä»¥ä¸çæè¿°ï¼å¨Issueé颿ä¾è¯¦ç»çä¿¡æ¯ã
æå¥½æä¾ï¼
+
+ å¯ç¨äºå¤ç°é®é¢çæè¿°åæå¼ï¼å¦Lunaixè¿è¡å¹³å°ç软硬件é
ç½®ï¼
+ é误çç¶æè¿°
+ ï¼å¦å¯è½ï¼è¿è¡æªå¾
@@ -305,4 +329,4 @@ LunaixOSå
æ ¸éæäºæåºæ¬çGDBè¿ç¨è°è¯æå¡å¨ãå¯éè¿ä¸²å£COM
### GDBè°è¯æ³¨æäºé¡¹
-å¨è°è¯ä¸ï¼è¯·é¿å
使ç¨`info stack`ï¼`bt`æè
任使¶å **æ å±å¼ï¼Stack Unwindingï¼** æè
**æ åæº¯ï¼Stack Backtracingï¼** çæä»¤ãå¦åï¼LunaixOS徿å¯è½ä¼åºç° **ä¸å¯é¢æçè¡ä¸º** ã
\ No newline at end of file
+å¨è°è¯ä¸ï¼è¯·é¿å
使ç¨`info stack`ï¼`bt`æè
任使¶å **æ å±å¼ï¼Stack Unwindingï¼** æè
**æ åæº¯ï¼Stack Backtracingï¼** çæä»¤ãå¦åï¼LunaixOS徿å¯è½ä¼åºç° **ä¸å¯é¢æçè¡ä¸º** ã
diff --git a/docs/README_en.md b/docs/README_en.md
index 2a115ca..be03432 100644
--- a/docs/README_en.md
+++ b/docs/README_en.md
@@ -6,16 +6,16 @@
ç®ä½ä¸æ | English
-# The LunaixOS Project
+# The LunaixOS Project
+
LunaixOS - A simple (yet naive), POSIX-complaint (hopefully!), operating system from scratch. This is started for educational & learning purpose, for my online video tutorial on OS development **in Chinese**ï¼[*Do It Yourself an Operating System*](https://space.bilibili.com/12995787/channel/collectiondetail?sid=196337).
## Features
-This operating system is a macro-kernel, has its root in Intel's x86 platform and its ecosystem. It runs in protected mode and uses 4GiB addressing with two-level paging mechanism. It does not have x86_64 variant and does not support multi-core machine.
+This operating system is a macro-kernel, has its root in Intel's x86 platform and its ecosystem. It runs in protected mode and uses 4GiB addressing with two-level paging mechanism. It does not have x86_64 variant and does not support multi-core machine.
The virtual address space is divided into two parts, that is, 3GiB for user space (0x400000 ~ 0xBFFFFFFF) and 1GiB for kernel space (0xC0000000 ~ 0xFFFFFFFF). Such paradigm is a common practicing found in major operating systems, for example x86_32 version of Linux and Windows. For a more detailed arrangement of memory in LunaixOS, please refer to [LunaixOS Virtual Memory Mappings](img/lunaix-os-mem.png).
-
The following list presents all features it does have in current stage.
+ Multiboot for bootstrapping
@@ -31,9 +31,13 @@ The following list presents all features it does have in current stage.
+ PCI 3.0
+ PCIe 1.1 (WIP)
+ Serial ATA AHCI
-+ Virtual File System
++ File System
+ + Virtual File System
+ + ISO9660
+ + Original
+ + Rock Ridge Extension (WIP)
+ GDB Remote debugger (via UART)
-
+
The OS has been tested in the following environments, including both virtual and bare-metal.
+ QEMU (>=7.0.0)
@@ -101,7 +105,7 @@ To maximize the value of this section, we will provide some FAQ below that hopef
This is a issue related to misconfiguration of ACPI table in QEMU, and has been addressed in version 7.0.0.
-#### Q2: General Protection exception get triggered.
+#### Q2: General Protection exception get triggered
It is possible a race condition result from multiprogramming. This is not possible in current stage, and we however encourage you to report in case of it.
@@ -132,30 +136,35 @@ The following list also enumerated such materials the author has used:
+ PCI Express Base Specification, Revision 1.1
+ PCI Firmware Specification, Revision 3.0
+ Serial ATA - Advanced Host Controller Interface (AHCI), Revision 1.3.1
-+ Serial ATA: HIgh Speed Serialized AT Attachment, Revision 3.2
++ Serial ATA: High Speed Serialized AT Attachment, Revision 3.2
+ SCSI Command Reference Manual
+ ATA/ATAPI Command Set - 3 (ACS-3)
++ [ECMA-119 (ISO9660)](https://www.ecma-international.org/publications-and-standards/standards/ecma-119/)
++ Rock Ridge Interchange Protocol (RRIP: IEEE P1282)
++ System Use Sharing Protocol (SUSP: IEEE P1281)
**DISCLAIMER: All rights of PCI-related specification is reserved by PCI-SIG. It is provided ONLY for learning purpose. Any commercial use should purchase a copy from PCI-SIG**
#### Textbook
+
+ *Computer System - A Programmer's Perspective Third Edition* (Bryant, R & O'Hallaron, D), a.k.a. CS:APP
+ *Modern Operating System* (Tanenbaum, A)
+#### Website
-#### Website
+ [OSDev](https://wiki.osdev.org/Main_Page) - For material gathering.
+ [FreeVGA](http://www.osdever.net/FreeVGA/home.htm) - For VGA references.
+ GNU CC & LD online documentation.
+ [PCI Lookup](https://www.pcilookup.com/) - For device look up
#### Others
-+ Linux Manual - For learning the system call behavior on real machine.
++ Linux Manual - For learning the system call behavior on real machine.
## Appendix 1: Supported System Call
**Unix/Linux/POSIX**
+
1. `sleep(3)`
1. `wait(2)`
1. `waitpid(2)`
diff --git a/reference-material/ECMA-119_4th_edition_june_2019.pdf b/reference-material/ECMA-119_4th_edition_june_2019.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..935c2d98db42e303ea05313e9a7b14b6cbaadea4
GIT binary patch
literal 1397281
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