我已经查找过 X86-64 处理器的 NUMA 文档,不幸的是我只找到了 NUMA 的优化文档。
我想要的是:如何在系统中初始化 NUMA(这包括获取系统的内存拓扑和处理器拓扑)。有谁知道有关 X86-64 AMD 和 Intel 处理器的 NUMA 的好文档吗?
我正在尝试从 NASM 调用外部 C++ 函数。当我在谷歌上搜索时,我没有找到任何相关的解决方案。
C++
void kernel_main()
{
char* vidmem = (char*)0xb8000;
/* And so on... */
}
Run Code Online (Sandbox Code Playgroud)
国家安全管理协会
;Some calls before
section .text
;nothing special here
global start
extern kernel_main ;our problem
Run Code Online (Sandbox Code Playgroud)
运行编译这两个文件后,我收到此错误:kernel.asm(.text+0xe): undefined reference to kernel_main'
这里有什么问题?谢谢。
我正在用汇编语言开发一个操作系统。\n在某个时间我从 NASM 收到此错误:
\n\n\n\n\nos.asm:113: 错误: TIMES 值 -138 为负数
\n
我想把这个项目进行到底。只有这样的错误才让我绝望!
\n\n这是代码:
\n\nBITS 16\n\nstart:\n mov ax, 07C0h ; Set up 4K stack space after this bootloader\n add ax, 288 ; (4096 + 512) / 16 bytes per paragraph\n mov ss, ax\n mov sp, 4096\n mov ax, 07C0h ; Set data segment to where we're loaded\n mov ds, ax\n call cls\n MOV AH, 06h ; Scroll up function\n XOR AL, AL ; Clear entire screen\n XOR …Run Code Online (Sandbox Code Playgroud) 阅读 Intel 和 AMD 之间的文档并查看代码有时会让人很难理解如何创建没有 IO 端口位图 (IOPB) 的正确任务状态段 (TSS)。使用 IOPB 创建 TSS 似乎也存在混淆,因为 IO 位图 (IOPB) 是否需要尾随字节似乎不明确0xff。
我知道 TSS 和 TSS 描述符(在 GDT 中)之间存在依赖关系。TSS 描述符控制 TSS 的基地址以及限制。描述符中的限制比结构的实际大小小一(本质上类似于 GDT 和 IDT 记录中指定的大小)。TSS 限制用于确定 IOPB 大小。
我知道:
我主要用 C++ 制作操作系统,但对于引导加载程序,我使用的是 FASM。当我尝试设置 GDT 时,Qemu 会清除屏幕并在顶部重新打印“SeaBIOS”。它会一直循环下去,直到我关闭它。这是它的gif:

