南大-ICS2021 PA3.1 学习笔记&记录

PA3.1

代码github网址:

https://github.com/xiao-tai/ics2021

最简单的操作系统

操作系统的一个主要任务就是"自动加载新程序", 即要在程序之间的执行流进行切换

  • Nanos-lite, 是南京大学操作系统Nanos的剪切版, 在AM看来, 它只是一个调用AM API的普通C程序而已.

  • 进程(正在运行的程序), 例:如果你打开了3次记事本, 计算机就会有3个记事本进程在运行, 但磁盘上的记事本程序只有一个.

一开始, 在nanos-lite/include/common.h中所有与实验进度相关的宏都没有定义, 此时Nanos-lite的功能十分简单. 我们来简单梳理一下Nanos-lite目前的行为:

  1. 打印Project-N的logo.

  2. 调用init_device()对设备进行一些初始化操作. 目前init_device()会直接调用ioe_init().

  3. 初始化ramdisk. 定义俩个uint_8变量ramdisk_start和ramdisk_end, '磁盘’的大小就为俩个变量地址的差.

  4. init_fs()init_proc(), 分别用于初始化文件系统和创建进程, 目前它们均未进行有意义的操作, 可以忽略它们.

  5. 调用panic()结束Nanos-lite的运行.

操作系统主要实现两点功能:

  • 用户程序执行结束之后, 可以跳转到操作系统的代码继续执行.

  • 操作系统可以加载一个新的用户程序来执行.

新需求: 需要进行程序流的切换, 还要当一个用户程序出错了, 操作系统可以运行下一个用户程序, 而不是整个系统崩溃.

等级制度

操作系统运行在较高的等级, 有权限访问所有的代码和数据, 一般的用户程序运行在较低的等级, 只能访问自己等级下的代码和数据. 模式的检查通常使用门电路来实现.

自陷指令

为了实现最简单的操作系统, 硬件还需要提供一种可以限制入口的执行流切换方式. 这种方式就是自陷指令, 程序执行自陷指令之后, 就会陷入到操作系统预先设置好的跳转目标. 这个跳转目标也称为异常入口地址.

穿越时空的旅程

riscv32提供了一些特殊的系统寄存器, 叫控制状态寄存器(CSR寄存器). 在PA中, 我们只使用如下3个CSR寄存器:

  • mtvec寄存器 - 存放异常的入口地址(ecall指令所使用)

  • mepc寄存器 - 存放触发异常的PC

  • mstatus寄存器 - 存放处理器的状态

  • mcause寄存器 - 存放触发异常的原因

riscv32触发异常后硬件的响应过程如下:

SR[mepc] <- PC
SR[mcause] <- 一个描述失败原因的号码
PC <- SR[mtvec]`

实现异常响应机制

问: AM究竟给程序提供了多大的栈空间呢?

答: 对于地址空间的大小, 都定义在am/src/platform/nemu/include/nemu.h中, 可能栈空间大小就是宏定义PMEM_SIZE?

首先, 在nemu/src/isa/riscv32/include/isa-def.h中添加上述提到的寄存器.

typedef struct {
  word_t mcause;
  vaddr_t mepc;
  word_t mstatus;
  word_t mtvec;
} riscv64_CSRs;

typedef struct {
  word_t gpr[32];
  vaddr_t pc;
  riscv64_CSRs csr;
} riscv64_CPU_state;

CTE定义了如下数据结构:

typedef struct
{
  enum
  {
    EVENT_NULL = 0,
    EVENT_YIELD, EVENT_SYSCALL, EVENT_PAGEFAULT, EVENT_ERROR,
    EVENT_IRQ_TIMER, EVENT_IRQ_IODEV,
  } event;
  uintptr_t cause, ref;
  const char *msg;
} Event;

nanos-lite中调用init_irq(), 接着调用am/src/riscv/nemu/cte.c中的cte_init来设置异常入口地址, 执行了csrw指令, 因为am/src/riscv/nemu/cte.c中的yield函数会调用ecall命令, 所以也需要再实现一下.

def_INSTR_IDTAB("??????? ????? ????? ??? ????? 11100 11", I     , csr);


def_THelper(csr) {
  def_INSTR_TAB("??????? ????? ????? 001 ????? ????? ??", csrrw);
  def_INSTR_TAB("??????? ????? ????? 010 ????? ????? ??", csrrs);    //识别csr的读写操作
  def_INSTR_TAB("0000000 00000 00000 000 00000 ????? ??", ecall);
  return EXEC_ID_inv;
}

接下来实现三个新指令, 新建一个csr.h, 注意要在nemu/src/isa/riscv32/include/isa-exec.h添加上新建的头文件.

static vaddr_t *csr_id_instr2address(word_t imm) {
  switch (imm)
  {
  case 0x341: return &(cpu.csr.mepc);
  case 0x342: return &(cpu.csr.mcause);
  case 0x300: return &(cpu.csr.mstatus);
  case 0x305: return &(cpu.csr.mtvec);
  default: panic("Unknown csr");
  }
}
#define csr(imm) csr_id_instr2address(imm)

def_EHelper(csrrw) {
    vaddr_t *csr = csr(id_src2->imm);
    vaddr_t temp = *csr;
    *csr = *dsrc1;
    *ddest = temp;
}

def_EHelper(csrrs) {
    vaddr_t *csr = csr(id_src2->imm);
    vaddr_t temp = *csr;
    *csr = temp | *dsrc1;
    *ddest = temp;
}

def_EHelper(ecall) {
    bool success = false;
    word_t trap_no = isa_reg_str2val("$a7", &success);
    if(!success)
        Assert(0, "Invalid gpr register");
    word_t trap_vec = isa_raise_intr(trap_no, s->snpc);
    rtl_j(s, trap_vec);
} 

根据触发异常后的响应过程实现isa_raise_intr()

word_t isa_raise_intr(word_t NO, vaddr_t epc) {
    cpu.csr.mcause = NO;
    cpu.csr.mepc = epc;
    return cpu.csr.mtvec;
}

后面的实在是太难了, 我这个小菜鸡选择跑路了…

参考博客

nju pa3 - NOSAE - 博客园 (cnblogs.com)

ICS PA3 实验记录 - 知乎 (zhihu.com)

ICS PA 3 (vgalaxy.work)

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值