我从https://github.com/jeffallen/xv6/blob/master/bootasm.S复制了bootasm.S,
#include "asm.h"
# Start the first CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.
#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack
#define CR0_PE 1 // protected mode enable bit
.code16 # Assemble for 16-bit mode
.globl start
start:
cli # BIOS enabled interrupts; disable
# Set up the important data segment registers (DS, ES, SS).
xorw %ax,%ax # Segment number zero
movw %ax,%ds # -> Data Segment
movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment
# Physical address line A20 is tied to zero so that the first PCs
# with 2 MB would run software that assumed 1 MB. Undo that.
seta20.1:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.1
movb $0xd1,%al # 0xd1 -> port 0x64
outb %al,$0x64
seta20.2:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.2
movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60
# Switch from real to protected mode. Use a bootstrap GDT that makes
# virtual addresses map dierctly to physical addresses so that the
# effective memory map doesn't change during the transition.
lgdt gdtdesc
movl %cr0, %eax
orl $CR0_PE, %eax
movl %eax, %cr0
# Complete transition to 32-bit protected mode by using long jmp
# to reload %cs and %eip. The segment registers are set up with no
# translation, so that the mapping is still the identity mapping.
ljmp $(SEG_KCODE<<3), $start32
.code32 # Tell assembler to generate 32-bit code now.
start32:
# Set up the protected-mode data segment registers
movw $(SEG_KDATA<<3), %ax # Our data segment selector
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %ss # -> SS: Stack Segment
xor %eax, %eax # Zero segments not ready for use
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
## sti TaoWang: It should NOT call STI here, since NO IDT is ready.
# Set up the stack pointer and call into C.
movl $start, %esp
call bootmain
spin:
jmp spin
# Bootstrap GDT
.p2align 2 # force 4 byte alignment
gdt:
SEG_NULLASM # null seg
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
gdtdesc:
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
.long gdt # address gdt
.fill 510-(.-start)
.word 0xaa55
#include "asm.h"
# Start the first CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.
#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack
#define CR0_PE 1 // protected mode enable bit
.code16 # Assemble for 16-bit mode
.globl start
start:
cli # BIOS enabled interrupts; disable
# Set up the important data segment registers (DS, ES, SS).
xorw %ax,%ax # Segment number zero
movw %ax,%ds # -> Data Segment
movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment
# Physical address line A20 is tied to zero so that the first PCs
# with 2 MB would run software that assumed 1 MB. Undo that.
seta20.1:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.1
movb $0xd1,%al # 0xd1 -> port 0x64
outb %al,$0x64
seta20.2:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.2
movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60
# Switch from real to protected mode. Use a bootstrap GDT that makes
# virtual addresses map dierctly to physical addresses so that the
# effective memory map doesn't change during the transition.
lgdt gdtdesc
movl %cr0, %eax
orl $CR0_PE, %eax
movl %eax, %cr0
# Complete transition to 32-bit protected mode by using long jmp
# to reload %cs and %eip. The segment registers are set up with no
# translation, so that the mapping is still the identity mapping.
ljmp $(SEG_KCODE<<3), $start32
.code32 # Tell assembler to generate 32-bit code now.
start32:
# Set up the protected-mode data segment registers
movw $(SEG_KDATA<<3), %ax # Our data segment selector
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %ss # -> SS: Stack Segment
xor %eax, %eax # Zero segments not ready for use
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
## sti TaoWang: It should NOT call STI here, since NO IDT is ready.
# Set up the stack pointer and call into C.
movl $start, %esp
call bootmain
spin:
jmp spin
# Bootstrap GDT
.p2align 2 # force 4 byte alignment
gdt:
SEG_NULLASM # null seg
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
gdtdesc:
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
.long gdt # address gdt
.fill 510-(.-start)
.word 0xaa55
并按如下所示更改bootmain.c,
#include "types.h"
char serial_buffer[256];
static void my_memcpy(void *dst, void *src, u32 length)
{
u32 i = 0;
for (i = 0; i < length; i ++) {
*(char *)dst = *(char *)src;
}
if (serial_buffer[0] == 'A') {
asm ("cli\nhlt\n");
} else {
asm ("vmcall");
}
}
int bootmain(void)
{
my_memcpy(serial_buffer, "Abcedife", 8);
return 0;
}
void handle_page_fault(void)
{
return;
}
通过Makefile(我在下面列出)构建代码后,加载输出二进制文件的代码在这里,
#include "types.h"
char serial_buffer[256];
static void my_memcpy(void *dst, void *src, u32 length)
{
u32 i = 0;
for (i = 0; i < length; i ++) {
*(char *)dst = *(char *)src;
}
if (serial_buffer[0] == 'A') {
asm ("cli\nhlt\n");
} else {
asm ("vmcall");
}
}
int bootmain(void)
{
my_memcpy(serial_buffer, "Abcedife", 8);
return 0;
}
void handle_page_fault(void)
{
return;
}
令我惊讶的是,while循环根本不执行。
这是我的linker.ld,我在Linux 4.4.0中运行它们。
unsigned char tempbuf[0x400];
void file_load(char *vmfname)
{
int vmfd = -1;
size_t cnt = 0, offset = 0;
vmfd = open( vmfname, O_RDWR );
if (vmfd < 0) {
exit(2);
}
do {
cnt = read(vmfd, tempbuf, sizeof(tempbuf));
// initialize the virtual-machine registers
memcpy((void *)(CODE_START + offset), tempbuf, cnt);
offset += cnt;
} while (cnt > 0);
close(vmfd);
printf("Loading %ld bytes of VM to run\n", offset);
}
Makefile,
unsigned char tempbuf[0x400];
void file_load(char *vmfname)
{
int vmfd = -1;
size_t cnt = 0, offset = 0;
vmfd = open( vmfname, O_RDWR );
if (vmfd < 0) {
exit(2);
}
do {
cnt = read(vmfd, tempbuf, sizeof(tempbuf));
// initialize the virtual-machine registers
memcpy((void *)(CODE_START + offset), tempbuf, cnt);
offset += cnt;
} while (cnt > 0);
close(vmfd);
printf("Loading %ld bytes of VM to run\n", offset);
}
我不知道为什么常量字符串未能作为参数传递或其内容全部为&#39; 0&#39; ?
如果我使用char数组,将数组名称作为参数放到myfputs(chararray),它将运行良好。
答案 0 :(得分:1)
我通过参考迈克尔关于将-j .data添加到Makefile的答案回答了这个问题,因此数据部分将被添加到最终的二进制文件中,这可以解决问题。
随着Makefile的更改,现在代码可以按预期工作
这是构建最终二进制文件的命令行
guest: test_app.c
$(CC) -g2 -Wall -Wextra -Werror $^ -o $@
$(CC) $(CFLAGS) -nostdinc -I. -c bootasm.S
$(CC) $(CFLAGS) -I. -c bootmain.c
$(LD) $(LDFLAGS) -N -e start -Tlinker.ld -o bootblock.o bootasm.o bootmain.o
$(OBJDUMP) -S bootblock.o > bootblock.asm
$(OBJCOPY) -S -O binary -j .text -j .data -j .bss bootblock.o bootblock.bin
guest: test_app.c
$(CC) -g2 -Wall -Wextra -Werror $^ -o $@
$(CC) $(CFLAGS) -nostdinc -I. -c bootasm.S
$(CC) $(CFLAGS) -I. -c bootmain.c
$(LD) $(LDFLAGS) -N -e start -Tlinker.ld -o bootblock.o bootasm.o bootmain.o
$(OBJDUMP) -S bootblock.o > bootblock.asm
$(OBJCOPY) -S -O binary -j .text -j .data -j .bss bootblock.o bootblock.bin