Aros/开发者/内核基础
现在串行调试配置在哪里处理,因为大多数在 x86-64 上修复的内容似乎已被删除。arch/all-native/bootconsole。我把这段代码移到了共享库,因为它被引导程序和启动程序都使用。
如果 grub(例如)现在在内存地址 0 处加载模块,正如你在另一篇文章中提到的那样,它会破坏 rsdp 指针,因此我们无法在内存中找到 acpi 表。来自地址 0 的模块。这与我的更改无关。这就是 GRUB 的工作方式。它将模块放置在最适合的内存区域中。当然,它应该从可用地址范围内排除所有 BIOS 信息。如果你的启动程序覆盖了 BIOS 信息,那么 GRUB 内存管理中存在一个错误。以前,启动程序不是分成小块。单个大型模块根本无法放入 640KB 的常规内存中。
另外,如果在启动程序之前内存页面受到保护,那么无论如何我们都无法扫描它。只有两个区域受到保护
- 从地址 0 开始的一页。
- 启动程序的只读区域(.code + .rodata + struct KrnBootPrivate)。
ACPI/SMP 配置的部分必须在内核资源中完成(主要是表解析等,用于配置 AP,因为它们需要访问内核资源中使用的结构/代码)。我知道这一点。ACPI 解析器似乎在 exec/内核基础创建之前被调用。
你遇到的重启问题似乎与串行输出代码有关。我从旧的 x86-64 代码中复制了它,尽可能少地进行更改。为了证明它会导致错误,你可以尝试在没有串行调试的情况下启动。看起来确实如此 - 我现在可以启动得更远一些(但仍然会崩溃,可能是由于我自己的更改),并且可以在屏幕上看到更多调试输出,但是添加 debug=serial:0@115200 行会让它再次在同一位置锁定(跳转到内核是最后显示的输出)。
文本模式输出也没有测试。实际上,我在 EFI GRUB 设置的 VESA(帧缓冲区)模式下运行良好。与 PC 的区别是:Multiboot 信息不包含 VESA 模式和控制器信息,而只包含通用帧缓冲区数据。VESA 模式信息是由引导程序根据它创建的,因为 AROS 启动程序需要它。也许模式设置中存在一些错误。首先,我会确保所有输出(文本模式屏幕、VBE 模式屏幕和串行端口)在引导程序中都能正常工作。如果在所有模式下你都能够到达“离开 32 位环境”,那就没问题,libbootconsole 可以正常工作。启动程序会重新初始化输出,它使用相同的代码,但使用不同的初始化例程。它会解析引导标记列表而不是 Multiboot 信息。你也可以在 test/boot 中找到一个对你来说很有用的东西。这是一个测试 libbootconsole 的小程序。如果它无法正常工作,那么那里肯定存在问题。标记列表解析器中的错误可以通过手动使用硬编码参数初始化 libbootconsole 来调试,然后转储标记列表和相关结构。这就是我调试它的方法。
还有一个提示:串行输出可能会产生中断。也许这就是问题所在。
好的。在对本机 x86_64 进行了一些缓慢的调试后(不断添加 halt 并重新构建以查看图形调试输出...)似乎到目前为止存在以下问题...kernel/kernel_startup.c
kernel_cstart() /* * Fill zero page with garbage in order to detect accesses to it in supervisor mode. * For user mode we will disable all access to it. */ MUNGE_BLOCK(NULL, 0xABADCAFE, PAGE_SIZE);
会导致 GPF,之后...
core_ProtKernelArea(0, PAGE_SIZE, 0, 0, 0);
几乎任何对内存列表的访问都会导致页面错误。我还没有对此进行太多研究,但要么内存列表位于第一页,要么低内存的内存头位于第一页,因此所有试图分配等内存的操作最终都会命中它并崩溃。
好的,这些问题中的大多数似乎源于使用第一页内存(或在从 mmap 初始化 melist 时注册它)。我认为最好调整要注册的区域的起始地址,以确保它们大于 PAGE_SIZE。
至于调试问题 - 并非只有在我们离开引导程序时串行调试才会停止 - 如果引导程序的输出超过 4k,也会发生这种情况。看来从 Grub -> 引导程序 -> 内核传递的内存列表在传递到 kernel.resource 之前被破坏了。我会尝试更深入地挖掘一下,看看问题出现在哪里。
如果我在 VMWare 中尝试 - 在 VESA 模式下启动会导致 kernel.resource 崩溃。但是,如果我在 VGA 模式下启动,它只显示串行调试输出的信息(即前 4k 的调试,或者直到离开 32 位模式为止) - 似乎没有其他事情发生(即,vesa 会导致虚拟 CPU 死亡或停止,vga 似乎只是处于挂起状态)。
你是否有任何自定义配置或编译器/链接器标志可能会影响实现 64 -> 32 位交叉编译所需的标志?有人知道“i386:x86-64 架构”是什么意思吗?据我所知,这意味着它是 64 位。我怀疑 smpboot 文件已被编译为 64 位,而它正在尝试将其链接到 32 位代码中。我能看到的唯一真正区别是 rule_compile 默认使用目标编译器,而 rule_link_binary 使用内核链接器。
软重启目前在 pc-i386 上已损坏。当我在进入图形模式之前进行软重置时,我可以看到没有找到模块,并且引导在 SAD 提示符处结束。我添加了一个本地调试语句,它打印出 exec_RomTagScanner() 开始搜索的地址:在 QEMU 上,初始启动时的地址是 0x07e70000,而软重启时的地址是 0x07fec170。这正常吗?不,这很不正常。我需要在 i386 本机执行中添加一些代码,它会保存引导程序中的引导消息标记列表的副本。没有它,软重启将仍然无法正常工作...
Exec 的量子值是任务可以运行的时间量吗?这个值是基于 vblank 频率的吗?是的,就是这样。每个 VBlank 量子都会递减。当它达到零时,就会切换任务。
所以任务可以拥有的最小量子是 1/60 秒吗?实际上是 1 / 50。(如果它没有自行放弃控制权)。而且,如果没有任何任务放弃控制权(并且量子 = 1),最多可以有 60 个“任务”获得运行的机会?每秒 60 次。不是系统中最多可以有 60 个任务。没错,每秒 60 次,“下一个等待”任务将获得运行的机会。它不依赖于任务数量。只有一个任务运行了 4 个 VBlank。据我所知,情况甚至更糟,因为任务一开始就会获得量子 = 4,所以每秒只有 15 次任务切换。既对又不对。如果任务没有进行任何会导致它休眠/等待的操作系统调用,那么它将会很低。不过,为了比较,Linux 以前默认设置高达 100(大约在 100 年前),现在默认设置为 250,并且可以配置为每秒执行高达 1000 次计时器中断。任务可以获得比一个量子(在 Linux 中称为一个 jiffy)更长的时隙。
我还没有机会研究 vblank 中断计时器是如何实现的,但我认为我们应该有一些方法让它注册一个“A”计时器源(以某种方式记录其精度),并让 exec 从可用计时器源中选择在启动时使用的计时器。
此外,其他计时器源应该能够在 exec 选择了一个计时器源之后注册,并且它们应该能够替换当前正在使用的计时器源。
我当时的想法是,正在使用的计时器应该有一个挂钩,它会存储(基本上是设置下一个“事件”),并且每次事件触发时,它都会调用这个挂钩以再次设置。如果另一个计时器(具有更高的精度)加载它,那么它就可以禁止多任务处理,用与它相关的量子值替换量子值,并将指向现有挂钩的指针替换为一个用于设置它自己的事件的指针。
那么,先前计时器源的下一个事件将使它恢复活动...
我认为我们还需要使用一些值(类似于 bogomips)以及量子的粒度来决定量子的合适实际值。
如果我的推论是正确的,那么使用的值(以及量子的粒度)似乎并不特别适合现代处理器 - 因此我想知道是否有办法为量子设置更精细的粒度?那会很棒。现在发生的情况是,当运行 GL 或 SDL 演示时,系统变得无响应(尤其是在涉及 IO 的任务中,例如打开 Wanderer 中的抽屉或加载其他软件)(甚至移动窗口也更慢)。但是,当我将优先级设置为 -1 时,这些演示的帧率只受到轻微影响,但系统仍然可以完全使用。你需要一个频率更高的计时器。请注意两件事
- 你不能提高 VBlank 的频率,否则你会破坏许多软件中的延迟。
- 一些架构(目前只有托管的架构)可能具有高频周期性计时器。它用于通过 timer.device 测量间隔,以及模拟 VBlank。但是,出于兼容性原因,量子仍然以 VBlank 衡量。
除了 Amiga 之外,原生架构没有这样的计时器,因为它们的硬件(至少 AROS 支持的硬件)只有一个计时器,由 timer.device 控制,timer.device 模拟了 VBlank 本身。Amiga 具有独立的硬件 VBlank 计时器,可以作为独立周期性计时器的例子,但是它的频率无法提高。i386-pc 端口存在问题(因为代码没有合并)。它仍然使用旧的 INTB_TIMERTICK 技巧,实际上是非周期性的(因为 timer.device 的工作方式),并且经过时间在它的处理程序中递减,而不是在 VBlank 处理程序中递减。这样,量子实际上在这个端口上是浮动的,取决于活动的计时器请求。我认为这需要修复。量子应该以一些确定性的单位测量。标准的 Amiga 调度程序以 VBlank 测量它,因此我的通用代码使用相同的单位。
这意味着您将不得不通过类似于 VBlank 的方式模拟您的高频计时器。我听说过“HPET”(高精度事件计时器),但是我不知道它有什么硬件功能。再说一次,至少在 UNIX 宿主上,您必须模拟它,因为 UNIX 只有计时器(SIGALRM)。您可以随意尝试,这是一个未开发的领域。请记住,kernel.resource 允许使用不同的调度程序(KrnSetScheduler() 函数),因此您可以随意实现自己的调度程序。HPET 是 x86 和 x86_64 上的标准高精度计时器,是传统 PC 架构基于 8254 的计时器的演变。
Wikipedia: "[http://software.intel.com/en-us/forums/showthread.php?t=52108 HPET] is meant to supplement and replace the 8254 programmable interval timer and the RTC's periodic interrupt function. Compared to these older timer circuits, the HPET has higher frequency (at least 10 MHz) and wider 64-bit counters (although they can be driven in 32-bit mode).[1]" There is also a specification for high-resolution POSIX timers, the IEEE 1003.1b REALTIME spec.
另一种改进方法是添加一个选项,暂时提升从等待特定类型事件返回的任务。例如,等待端口并唤醒以处理来自高优先级任务的消息的任务,可以被分配一个更大的量子,作为一次性处理它(例如,处理来自直觉/输入处理程序的消息)。Linux 试图半途而废地做到这一点,但他们对“特殊情况”很反感,而且由于在 Linux 中 GUI 是“另一个进程”,因此他们很难以一种没有丑陋的边缘情况的方式做到这一点,但如果使用事件来源的知识,您可以让系统始终(在一定程度上)优先考虑用户发起的事件。不过,在更高速度的 CPU 上缩短量子的长度仍然是一个好主意。或者使其可配置。
目前正在尝试清理 x86_64 端口中的 LAPIC 等代码(在我的本地树中),以便我开始使用它。我的第一个“目标”是完成 AP 的配置,以便 APIC、GDT、TLB、PTE 等被配置,并且中断在相关的核心上被处理。请更新您的树。我重写了 x86-64 内核并合并了代码。顺便说一下,我记得 acpi.resource 正在开发中。我记得在一个分支中。将 ACPI 内容从 kernel.resource 移动到那里怎么样?没有必要从与引导程序相同的点运行次要核心。
- 使用 XT 计时器作为 timer.device。
- 在 kernel.resource 中使用 HPET 作为高频周期性计时器。然后,VBlank 将由内核模拟。
内核周期性计时器的当前支持在 kernel_timer.c 中。我已经告诉过您调度程序切换函数。仅此而已。如果需要更多内容,应该由将要实现该内容的人来开发。:)
也许吧。但是这个特定的任务可以用更简单的方式解决。您只需要一个独立于 timer.device 的周期性计时器。假设它的频率是 VBlank 的倍数,您可以使用现有的 kernel.resource 代码。在 timer.device 的硬件实现中,VBlank 通过反复排队计时器请求来模拟。kernel.resource 的模拟在这些架构上没有实现。此外,x86-64 timer.device 通过将 KAttr_VBlankEnable 设置为 FALSE 来确保它处于关闭状态。实际上,所有实现都应该这样做。顺便说一下,您可以尝试在宿主上通过提高它的默认周期性计时器频率(目前默认情况下它等于 VBlank)并将其用作调度源来进行实验。为 exec.library 发明一些算法来选择它。您可以使用 KrnGetSystemAttr() 函数和 KAttr_TimerIRQ 属性向 kernel.resource 查询此中断。此计时器的频率由内核放入 SysBase->ex_EClockFrequency 中,您可以依赖这个事实。这也由通用 timer.device 实现使用。它不驱动任何硬件,只是简单地计数计时器中断。
好吧,我使用的是 Gimmearos 脚本,它执行
export CC=$gccversion" -m32" "../$srcdir/configure" --target=pc-i386 --with-portssources="$curdir/$portsdir"
第一行应该是没有必要的。标准配置脚本在适当的情况下设置“-m32”。虽然我无法看到这会导致您的错误,但可能存在其他变量被 gimmearos 覆盖。
您是否尝试过更改..
%rule_compile basename=smpbootstrap targetdir=$(OBJDIR)
到..
%rule_compile basename=smpbootstrap targetdir=$(OBJDIR) compiler=kernel
?
在 config/make.tmpl 中的 %rule_link_binary 中是否应该声明 -melf_i386?至少不在宏中,但可能已传递