- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在学习汇编的同时尝试了解内核的工作原理,并且在学习如何成功创建可启动的 x86_64 内核的过程中,我遇到了一个问题:
我尝试使用 0xB8000
中的 VGA 缓冲区成功输出一些带有“main.c”(下面的所有文件)中函数的文本,就像我对 32 位版本内核所做的那样原型(prototype),但不同的是启动文件不同。
这里的问题是,当我对 32 位版本使用完全相同的函数时,它成功打印到屏幕上,但是当使用新文件达到长模式时(multiboot.S
和 >start.S
)这不会发生,在 qemu 中测试时屏幕只是变黑,几秒钟后它崩溃并显示错误消息:
warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
qemu-system-x86_64: Trying to execute code outside RAM or ROM at 0x00000000000a0000
为什么会发生这种情况? VGA 缓冲区不在 0xB8000
中,*.S 文件有问题吗?预先感谢!
我将在此处粘贴内核文件:
内核由 4 个文件组成:“main.c”、“start.S”、“multiboot.S”和链接描述文件“linker.ld”。
这3个文件链接并编译没有任何错误,文件如下:这是main.c(你会看到一个“basiccolors.h”,这个文件只是定义了vga颜色代码)
#include "basiccolors.h"
#include <stddef.h>
#include <stdint.h>
volatile uint16_t* vga_buffer = (uint16_t*)0xB8000; /* memory location of the VGA textmode buffer */
/* Columns and rows of the VGA buffer */
const int VGA_COLS = 80;
const int VGA_ROWS = 25;
/* We start displaying text in the top-left of the screen (column = 0, row = 0) */
int term_col = 0;
int term_row = 0;
uint8_t term_color = WHITE_TXT; /* This color and others are defined in basiccolors.h */
/* term_init() : This function initiates the terminal by clearing it */
void term_init()
{
/* Clear the textmode buffer */
for (int col = 0; col < VGA_COLS; col ++)
{
for (int row = 0; row < VGA_ROWS; row ++)
{
/* The VGA textmode buffer has size (VGA_COLS * VGA_ROWS) */
/* Given this, we find an index into the buffer for our character */
const size_t index = (VGA_COLS * row) + col;
/* Entries in the VGA buffer take the binary form BBBBFFFFCCCCCCCC, where: */
/* - B is the background color */
/* - F is the foreground color */
/* - C is the ASCII character */
/* Now we set the character to blank (a space character) */
vga_buffer[index] = ((uint16_t)term_color << 8) | ' ';
}
}
}
/* term_putc(char c) : This function places a single character onto the screen */
void term_putc(char c)
{
/* We don't want to display all characters, for example, the newline ones */
switch (c)
{
case '\n': /* Newline characters should return the column to 0, and increment the row */
{
term_col = 0;
term_row ++;
break;
}
default: /* Normal characters just get displayed and then increment the column */
{
/* Like before, calculate the buffer index */
const size_t index = (VGA_COLS * term_row) + term_col;
vga_buffer[index] = ((uint16_t)term_color << 8) | c;
term_col ++;
break;
}
}
/* We need to reset the column to 0, and increment the row to get to a new line */
if (term_col >= VGA_COLS)
{
term_col = 0;
term_row ++;
}
/* we get past the last row, so we need to reset both column and row to 0 in order to loop back to the top of the screen */
if (term_row >= VGA_ROWS)
{
term_col = 0;
term_row = 0;
}
}
/* term_print : prints an entire string onto the screen, remember to use the "\n" and that short of things */
void term_print(const char* str)
{
for (size_t i = 0; str[i] != '\0'; i ++) /* Keep placing characters until we hit the null-terminating character ('\0') */
term_putc(str[i]);
}
/* Main function of the kernel, the one that is called at the end of the loading */
void kmain(void)
{
/* Now we should initialize the interfaces */
term_init(); /* VGA basic interface, in "basicoutput.c/h" */
term_print("CKA Cobalt release [0-0-1]\n");
};
这是开始。S:
.extern kmain
.section .data
.align 16
gdtr:
gdtr_limit:
.word (global_descriptor_table_end - global_descriptor_table) - 1
gdtr_pointer:
.int global_descriptor_table
.global global_descriptor_table
global_descriptor_table:
null_descriptor:
.quad 0x0000000000000000
code_descriptor:
.quad 0x0020980000000000
data_descriptor:
.quad 0x0000900000000000
global_descriptor_table_end:
.global null_segment
.set null_segment, (null_descriptor - global_descriptor_table)
.global code_segment
.set code_segment, (code_descriptor - global_descriptor_table)
.global data_segment
.set data_segment, (data_descriptor - global_descriptor_table)
multiboot_magic:
.space 4
multiboot_info:
.space 4
.section .bss
.global kernel_pagetable
.align 0x1000
kernel_pagetable:
pml4:
.space 0x1000
pdpt:
.space 0x1000
pd:
.space 0x1000
kernel_pagetable_end:
.global kernel_stack
kernel_stack:
.space 0x1000
kernel_stack_end:
.section .text
.code32
.global start
start:
cli
# store multiboot parameters in .data
mov %eax, multiboot_magic
mov %ebx, multiboot_info
# zerofill .bss
cld
mov $bss, %edi
mov $bss_end, %ecx
sub %edi, %ecx
xor %eax, %eax
rep stosb
# create pagetable for identity mapping lower 2 megabytes
# make minimal page table entries
.set pml4_entry, (pdpt + 0x03)
.set pdpt_entry, (pd + 0x03)
.set pd_entry, 0b10000011
movl $pml4_entry, pml4
movl $pdpt_entry, pdpt
movl $pd_entry, pd
# setup long mode
# load global descriptor table
lgdt (gdtr)
# enable Physical Address Extension (PAE)
mov %cr4, %eax
bts $5, %eax
mov %eax, %cr4
# set up page table
mov $kernel_pagetable, %eax
mov %eax, %cr3
# set up long mode
.set EFER_MSR_ADDRESS, 0xC0000080
mov $EFER_MSR_ADDRESS, %ecx
rdmsr
bts $8, %eax
wrmsr
# enable paging
mov %cr0, %eax
bts $31, %eax
mov %eax, %cr0
# long jump to set code segment
ljmp $code_segment, $longmode_start
.code64
longmode_start:
# data segment selector to all data segments
mov $data_segment, %bx
mov %bx, %ds
mov %bx, %es
mov %bx, %fs
mov %bx, %gs
# null segment selector to ss
mov $null_segment, %bx
mov %bx, %ss
# set up kernel stack
mov $kernel_stack_end, %rsp
push $0 # debugger backtrace stops here
# call kmain
mov multiboot_magic, %edi
mov multiboot_info, %esi
call kmain
# hang the computer
cli
hang:
hlt
jmp hang
这是多重引导。S:
.set MULTIBOOT_PAGE_ALIGN, 1 << 0
.set MULTIBOOT_MEM_INFO, 1 << 1
.set MULTIBOOT_AOUT_KLUDGE, 1 << 16
.set MULTIBOOT_MAGIC, 0x1BADB002
.set MULTIBOOT_FLAGS, MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEM_INFO | MULTIBOOT_AOUT_KLUDGE
.set MULTIBOOT_CHECKSUM, -(MULTIBOOT_MAGIC + MULTIBOOT_FLAGS)
.section .mboot
.align 4
.global multiboot_header
multiboot_header:
.int MULTIBOOT_MAGIC
.int MULTIBOOT_FLAGS
.int MULTIBOOT_CHECKSUM
.int multiboot_header
.int text
.int data_end
.int kernel_end
.int start
这是我的 linker.ld:
OUTPUT_FORMAT("elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(start)
phys = 0x0100000;
SECTIONS
{
. = phys;
kernel_start = .;
.text ALIGN(4096) : AT( ADDR(.text) )
{
text = .;
*(.mboot) /* Put Multiboot header section in the beginning of .text section */
*(.text)
*(.rodata)
text_end = .;
}
.data ALIGN(4096) : AT( ADDR(.data) )
{
data = .;
*(.data)
data_end = .;
}
.bss ALIGN(4096) : AT( ADDR(.bss) )
{
bss = .;
*(.bss)
bss_end = .;
}
kernel_end = .;
}
所有这些代码都是通过以下命令编译和链接的:
正在编译...
x86_64-elf-gcc -ffreestanding -mcmodel=large -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -c <file> -o <object-file>
和链接:
x86_64-elf-gcc -ffreestanding -T linker.ld multiboot.o start.o main.o -o kernel.bin -nostdlib -lgcc
此命令是 osdev.com 在教程 http://wiki.osdev.org/Creating_a_64-bit_kernel 中建议的所有内容均使用 x86_64 架构的 gcc 交叉编译器进行编译和链接。
最佳答案
使用 -kernel
参数时,QEMU 不支持 ELF64 可执行文件。您需要使用 multiboot2 兼容加载程序(例如 GRUB2)来启动内核。不幸的是,您还需要将多重引导 header 更改为 multiboot2 compliant 。您可以将 multiboot.S
文件替换为:
.section .mboot
.code32
.align 8
# constants for multiboot2 header:
.set MAGIC2, 0xe85250d6
.set ARCH2, 0 # i386 protected mode
.set CHECKSUM2, (-(MAGIC2 + ARCH2 + (mboot2_end - mboot2_start)) & 0xffffffff)
/* multiboot2 header */
mboot2_start:
.long MAGIC2
.long ARCH2
.long mboot2_end - mboot2_start
.long CHECKSUM2
.word 0 # type
.word 0 # flags
.long 8 # size
mboot2_end:
您按照之前的方式进行编译。有一个问题 - 为了确保此 header 最终不会被推到文件的前 8kb 之外,您可能需要在链接时指定 4kb 页面:
x86_64-elf-gcc -z max-page-size=0x1000 -ffreestanding -T linker.ld \
multiboot.o start.o main.o -o kernel.bin -nostdlib -lgcc
添加 -z max-page-size=0x1000
强制最大页面大小为 4kb。
关于gcc - 我的内核原型(prototype)存在问题(x86_64),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47102098/
好吧,我怀疑这是一个独特的情况,所以要么有人这样做了,要么有人认为这是不可能的,至少以我所要求的方式。 我有 2 个原型(prototype)变量(函数),一个是父变量,另一个是助手。我想做的是从助手
这是 JavaScript 大师的问题。我正在尝试更优雅地使用 JavaScript 原型(prototype)模型。这是我的实用程序代码(它提供了真实的原型(prototype)链并正确使用 ins
我们知道在 JavaScript 中有一个用于数组的 .forEach() 方法。但是字符串没有内置该方法。 那么,下面的代码片段有没有问题:String.prototype.forEach = Ar
我们知道在 JavaScript 中有一个用于数组的 .forEach() 方法。但是字符串没有内置该方法。 那么,下面的代码片段有没有问题:String.prototype.forEach = Ar
我看到了两种不同的模式和解释。来自 DailyJS 和许多其他人的一篇:矩形.prototype = new Shape(); 然后是 Crockford 的 here 这意味着只是 矩形.proto
尝试在 Object.prototype 以及 String.prototype 和 Number.prototype 上定义一个 hashCode 方法>。我正在使用以下方法定义原型(prototy
在本教程中,您将借助示例了解 JavaScript 中的原型。 在学习原型之前,请务必查看以下教程: JavaScript 对象 JavaScript 构造函数 如您所知,您可以使用对象构造函
当构造新对象时,该对象被设置为委托(delegate)任何尚未显式设置为其构造函数原型(prototype)的属性。这意味着我们可以稍后更改原型(prototype),并且仍然可以看到实例中的更改。
我正在努力获得更好的 JavaScript 实用知识。所以,我买了 Douglas Crockford 的书“JavaScript the good parts”。 我现在很难掌握原型(prototy
我的理解是相同类型的所有对象将共享相同的原型(prototype)。因此对原型(prototype)的更改将反射(reflect)在每个对象上。但是值类型的属性似乎不是这样。这种属性是如何存储的? f
这个问题在这里已经有了答案: 关闭 12 年前。 Possible Duplicate: JavaScript: Class.method vs. Class.prototype.method 创建
为什么在 MDN 函数中 polyfills 使用“if (!Array.prototype.filter)”? if (!Array.prototype.filter) { Array.prot
这个问题已经有答案了: Assigning prototype methods *inside* the constructor function - why not? (6 个回答) 已关闭 7 年
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 9 年前。 Improve this qu
面向对象有一个特征是继承,即重用某个已有类的代码,在其基础上建立新的类,而无需重新编写对应的属性和方法,继承之后拿来即用; 。 在其他的面向对象编程语言比如Java中,通常是指,子类继承父类的属性和
OOP 中原型(prototype)设计模式最重要的部分之一是我们不会从头开始创建新对象,我们只是使用 clone() 函数从现有对象克隆它们。 那么clone()函数是深拷贝还是浅拷贝? 如果它是一
在进行原型(prototype)设计时,您在多大程度上放弃了最佳实践来支持代码和修复黑客攻击?当然,代码并不打算在完整的生产环境中保留。 补充:我正在研究一个用 Python 制作的相当大的半工作原型
我开始学习设计模式。我知道原型(prototype)是用来制作我已经拥有的对象的精确副本,而享元是用来制作类似的对象。 我已经编写了 2D 平台游戏,例如马里奥(Java)。有很多相同的敌人,唯一的区
我正在使用 Maven 生成原型(prototype)。我能够使原型(prototype)生成正常,并且它生成的项目模板按预期工作。唯一的问题是在我的 shell 脚本中。脚本中注释掉的任何内容都会被
我想用 primefaces 配置一个 Java EE 项目。我在某处读到可以使用 mvn arechetype:generate 创建项目结构。当我使用它时,我只看到了 41 个选项,而在该教程中,
我是一名优秀的程序员,十分优秀!