+ 远程GDB串口调试 (COM1@9600Bd)
+ 用户程序加载与执行
+ 动态链接 (WIP)
++ 通用设备抽象层
++ 通用图形设备抽象层
+ + 标准VGA实现
已经测试过的环境:
**※:由于在`-O2`模式下,GCC会进行CSE优化,这导致LunaixOS会出现一些非常奇怪、离谱的bug,从而影响到基本运行。具体原因有待调查。**
-## 运行,分支以及Issue
+## 运行,分支以及 Issue
运行该操作系统需要一个虚拟磁盘镜像,可以使用如下命令快速创建一个:
正常,**因为Bochs不支持SATA**。请使用QEMU或VirtualBox。
+## 调试 Lunaix 内核
+
+除了[附录4:串口GDB远程调试](#appendix4)描述的一种用于实机调试的方式以外。LunaixOS还提供了LunaDBG调试套件。这是一个GDB客户端插件,包含了对GDB原生命令集的一些扩充,主要用于改善与简化内核调试的过程。目前包含以下几个命令:
+
++ `vmrs [pid]` 列举进程`<pid>`的内存区域图(Memory Regions),如果`<pid>`未指定,则默认为正在运行的进程(smp=1)。
++ `proc [pid]` 打印进程`<pid>`的进程控制块状态,如果`<pid>`未指定,则默认为正在运行的进程(smp=1)。
++ `proc_table` 列举所有非终止的进程以及他们的状态。
+
+该插件可以通过运行以下命令来进行安装:
+
+```shell
+./scripts/gdb/install_lunadbg
+```
+
## 参考教程
**没有!!** 本教程以及该操作系统均为原创,没有基于任何市面上现行的操作系统开发教程,且并非是基于任何的开源内核的二次开发。
+ *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)
#### 网站
+ (如可能)提供错误发生时,EIP附近的指令(精确到函数)。如果使用`make all-debug`,会提供`build/kdump.txt`,你可以在这里面定位。或者也可以直接`objdump`
+ (如可能)虚拟内存映射信息(QEMU下可使用`info mem`查看)。
-## 附录4:串口GDB远程调试
+## 附录4:串口GDB远程调试<a id="appendix4"></a>
LunaixOS内核集成了最基本的GDB远程调试服务器。可通过串口COM1在9600波特率上与之建立链接。但是,在将GDB与内核链接起来之前,还需要让内核处在调试模式下。
draft/
iso_inspect/
unused/
+__pycache__/
.gdb_history
// cleanup
isrm_ivfree(iv_timer);
+ ticks_t tphz = base_freq / frequency;
+ timer->base_freq = base_freq;
+ apic_write_reg(APIC_TIMER_ICR, tphz);
+
apic_write_reg(
APIC_TIMER_LVT,
LVT_ENTRY_TIMER(isrm_ivexalloc(apic_timer_tick_isr), LVT_TIMER_PERIODIC));
-
- timer->base_freq = base_freq;
-
- ticks_t tphz = base_freq / frequency;
- apic_write_reg(APIC_TIMER_ICR, tphz);
}
struct hwtimer*
region_get(vm_regions_t* lead, unsigned long vaddr);
void
-region_copy(struct proc_mm* src, struct proc_mm* dest);
+region_copy_mm(struct proc_mm* src, struct proc_mm* dest);
+
+struct mm_region*
+region_dup(struct mm_region* origin);
#endif /* __LUNAIX_REGION_H */
#include <lunaix/time.h>
#include <sys/interrupts.h>
-#define SYS_TIMER_FREQUENCY_HZ 1024
+#define SYS_TIMER_FREQUENCY_HZ 1000
#define TIMER_MODE_PERIODIC 0x1
p->execp->vector,
p->execp->err_code);
- trace_print_code_entry(sym->pc, p->execp->eip, ksym_getstr(sym));
+ ptr_t sym_pc = sym ? sym->pc : p->execp->eip;
+ trace_print_code_entry(sym_pc, p->execp->eip, ksym_getstr(sym));
}
void
if (pos->start > found_loc && avail_space > param->mlen) {
goto found;
}
- found_loc = pos->end + PG_SIZE;
+ found_loc = pos->end + MEM_PAGE;
}
last_end = pos->end;
region_release(region);
}
+// Case: head inseted, tail inseted
+#define CASE_HITI(vmr, addr, len) \
+ ((vmr)->start <= (addr) && ((addr) + (len)) <= (vmr)->end)
+
+// Case: head inseted, tail extruded
+#define CASE_HITE(vmr, addr, len) \
+ ((vmr)->start <= (addr) && ((addr) + (len)) > (vmr)->end)
+
+// Case: head extruded, tail inseted
+#define CASE_HETI(vmr, addr, len) \
+ ((vmr)->start > (addr) && ((addr) + (len)) <= (vmr)->end)
+
+// Case: head extruded, tail extruded
+#define CASE_HETE(vmr, addr, len) \
+ ((vmr)->start > (addr) && ((addr) + (len)) > (vmr)->end)
+
+static void
+__unmap_overlapped_cases(struct mm_region* vmr, ptr_t* addr, size_t* length)
+{
+ // seg start, umapped segement start
+ ptr_t seg_start = *addr, umps_start = 0;
+
+ // seg len, umapped segement len
+ size_t seg_len = *length, umps_len = 0;
+
+ size_t displ = 0, shrink = 0;
+
+ if (CASE_HITI(vmr, seg_start, seg_len)) {
+ size_t new_start = seg_start + seg_len;
+ if (new_start < vmr->end) {
+ struct mm_region* region = region_dup(vmr);
+ if (region->mfile) {
+ size_t f_shifted = new_start - region->start;
+ region->foff += f_shifted;
+ region->flen = MAX(region->flen, f_shifted) - f_shifted;
+ }
+ region->start = new_start;
+ llist_insert_after(&vmr->head, ®ion->head);
+ }
+
+ shrink = vmr->end - seg_start;
+ umps_len = shrink;
+ umps_start = seg_start;
+ } else if (CASE_HITE(vmr, seg_start, seg_len)) {
+ shrink = vmr->end - seg_start;
+ umps_len = shrink;
+ umps_start = seg_start;
+ } else if (CASE_HETI(vmr, seg_start, seg_len)) {
+ displ = seg_len - (vmr->start - seg_start);
+ umps_len = displ;
+ umps_start = vmr->start;
+ } else if (CASE_HETE(vmr, seg_start, seg_len)) {
+ shrink = vmr->end - vmr->start;
+ umps_len = shrink;
+ umps_start = vmr->start;
+ } else {
+ fail("invalid case");
+ }
+
+ vmr->start += displ;
+ vmr->end -= shrink;
+
+ if (vmr->start >= vmr->end) {
+ llist_delete(&vmr->head);
+ region_release(vmr);
+ } else if (vmr->mfile) {
+ vmr->foff += displ;
+ vmr->flen = MAX(vmr->flen, displ) - displ;
+ }
+
+ *addr = umps_start + umps_len;
+
+ size_t ump_len = *addr - seg_start;
+ *length = MAX(seg_len, ump_len) - ump_len;
+}
+
int
mem_unmap(ptr_t mnt, vm_regions_t* regions, ptr_t addr, size_t length)
{
llist_for_each(pos, n, regions, head)
{
- if (pos->start <= cur_addr && pos->end >= cur_addr) {
+ u32_t l = pos->start - cur_addr;
+ if ((pos->start <= cur_addr && cur_addr < pos->end) || l <= length) {
break;
}
}
- while (&pos->head != regions && cur_addr >= pos->start) {
- u32_t l = pos->end - cur_addr;
- pos->end = cur_addr;
-
- if (l > length) {
- // unmap cause discontinunity in a memory region - do split
- struct mm_region* region = valloc(sizeof(struct mm_region));
- *region = *pos;
- region->start = cur_addr + length;
- llist_insert_after(&pos->head, ®ion->head);
- l = length;
- }
-
- mem_sync_pages(mnt, pos, cur_addr, l, 0);
-
- for (size_t i = 0; i < l; i += PG_SIZE) {
- ptr_t pa = vmm_del_mapping(mnt, cur_addr + i);
- if (pa) {
- pmm_free_page(pos->proc_vms->pid, pa);
- }
- }
-
+ while (&pos->head != regions && length) {
n = container_of(pos->head.next, typeof(*pos), head);
- if (pos->end == pos->start) {
- llist_delete(&pos->head);
- region_release(pos);
- }
+ __unmap_overlapped_cases(pos, &cur_addr, &length);
pos = n;
- length -= l;
- cur_addr += length;
}
return 0;
#include <lunaix/mm/valloc.h>
#include <lunaix/spike.h>
+#include <sys/mm/mempart.h>
+
#include <klibc/string.h>
struct mm_region*
struct mm_region* region = valloc(sizeof(struct mm_region));
*region = (struct mm_region){ .attr = attr,
.start = start,
- .end = PG_ALIGN(start + length - 1) };
+ .end = ROUNDUP(start + length, MEM_PAGE) };
+ return region;
+}
+
+struct mm_region*
+region_dup(struct mm_region* origin)
+{
+ struct mm_region* region = valloc(sizeof(struct mm_region));
+ *region = *origin;
+
+ if (region->mfile) {
+ vfs_ref_file(region->mfile);
+ }
+
+ llist_init_head(®ion->head);
return region;
}
return;
}
+ struct mm_region *pos = (struct mm_region*)lead->next,
+ *n = list_entry(pos->head.next, struct mm_region, head);
ptr_t cur_end = 0;
- struct mm_region *pos = (struct mm_region*)lead,
- *n = list_entry(lead->next, struct mm_region, head);
- do {
- if (vmregion->start > cur_end && vmregion->end < n->start) {
+
+ llist_for_each(pos, n, lead, head)
+ {
+ if (vmregion->start >= cur_end && vmregion->end <= pos->start) {
break;
}
- cur_end = n->end;
- pos = n;
- n = list_entry(n->head.next, struct mm_region, head);
- } while ((ptr_t)&n->head != (ptr_t)lead);
+ cur_end = pos->end;
+ }
// XXX caution. require mm_region::head to be the lead of struct
- llist_insert_after(&pos->head, &vmregion->head);
+ llist_append(&pos->head, &vmregion->head);
}
void
}
void
-region_copy(struct proc_mm* src, struct proc_mm* dest)
+region_copy_mm(struct proc_mm* src, struct proc_mm* dest)
{
struct mm_region *pos, *n, *dup;
llist_for_each(pos, n, lead, head)
{
- if (pos->start <= vaddr && vaddr <= pos->end) {
+ if (pos->start <= vaddr && vaddr < pos->end) {
return pos;
}
}
}
__copy_fdtable(pcb);
- region_copy(&__current->mm, &pcb->mm);
+ region_copy_mm(&__current->mm, &pcb->mm);
/*
* store the return value for forked process.
#include <sys/interrupts.h>
#include <sys/mm/mempart.h>
-#include <sys/cpu.h>
#include <hal/intc.h>
+#include <sys/cpu.h>
#include <lunaix/fs/taskfs.h>
#include <lunaix/mm/cake.h>
{
struct proc_info* leader = sched_ctx._procs[0];
struct proc_info *pos, *n;
- time_t now = clock_systime();
+ time_t now = clock_systime() / 1000;
llist_for_each(pos, n, &leader->sleep.sleepers, sleep.sleepers)
{
if (proc_terminated(pos)) {
return 0;
}
+ time_t systime = clock_systime() / 1000;
+
if (__current->sleep.wakeup_time) {
- return (__current->sleep.wakeup_time - clock_systime()) / 1000U;
+ return (__current->sleep.wakeup_time - systime);
}
struct proc_info* root_proc = sched_ctx._procs[0];
- __current->sleep.wakeup_time = clock_systime() + seconds;
+ __current->sleep.wakeup_time = systime + seconds;
if (llist_empty(&__current->sleep.sleepers)) {
llist_append(&root_proc->sleep.sleepers, &__current->sleep.sleepers);
__DEFINE_LXSYSCALL1(unsigned int, alarm, unsigned int, seconds)
{
time_t prev_ddl = __current->sleep.alarm_time;
- time_t now = clock_systime();
+ time_t now = clock_systime() / 1000;
__current->sleep.alarm_time = seconds ? now + seconds : 0;
llist_append(&root_proc->sleep.sleepers, &__current->sleep.sleepers);
}
- return prev_ddl ? (prev_ddl - now) / 1000 : 0;
+ return prev_ddl ? (prev_ddl - now) : 0;
}
__DEFINE_LXSYSCALL1(void, exit, int, status)
ticks_t t = hwtimer_current_systicks();
ticks_t tu = systimer->running_freq / 1000;
- return t / (tu + 1);
+
+ if (unlikely(!tu)) {
+ return t;
+ }
+
+ return t / (tu);
}
void
timer_ctx->base_frequency = hwtimer_base_frequency();
- sched_ticks = SYS_TIMER_FREQUENCY_HZ / 1000 * SCHED_TIME_SLICE;
+ sched_ticks = (SYS_TIMER_FREQUENCY_HZ * SCHED_TIME_SLICE) / 1000;
sched_ticks_counter = 0;
}
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import sys
+from os import path
+
+
+directory, file = path.split(__file__)
+directory = path.expanduser(directory)
+directory = path.abspath(directory)
+
+sys.path.append(directory)
+
+import lunadbg
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+echo "source \"${SCRIPT_DIR}/gdbinit.py\"" >> ~/.gdbinit
\ No newline at end of file
--- /dev/null
+from .commands import load_commands
+
+load_commands()
\ No newline at end of file
--- /dev/null
+from .region_dump import MemoryRegionDump
+from .proc_table_dump import ProcessDump, ProcessTableDump
+
+
+def load_commands():
+ MemoryRegionDump()
+ ProcessTableDump()
+ ProcessDump()
\ No newline at end of file
--- /dev/null
+import gdb
+from .utils import pid_argument
+
+class ProcessHelper:
+ PS_READY = 0
+ PS_RUNNING = 1
+ PS_TERMNAT = 2
+ PS_DESTROY = 4
+ PS_PAUSED = 8
+ PS_BLOCKED = 16
+ PS_CREATED = 32
+
+ def get_state(proc: gdb.Value):
+ state_t = proc["state"]
+ if (state_t == ProcessHelper.PS_READY):
+ return "ready"
+ if (state_t == ProcessHelper.PS_RUNNING):
+ return "running"
+ if (state_t & (ProcessHelper.PS_TERMNAT | ProcessHelper.PS_DESTROY)):
+ return "terminated"
+ if (state_t & ProcessHelper.PS_BLOCKED):
+ return "blocked"
+ if (state_t & ProcessHelper.PS_PAUSED):
+ return "paused"
+
+ def process_at(pid):
+ return gdb.parse_and_eval(pid_argument(pid))
+
+ @staticmethod
+ def pp_process(proc: gdb.Value):
+ print(" pid:", proc["pid"])
+ print(" pgid:", proc["pgid"])
+ if proc["parent"] == 0:
+ print(" root process")
+ else:
+ print(" ppid:", proc["parent"]["pid"])
+ print(" page table:", proc["page_table"])
+
+ print(" state:", ProcessHelper.get_state(proc))
+ print(" created: +%dms"%(proc["created"]))
+ print(" saved context:")
+ print(" %s"%(proc["intr_ctx"].dereference()
+ .format_string(max_depth=3, format='x')
+ .replace('\n', '\n ')))
+
+
+class ProcessDump(gdb.Command):
+ """Dump the state of Lunaix PCB"""
+ def __init__(self) -> None:
+ super().__init__("proc", gdb.COMMAND_USER)
+
+ def invoke(self, argument: str, from_tty: bool) -> None:
+ argument = pid_argument(argument)
+
+ proc = gdb.parse_and_eval(argument)
+
+ ProcessHelper.pp_process(proc)
+
+class ProcessTableDump(gdb.Command):
+ """Dump the state of Lunaix process table"""
+ def __init__(self) -> None:
+ super().__init__("proc_table", gdb.COMMAND_USER)
+
+ def invoke(self, argument: str, from_tty: bool) -> None:
+ sched_context = gdb.parse_and_eval("sched_ctx")
+ total_entries = sched_context["ptable_len"]
+ print("inited entries: %d"%(total_entries))
+ print("scheduled pid: %d"%(sched_context["procs_index"]))
+ print("Process Table:")
+
+ for i in range(0, total_entries):
+ p = ProcessHelper.process_at(i)
+ if (p == 0):
+ continue
+ state = ProcessHelper.get_state(p)
+ print(" pid:%02d [%s]"%(i, state))
\ No newline at end of file
--- /dev/null
+import gdb
+from .utils import pid_argument
+
+class MemoryRegionDump(gdb.Command):
+ """Dump virtual memory regions associated with a process"""
+ def __init__(self) -> None:
+ super().__init__("vmrs", gdb.COMMAND_USER)
+
+ def invoke(self, argument: str, from_tty: bool) -> None:
+ argument = pid_argument(argument)
+
+ pid = gdb.parse_and_eval(f"{argument}->pid")
+
+ argument = f"&{argument}->mm.regions"
+ val = gdb.parse_and_eval(argument)
+ head = val
+
+ region_t = gdb.lookup_type("struct mm_region").pointer()
+
+ print("VMRS (pid: %d)"%(pid))
+
+ i = 0
+ while(val["next"] != head):
+ region = val["next"].cast(region_t)
+ print(f"VMR #{i}:")
+ print( " 0x%x...0x%x [0x%x]"%(
+ region['start'], region['end'],
+ region['end'] - region['start']))
+
+ attr = region["attr"]
+ attr_str = []
+ if (attr & (1 << 2)):
+ attr_str.append("R")
+ if (attr & (1 << 3)):
+ attr_str.append("W")
+ if (attr & (1 << 4)):
+ attr_str.append("X")
+ print( " attr: 0x%x (%s)"%(attr, "".join(attr_str)))
+
+ file = region["mfile"]
+ if file == 0:
+ print( " anonymous region")
+ else:
+ print( " file mapped:")
+ print( " dnode: %s @0x%x"%(file["dnode"]["name"]["value"].string(), file))
+ print( " frange: 0x%x+0x%x"%(region["foff"], region["flen"]))
+
+ val = val["next"]
+ i+=1
--- /dev/null
+
+def pid_argument(argument):
+ if not argument:
+ return "__current"
+ else:
+ return f"sched_ctx._procs[({argument})]"
\ No newline at end of file
SECTIONS {
. = 0x400000;
- .text : {
+ .text BLOCK(4K) : {
*(.text)
}
- .data : {
+ .data BLOCK(4K) : {
*(.data)
}
- .ro.data : {
+ .ro.data BLOCK(4K) : {
*(.ro.data)
}
- .bss : {
+ .bss BLOCK(4K) : {
*(.bss)
}
}