From 47b561098d169fe9a417ce05c0500ba39ea348b7 Mon Sep 17 00:00:00 2001 From: acevest Date: Sun, 4 Jan 2026 22:25:53 +0800 Subject: [PATCH] =?utf8?q?=E8=AE=A9AP=E8=BF=9B=E5=85=A5=E4=BF=9D=E6=8A=A4?= =?utf8?q?=E6=A8=A1=E5=BC=8F=E5=90=8E=E5=8F=AF=E4=BB=A5=E5=92=8CBSP?= =?utf8?q?=E5=85=B1=E4=BA=AB=E5=90=8C=E6=A0=B7=E7=9A=84=E5=86=85=E6=A0=B8?= =?utf8?q?=E5=9C=B0=E5=9D=80=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- boot/ap_boot.S | 51 +++++++++++++++++++++++++++++++++++++++++++++ boot/multiboot.S | 1 - boot/unpaged_boot.c | 8 +++++++ kernel/ap.c | 21 +++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 kernel/ap.c diff --git a/boot/ap_boot.S b/boot/ap_boot.S index 538fce8..4e5c0c8 100644 --- a/boot/ap_boot.S +++ b/boot/ap_boot.S @@ -17,6 +17,7 @@ ap_boot_bgn: ap_realmode_base: ap_real_mode_entry: cli + # TODO 只允许CPU1运行 wbinvd # 将CPU缓存写回主内存并使缓存失效 @@ -64,6 +65,56 @@ ap_real_mode_entry: .align 32 ap_code32_entry: cli + nop + nop + + # 数据段寄存器需要调整成GDT的描述符的选择子 + movl $0x10, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + # 加载一个跳板页目录,为真正切换到内核地址空间做准备 + .extern ap_pre_pgd + leal ap_pre_pgd, %eax + movl %eax, %cr3 + + # 开启分页 + movl %cr0, %eax + orl $0x80000000, %eax + movl %eax, %cr0 + + leal ap_code32_real_entry, %eax + jmp *%eax + +# 虽然下面的代码也会被复制到小于1MB的内存 +# 但是它不会在那里被执行,而是会在大于1MB的物理内存,并且是内核的地址空间执行 +# 也就是说他们在内核的原来的地址执行 +# 当然也可以把它搬到ap_boot_end之后,这样它就不会被复制到小于1MB的内存里了 +ap_code32_real_entry: + + nop + nop + + # 把栈指针调整到内核的地址空间 + addl $kernel_virtual_addr_start, %esp + + # 到这里我们就可以切到BSP一样cr3了 + # 为什么在开启分页前无法直接加载BSP的cr3? + # 因为BSP把小于内核地址空间的映射都清0了,如果直接加载,分页后的第一行指令就无法执行 + + .extern init_pgd + leal init_pgd, %eax + subl $kernel_virtual_addr_start, %eax # 算出物理地址 + movl %eax, %cr3 + + # 进入内核ap代码 + .extern ap_kernel_entry + leal ap_kernel_entry, %eax + jmp *%eax + nop nop hlt diff --git a/boot/multiboot.S b/boot/multiboot.S index 4f850bc..941b377 100644 --- a/boot/multiboot.S +++ b/boot/multiboot.S @@ -21,7 +21,6 @@ .extern setup_kernel .extern root_task_entry .extern get_root_task_stack_top -.extern kernel_virtual_addr_start #define MULTIBOOT_STACK_SIZE 0x200 diff --git a/boot/unpaged_boot.c b/boot/unpaged_boot.c index 53d67ef..4fbf2fa 100644 --- a/boot/unpaged_boot.c +++ b/boot/unpaged_boot.c @@ -17,6 +17,8 @@ extern unsigned long kernel_virtual_addr_start; extern pde_t __initdata init_pgd[PDECNT_PER_PAGE] __attribute__((__aligned__(PAGE_SIZE))); extern pte_t __initdata init_pgt[PTECNT_PER_PAGE * BOOT_INIT_PAGETBL_CNT] __attribute__((__aligned__(PAGE_SIZE))); +pde_t ap_pre_pgd[PDECNT_PER_PAGE] __attribute__((__aligned__(PAGE_SIZE))); + // Length = BOOT_INIT_PAGETBL_CNT*4M // [0x00000000, 0x00000000 + Length - 1] // [0xC0000000, 0xC0000000 + Length - 1] @@ -30,6 +32,7 @@ void boot_paging() { pde_t* dir = (pde_t*)init_pgd_paddr; for (int i = 0; i < PDECNT_PER_PAGE; i++) { dir[i] = 0; + ap_pre_pgd[i] = 0; } // 初始化页目录 @@ -38,6 +41,11 @@ void boot_paging() { for (int i = 0; i < BOOT_INIT_PAGETBL_CNT; i++) { dir[i] = pd_entry; // 设置低端线性地址空间的页表项 dir[kpde_base + i] = pd_entry; // 设置内核线性地址空间的页表项 + + // AP初始化用于开启分页后再跳入内核空间的跳板页目录 + ap_pre_pgd[i] = pd_entry; + ap_pre_pgd[kpde_base + i] = pd_entry; + pd_entry += PAGE_SIZE; } diff --git a/kernel/ap.c b/kernel/ap.c new file mode 100644 index 0000000..5b9a3a0 --- /dev/null +++ b/kernel/ap.c @@ -0,0 +1,21 @@ +/* + * ------------------------------------------------------------------------ + * File Name: ap.c + * Author: Zhao Yanbai + * 2026-01-04 20:15:33 Sunday CST + * Description: none + * ------------------------------------------------------------------------ + */ + +#include + +extern pde_t* ap_pre_pgd; + +void ap_kernel_entry() { + // 虽然ap_pre_pgd只做了一下跳板页目录看起来很可惜 + // 不过可以把它拿来个AP的栈用 + asm("mov %0, %%esp" : : "r"(pa2va(&ap_pre_pgd) + PAGE_SIZE)); + while (1) { + asm("nop;"); + } +} -- 2.47.0