我尝试使用 -nographic 运行它,但它在 Windows 控制台中执行相同的操作。
哦,是的,操作系统/版本信息。
Windows:20H2
FASM:1.73.25
Qemu:5.1.0
这是我的代码:
gdt_start:
dd 0x00
dd 0x00
gdt_code:
dw 0xffff
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
switch_to_pm:
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, …Run Code Online (Sandbox Code Playgroud) 我最近更换了我的计算机,从那时起,我的 makefile 链会输出一个 512 字节的二进制文件,其中只有 0x00s 或引导加载程序,但没有其他任何内容。我将以下内容创建为 MRE:
引导.asm:
BITS 16
SECTION boot
GLOBAL _entry
EXTERN _start
_entry:
mov [disk],dl
mov ah, 0x2 ; read sectors
mov al, 6 ; amount = 6
mov ch, 0 ; zylinder = 0
mov cl, 2 ; first sector to read = 2
mov dh, 0 ; head = 0 (up)
mov dl, [disk] ; disk
mov bx, _start ; segment:offset address
int 0x13
cli
lgdt [GDT_POINTER]
mov eax, cr0
or al, 1 …Run Code Online (Sandbox Code Playgroud) 我正在使用 Rust 编写自己的内核(大致基于 phil-opp 的博客,https://os.phil-opp.com/),到目前为止,我已经成功复制了 4 级页表,为用户模式应用程序代码和数据,将裸函数映射到虚拟地址0x40000000000,设置堆栈并跳转到代码。我还有一个使用 syscall/sysret 工作的系统调用处理程序,它在遇到系统调用时简单地打印一条消息。我注意到,每当 PIC 定时器中断发生时,它总是在 CPL=0 时发生,因为系统调用处理程序的代码比用户模式应用程序长很多倍,后者只是在无限循环中执行系统调用。如果我禁用打印(这需要大多数指令),则在循环数百次迭代之后,当 CPL=3 时会发生计时器中断。然而,CPU 没有调用中断处理程序,而是抛出一个页面错误,错误代码为 2(仅对应于写入位设置)。在我看来,这没有任何意义,问题可能出在哪里?
总表:
static ref GDT: (gdt::GlobalDescriptorTable, Selectors) = {
let mut gdt = gdt::GlobalDescriptorTable::new();
let kernel_code_selector = gdt.add_entry(gdt::Descriptor::kernel_code_segment());
let kernel_data_selector = gdt.add_entry(gdt::Descriptor::kernel_data_segment());
let tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&TSS));
let user_data_selector = gdt.add_entry(gdt::Descriptor::user_data_segment());
let user_code_selector = gdt.add_entry(gdt::Descriptor::user_code_segment());
(gdt, Selectors { kernel_code_selector, kernel_data_selector, tss_selector, user_code_selector, user_data_selector })
};
Run Code Online (Sandbox Code Playgroud)
离散时间:
const DOUBLE_FAULT_IST_INDEX: u16 = 0;
let mut IDT: idt::InterruptDescriptorTable = idt::InterruptDescriptorTable::new();
IDT.breakpoint.set_handler_fn(interrupts::breakpoint::breakpoint_handler);
IDT.double_fault.set_handler_fn(interrupts::double_fault::double_fault_handler).set_stack_index(DOUBLE_FAULT_IST_INDEX);
IDT.page_fault.set_handler_fn(interrupts::page_fault::page_fault_handler);
IDT.general_protection_fault.set_handler_fn(interrupts::general_protection_fault::general_protection_fault_handler);
IDT.stack_segment_fault.set_handler_fn(interrupts::stack_segment_fault::stack_segment_fault_handler);
IDT.segment_not_present.set_handler_fn(interrupts::segment_not_present::segment_not_present_handler); …Run Code Online (Sandbox Code Playgroud) 我编写 x86 引导加载程序已经有一段时间了,并且已经验证我的引导加载程序可以在 bochs 和 qemu 以及 vmware 上运行。但是,我在真实硬件 (Thinkpad T400) 上遇到磁盘读取错误,错误代码为 0x0880,但在 qemu 上却没有。
[bits 16]
[extern kernel]
[global isr_stub_table]
[global IDT_load]
[extern FortressLoader_IdtInit]
[extern FortressLoader_ChecmMemMap]
[extern X86TTY_Init]
[extern X86TTY_Clear]
[extern printf]
[global FortressLoader_HeapStart]
[global FortressLoader_HeapEnd]
[global startKernel]
section .boot
global boot
KERNEL_SIZE_SECTORS equ 128
jmp boot
TIMES 3-($-$$) DB 0x90
OEMname: db "mkfs.fat"
bytesPerSector: dw 512
sectPerCluster: db 1
reservedSectors: dw 1
numFAT: db 2
numRootDirEntries: dw 224
numSectors: dw 2880
mediaType: db 0xf0
numFATsectors: dw 9
sectorsPerTrack: …Run Code Online (Sandbox Code Playgroud) 我编写了一些初始化IDT的代码,该IDT将32位地址存储在两个不相邻的16位半部分中。IDT可以存储在任何地方,您可以通过运行LIDT指令告诉CPU在哪里。
这是初始化表的代码:
void idt_init(void) {
/* Unfortunately, we can't write this as loops. The first option,
* initializing the IDT with the addresses, here looping over it, and
* reinitializing the descriptors didn't work because assigning a
* a uintptr_t (from (uintptr_t) handler_func) to a descr (a.k.a.
* uint64_t), according to the compiler, "isn't computable at load
* time."
* The second option, storing the addresses as a local array, simply is
* inefficient …Run Code Online (Sandbox Code Playgroud) 我目前正在尝试编写16位实模式启动代码,该代码打印一个字母,然后从软盘加载第二个段并跳转到该段,然后再打印一个字母。
但是,我对“从云端硬盘读取扇区”呼叫的工作方式有些困惑。到目前为止,这是我的代码:
[BITS 16]
org 0x7B00
start:
mov ax, 0xB800 ; Video buffer
mov es, ax ; Copy address of video buffer to extra segment
mov byte [es:0], 'A' ; Move character A to first address
mov byte [es:1], 0x17 ; Format for blue background, white foreground
mov ah, 0x02 ; Read sectors from drive
mov al, 1 ; Read 1 sector
mov ch, 0 ; Cylinder 0
mov cl, 0 ; Sector 0
mov dh, 0 ; Head 0 …Run Code Online (Sandbox Code Playgroud)