October 25, 2015 this source | ||||
---|---|---|---|---|
| ||||
Posted in reply to David Nadlinger | #ifndef __BOOT_ASM_H__ #define __BOOT_ASM_H__ /* Assembler macros to create x86 segments */ /* Normal segment */ #define SEG_NULLASM \ .word 0, 0; \ .byte 0, 0, 0, 0 #define SEG_ASM(type,base,lim) \ .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) /* Application segment type bits */ #define STA_X 0x8 #define STA_E 0x4 #define STA_C 0x4 #define STA_W 0x2 #define STA_R 0x2 #define STA_A 0x1 #endif /* !__BOOT_ASM_H__ */ ***************************************** #include <asm.h> # Start the 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. .set PROT_MODE_CSEG, 0x8 # kernel code segment selector .set PROT_MODE_DSEG, 0x10 # kernel data segment selector .set CR0_PE_ON, 0x1 # protected mode enable flag .globl start start: .code16 # Assemble for 16-bit mode cli cld # 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 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 lgdt gdtdesc movl %cr0, %eax orl $CR0_PE_ON, %eax movl %eax, %cr0 ljmp $PROT_MODE_CSEG, $protcseg .code32 # Assemble for 32-bit mode protcseg: movw $PROT_MODE_DSEG, %ax movw %ax, %ds # -> DS: Data Segment movw %ax, %es # -> ES: Extra Segment movw %ax, %fs # -> FS movw %ax, %gs # -> GS movw %ax, %ss # -> SS: Stack Segment movl $0x0, %ebp movl $start, %esp call bootmain spin: jmp spin .p2align 2 gdt: SEG_NULLASM SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) SEG_ASM(STA_W, 0x0, 0xffffffff) gdtdesc: .word 0x17 .long gdt **************************************** module bootmain; import types; import x86; const COM1 = 0x3F8; const CRTPORT = 0x3D4; const LPTPORT = 0x378; const COM_TX = 0; const COM_LSR = 5; const COM_LSR_TXRDY = 20; extern (C){ uint8_t inb(uint16_t port); void outb(uint16_t port, uint8_t data); uint16_t *crt; // CGA memory } static this(){ uint16_t *crt = cast(uint16_t *)0xB8000; } /* stupid I/O delay routine necessitated by historical PC design flaws */ static void delay() { inb(0x84); inb(0x84); inb(0x84); inb(0x84); } /* lpt_putc - copy console output to parallel port */ static void lpt_putc(int c) { int i; for (i = 0; !(inb(LPTPORT + 1) & 0x80) && i < 12800; i ++) { delay(); } outb(LPTPORT + 0, cast(uint8_t)c); outb(LPTPORT + 2, 0x08 | 0x04 | 0x01); outb(LPTPORT + 2, 0x08); } /* cga_putc - print character to console */ static void cga_putc(int c) { int pos; // cursor position: col + 80*row. outb(CRTPORT, 14); pos = inb(CRTPORT + 1) << 8; outb(CRTPORT, 15); pos |= inb(CRTPORT + 1); if (c == '\n') { pos += 80 - pos % 80; } else { crt[pos ++] = (c & 0xff) | 0x0700; } outb(CRTPORT, 14); outb(CRTPORT + 1, cast(uint8_t)(pos >> 8)); outb(CRTPORT, 15); outb(CRTPORT + 1, cast(uint8_t)pos); } /* serial_putc - copy console output to serial port */ static void serial_putc(int c) { int i; for (i = 0; !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800; i ++) { delay(); } outb(COM1 + COM_TX, cast(uint8_t)c); } /* cons_putc - print a single character to console*/ static void cons_putc(int c) { lpt_putc(c); cga_putc(c); serial_putc(c); } /* cons_puts - print a string to console */ static void cons_puts(const char *str) { int i; for (i = 0; *str != '\0'; i ++) { cons_putc(str[i]); } } /* bootmain - the entry of bootloader */ static void bootmain() { cons_puts("This is a bootloader: Hello world!!"); /* do nothing */ while (true){} } **************************** module types; extern (C): alias int BOOL; alias char int8_t; //C typedef unsigned char uint8_t; alias ubyte uint8_t; //C typedef short int16_t; alias short int16_t; //C typedef unsigned short uint16_t; alias ushort uint16_t; //C typedef int int32_t; alias int int32_t; //C typedef unsigned int uint32_t; alias uint uint32_t; //C typedef long long int64_t; alias long int64_t; //C typedef unsigned long long uint64_t; alias ulong uint64_t; ******************************** module x86; import types; extern (C){ uint8_t inb(uint16_t port); void outb(uint16_t port, uint8_t data); } extern (C) uint8_t inb(uint16_t port) { uint8_t data; asm { mov DX,port; in AL,DX; mov data,AL; } return data; } extern (C) void outb(uint16_t port, uint8_t data) { asm { mov DX,port; mov AL,data; out DX,AL; } } ***************************** bootasm:bootasm.S asm.h rm obj\*.o -rf gcc -I. -fno-builtin -Wall -ggdb -m16 -nostdinc -fno-stack-protector -Os -nostdinc -c bootasm.S -o obj/bootasm.o bootmain:bootmain.d types.d x86.d ldc2 -L=Os -c $^ -odobj all:bootasm bootmain ****************************** bootblock:bootasm.o bootmain.o x86.o types.o ld -m elf_i386 -N -e start -Ttext 0x7C00 $^ -o bootblock.o objdump -S bootblock.o > bootblock.asm objcopy -S -O binary bootblock.o bootblock.out rm bootblock -f ./writeHDD bootblock.out bootblock dd if=/dev/zero of=ucore.img count=10000 dd if=bootblock of=ucore.img conv=notrunc |
October 25, 2015 build erro | ||||
---|---|---|---|---|
| ||||
Posted in reply to guodemone | ld -m elf_i386 -N -e start -Ttext 0x7C00 bootasm.o bootmain.o x86.o types.o -o bootblock.o bootasm.o:在函数‘protcseg’中: /home/glg/桌面/bootloader_D/bootasm.S:60:对‘bootmain’未定义的引用 bootmain.o:在函数‘ldc.dso_ctor.8bootmain’中: bootmain.d:(.text.ldc.dso_ctor.8bootmain[ldc.dso_ctor.8bootmain]+0x4d):对‘_d_dso_registry’未定义的引用 bootmain.o:在函数‘ldc.dso_dtor’中: bootmain.d:(.text.ldc.dso_dtor[ldc.dso_dtor]+0x4d):对‘_d_dso_registry’未定义的引用 x86.o:在函数‘ldc.dso_ctor.3x86’中: x86.d:(.text.ldc.dso_ctor.3x86[ldc.dso_ctor.3x86]+0x4d):对‘_d_dso_registry’未定义的引用 types.o:在函数‘ldc.dso_ctor.5types’中: types.d:(.text.ldc.dso_ctor.5types[ldc.dso_ctor.5types]+0x4d):对‘_d_dso_registry’未定义的引用 makefile:2: recipe for target 'bootblock' failed make: *** [bootblock] Error 1 |
October 25, 2015 I want to new os in Dlang | ||||
---|---|---|---|---|
| ||||
Posted in reply to guodemone | I need excample bootloader(asm+D). (dmd,gdc,ldc build flag) (linker flag) to now,D no have bootloader(asm+D). |
October 26, 2015 Re: build erro | ||||
---|---|---|---|---|
| ||||
Posted in reply to guodemone | It's a missing function _d_dso_registry. Not sure if you need it or not, but you can stub it: extern(C) void _d_dso_registry(void* data) { } Also don't use static constructors yet. They are invoked during initialization of modules, which you probably don't run. |
October 26, 2015 build bin file format | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kagamin | How build .bin? eg. ldc -c bootmain.d of bin |
October 26, 2015 Re: build bin file format | ||||
---|---|---|---|---|
| ||||
Posted in reply to guodemone | You mean a CD image with your loader in MBR? |
October 26, 2015 Re: build bin file format | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kagamin | file asm.h /* 是bootasm.S汇编文件所需要的头文件,主要是一些与X86保护模式的段访问方式相关的宏定义 */ #ifndef __BOOT_ASM_H__ #define __BOOT_ASM_H__ /* Assembler macros to create x86 segments */ /* Normal segment */ #define SEG_NULLASM \ .word 0, 0; \ .byte 0, 0, 0, 0 #define SEG_ASM(type,base,lim) \ .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) /* Application segment type bits */ #define STA_X 0x8 // 可执行 #define STA_E 0x4 // 向下扩展段(非可执行段) #define STA_C 0x4 // 一致性代码段(只执行) #define STA_W 0x2 // 段可写(非可执行段) #define STA_R 0x2 // 段可读 (可执行段) #define STA_A 0x1 // 可访问 #endif /* !__BOOT_ASM_H__ */ ********************************************************** file bootasm.S # 定义并实现了bootloader最先执行的函数start,此函数进行了一定的初始化,完成了 # 从实模式到保护模式的转换,并调用bootmain.c中的bootmain函数 #include <asm.h> # Start the 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. # gdt 全局描述符表内的数组索引 .set PROT_MODE_CSEG, 0x8 # kernel code segment selector .set PROT_MODE_DSEG, 0x10 # kernel data segment selector .set CR0_PE_ON, 0x1 # protected mode enable flag .globl start start: .code16 # Assemble for 16-bit mode cli # 禁用中断 cld # 字符串操作设定为递增 si++ di++ ,cld的作用是将direct flag标志位清零 # 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 # A20地址线控制打开工作 # Enable A20: # 为了向后兼容早期的PC机,让物理地址线20接低电平 # 如果A20是关闭的,16bit的寻址范围2^20是1M,如果是打开的,那么就是2^21次方, # 但是寻址还是FFFFh:FFFFh=FFFF0h+FFFFh=10FFEFh=1M+64K-16Bytes seta20.1: inb $0x64, %al # Wait for not busy testb $0x2, %al jnz seta20.1 #测试 bit 1 是不是为0,如果不是跳回去继续执行 # 对于键盘的8042控制芯片 0x64是命令端口 0xd1 代表写命令 movb $0xd1, %al # 0xd1 -> port 0x64 outb %al, $0x64 seta20.2: inb $0x64, %al # Wait for not busy testb $0x2, %al jnz seta20.2 # 设置写命令后 给0x60端口 发送命令数据0xdf就是打开A20地址线,0xdd就是关闭 movb $0xdf, %al # 0xdf -> port 0x60 outb %al, $0x60 # 转入保护模式,这里需要指定一个临时的GDT,来翻译逻辑地址。 # 这里使用的GDT通过gdtdesc段定义,它翻译得到的物理地址和虚拟地址相同, # 所以转换过程中内存映射不会改变 lgdt gdtdesc # 启动保护模式前建立好的段描述符合段描述符表 # 打开保护模式标志位,相当于按下了保护模式的开关。 # cr0寄存器的第0位就是这个开关,通过CR0_PE_ON或cr0寄存器,将第0位置1 movl %cr0, %eax orl $CR0_PE_ON, %eax movl %eax, %cr0 # 由于上面的代码已经打开了保护模式了,所以这里要使用逻辑地址, # 而不是之前实模式的地址了。这里用到了PROT_MODE_CSEG, # 他的值是0x8。根据段选择子的格式定义,0x8就翻译成: # INDEX TI CPL # 0000 0000 0000 1 00 0 # INDEX代表GDT中的索引,TI代表使用GDTR中的GDT, CPL代表处于特权级。 # PROT_MODE_CSEG选择子选择了GDT中的第1个段描述符。 # 这里使用的gdt就是变量gdt,下面可以看到gdt的第1个段描述符的基地址是0x0000, # 所以经过映射后和转换前的内存映射的物理地址一样。0000:7C00=0x00007C00 0000:protcseg 都是相对于物理内存0000基址的 ljmp $PROT_MODE_CSEG, $protcseg .code32 # Assemble for 32-bit mode protcseg: # 重新初始化各个段寄存器。也就是采用平坦式内存方式, # 代码段同其它段都采用一个内存空间 movw $PROT_MODE_DSEG, %ax # 自定义数据段选择子,因为段选择子是16位的 movw %ax, %ds # -> DS: Data Segment movw %ax, %es # -> ES: Extra Segment movw %ax, %fs # -> FS movw %ax, %gs # -> GS movw %ax, %ss # -> SS: Stack Segment # 栈顶设定在start处,也就是地址0x7c00处, # 低地址 0x0000 ^ 此地址为栈基址 0000 # call函数将返回地址入栈,将控制权交给bootmain # /|\ movl $0x0, %ebp # | movl $start, %esp # | call bootmain # 地址 0x7C00 | 栈顶指针 # bootmain 如果返回,就会在此处死循环,但是目前的bootmain的函数不会返回了,因为他内部就会死循环 spin: jmp spin # 注意以下数据结构 # Bootstrap GDT .p2align 2 # 调整为4字节对齐 # 3个段描述符,每个段描述符占8字节,共24字节 # gdtdesc指出了全局描述符表(可以看成是段描述符组成的一个数组)的起始位置在gdt符号处, # 而gdt符号处放置了三个段描述符的信息 # 第一个是NULL段描述符,没有意义,表示全局描述符表的开始 # 紧接着是代码段描述符(位于全局描述符表的0x8处的位置),具有可读(STA_R)和可执行(STA_X)的属性, # 并且段起始地址为0,段大小为4GB; # 接下来是数据段描述符(位于全局描述符表的0x10处的位置),具有可读(STA_R)和可写(STA_W)的属性, # 并且段起始地址为0,段大小为4GB。 gdt: SEG_NULLASM # null seg NULL段 SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel CODE段 SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel DATA段 gdtdesc: .word 0x17 # sizeof(gdt) - 1 类似于数组 数组是0开始的,所以数组长度就要减1 .long gdt # address gdt ***************************************************************************** file bootmain.c /* 定义并实现了bootmain函数实现了通过屏幕、串口和并口显示字符串 */ //#include <types.h> //#include <x86.h> #define COM1 0x3F8 #define CRTPORT 0x3D4 #define LPTPORT 0x378 #define COM_TX 0 // Out: Transmit buffer (DLAB=0) #define COM_LSR 5 // In: Line Status Register #define COM_LSR_TXRDY 20 // Transmit buffer avail static uint16_t *crt = (uint16_t *) 0xB8000; // CGA memory /* stupid I/O delay routine necessitated by historical PC design flaws */ static void delay(void) { inb(0x84); inb(0x84); inb(0x84); inb(0x84); } /* 考虑到简单性,在proj1中没有对并口设备进行初始化,通过并口进行输出的过程也很简单: 第一步:执行inb指令读取并口的I/O地址(LPTPORT + 1)的值,如果发现发现读出的值代表并口忙, 则空转一小会再读; 如果发现发现读出的值代表并口空闲,则执行outb指令把字符写到并口的I/O地址(LPTPORT ), 这样就完成了一个字符的并口输出。 */ /* lpt_putc - copy console output to parallel port */ static void lpt_putc(int c) { int i; for (i = 0; !(inb(LPTPORT + 1) & 0x80) && i < 12800; i ++) { delay(); } outb(LPTPORT + 0, c); outb(LPTPORT + 2, 0x08 | 0x04 | 0x01); outb(LPTPORT + 2, 0x08); } /* 通过CGA显示控制器进行输出的过程也很简单:首先通过in/out指令获取当前光标位置; 然后根据得到的位置计算出显存的地址,直接通过访存指令写内存来完成字符的输出; 最后通过in/out指令更新当前光标位置。 */ /* cga_putc - print character to console */ static void cga_putc(int c) { int pos; // cursor position: col + 80*row. outb(CRTPORT, 14); pos = inb(CRTPORT + 1) << 8; outb(CRTPORT, 15); pos |= inb(CRTPORT + 1); if (c == '\n') { pos += 80 - pos % 80; } else { crt[pos ++] = (c & 0xff) | 0x0700; } outb(CRTPORT, 14); outb(CRTPORT + 1, pos >> 8); outb(CRTPORT, 15); outb(CRTPORT + 1, pos); } /* 通过串口进行输出的过程也很简单:第一步:执行inb指令读取串口的I/O地址(COM1 + COM_LSR)的值, 如果发现发现读出的值代表串口忙,则空转一小会(0x84是什么地址???); 如果发现发现读出的值代表串口空闲,则执行outb指令把字符写到串口的I/O地址(COM1 + COM_TX), 这样就完成了一个字符的串口输出。 */ /* serial_putc - copy console output to serial port */ static void serial_putc(int c) { int i; for (i = 0; !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800; i ++) { delay(); } outb(COM1 + COM_TX, c); } /* 显示字符的函数接口*/ /* 一个cons_putc函数接口,完成字符的输出*/ /* cons_putc - print a single character to console*/ static void cons_putc(int c) { lpt_putc(c); cga_putc(c); serial_putc(c); } /* 提供了一个cons_puts函数接口:完成字符串的输出*/ /* cons_puts - print a string to console */ static void cons_puts(const char *str) { int i; for (i = 0; *str != '\0'; i ++) { cons_putc(*str ++); } } /* bootmain - the entry of bootloader */ void bootmain(void) { cons_puts("This is a bootloader: Hello world!!"); /* do nothing */ while (1); } *************************************************** These codes(asm.h bootasm.S bootmain.c) trans to (asm.h bootasm.S bootmain.d). ldc -c asm.h bootasm.S bootmain.d ld bootasm.o bootmain.o of outbin.o |
October 26, 2015 I need [D version of the bootloader] | ||||
---|---|---|---|---|
| ||||
Posted in reply to guodemone | bootloader of [asm+D] version |
October 26, 2015 Re: I need [D version of the bootloader] | ||||
---|---|---|---|---|
| ||||
Posted in reply to guodemone | In C version you compile bootmain.c to object file bootmain.o, in d version you again get bootmain.o, after that you do the same as in C version. |
Copyright © 1999-2021 by the D Language Foundation