this is my code :
...
data_seg equ os_data-gdt_start
code_seg equ os_code-gdt_start
...
jmp code_seg:pm_start
[BITS 32]
pm_start:
mov ax,data_seg
mov ds,ax
mov word [ds:0xb8000],0xC341
it work correctly when dc bit (Third bit of Access byte) in the gdt is zero.
I want to know why not work when it is 1?
I know that dc bit is Direction bit of data selectors , and when it's 0 , the segment grows up and when it's 1 the segment grows down. but not know what is the meaning of grows up and grows down exactly. grows up and grows down means to me when I want to use the stack.( ESP++ and ESP-- )
DC bit is name by osdev.org, by IntelĀ“s manual it's expansion-direction. Number can go only in two directions: it can increase or decrease. DC bit is the thing that plays with it.
If the size of a stack segment needs to be dynamically, the stack segment can be an expand-down data segment (expansion-direction flag is set). Dynamically changing segment limit causes stack space to be added to the bottom of the stack.
Related
I am trying to make a operating system. I just finished the bootloader, however I am having a problem loading my kernel.
Boot.asm:
section .boot
bits 16
global boot
boot:
mov ax, 0x2401
int 0x15
mov ax, 0x3
int 0x10
mov [disk],dl
mov ah, 0x2 ;read sectors
mov al, 6 ;sectors to read
mov ch, 0 ;cylinder idx
mov dh, 0 ;head idx
mov cl, 2 ;sector idx
mov dl, [disk] ;disk idx
mov bx, copy_target;target pointer
int 0x13
cli
lgdt [gdt_pointer]
mov eax, cr0
or eax,0x1
mov cr0, eax
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp CODE_SEG:boot2
gdt_start:
dq 0x0
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_pointer:
dw gdt_end - gdt_start
dd gdt_start
disk:
db 0x0
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
times 510 - ($-$$) db 0
dw 0xaa55
copy_target:
bits 32
hello: db "Hello more than 512 bytes world!!",0
boot2:
mov esi,hello
mov ebx,0xb8000
.loop:
lodsb
or al,al
jz halt
or eax,0x0F00
mov word [ebx], ax
add ebx,2
jmp .loop
halt:
mov esp,kernel_stack_top
extern kzos
call kzos
cli
hlt
section .bss
align 4
kernel_stack_bottom: equ $
resb 16384 ; 16 KB
kernel_stack_top:
kzos.cpp
extern "C" void kzos()
{
const short color = 0x0F00;
const char* hello = "Kernel test!";
short* vga = (short*)0xb8000;
for (int i = 0; i<16;++i)
vga[i+80] = color | hello[i];
}
The error I am getting is with " extern kzos" the error reads "boot.asm:77: error: binary output format does not support external references"
I tried adding " extern kzos.cpp " but then I get "boot.asm:77: error: symbol `kmain' undefined"
if I add ".cpp" to the call function I get "boot4.asm:77: error: binary output format does not support external references"
I am using Nasm to compile to bin and qemu to run it.
I just finished the bootloader, ...
No you didn't. At least half of a boot loader's code exists to handle errors that "shouldn't" happen (things like checking if the BIOS failed to enable A20, or if the BIOS says there was a problem reading data from disk) and handling those cases somehow - at a minimum, by providing the user information that can help them fix the problem (and determine if it's faulty hardware or a problem with the way the OS was installed or ...), so the user isn't stuck wondering why their entire computer is unusable (and so that you're not stuck with a bug report saying "doesn't boot" with no useful information either).
how do I load my kernel from my bootloader?
Your choices for finding where the kernel is (its location on the disk and its size) are:
a) Put the kernel in some kind of normal file system, and have the boot loader support that file system (e.g. find the right directory entry and get location of file's data from the file's directory entry). Note that this is complicated (e.g. you'll have a lot of error handling, in case the file system's structures are corrupted, etc)
b) Put the kernel in some kind of purpose designed file system or in some kind of purpose designed "special/reserved area" of a normal file system. This could be as simple as a table of "offset and length" structures stored in the first sector, where kernel's file is always the first entry in that table.
c) Include the kernel's binary directly into the boot loader using something like the incbin directive in NASM. In this case you can use labels to determine the size of the kernel's file, like:
kernel_start:
incbin 'kernel.bin'
kernel_end:
In this case you can determine where the kernel is on disk from where the boot loader was on disk, and calculate how many sectors it is (e.g. (kernel_end - kernel_start + SECTOR_SIZE-1)/SECTOR_SIZE). Of course this is horribly inflexible (e.g. you can't easily update the kernel without assembling and then reinstalling the boot loader).
Once you've determined the location and size of the kernel on disk; you need to load it into memory somewhere. Note that this can depend on what kind of executable file format you chose to use for the kernel; and could involve loading the executable file's headers and parsing them to figure out which parts of the file should go where in memory (and setting up things like ".bss" that aren't in the file).
the output format is bin, flat format, that is
it does not support rellocations
thus call kzos's location/address is lost once the code is rendered
any linking utility will fail locating call kzos' in order
to link it to the new address of extern "C" void kzos() from kzos.cpp
I am trying to calculate the relative address offset from one instruction to another.
I understand the basic calculation, and why I have to -5 (to cater for size of jmp and instruction size) (Calculating JMP instruction's address)
The question is, what if I want to jump not to start of a code but some specific instructions after it?
For example:
original function
I want to JMP to the highlighted instruction OPENGL32.dll+48195, while I only have the start address of OPENGL32.wglSwapBu.
From my code, I understand I can do
uintptr_t gatewayRelativeAddr = src - gateway - 5;
where src is the address of OPENGL32.wglSwapBu and gateway is the start address of my code.
// len is 5
BYTE* gateway = (BYTE*)VirtualAlloc(NULL, len+5, MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
memcpy_s(gateway, len, src, len);
// Jump back to original function, but at the highlighted instruction
uintptr_t gatewayRelativeAddr = src - gateway - 5;
// add the jmp opcode to end of gateway
*(gateway + len) = 0xE9;
*(uintptr_t*)(gateway + len + 1) = gatewayRelativeAddr;
I understand thus far what the code does:
calculate the relative address/bytes from the start address of gateway to start address of src(original function). I also -5 to cater for the size of the jump.
However, when I viewed it in memory, it ended up at where I want. But no where in the code I specified it to jmp to the highlighted instruction.
This works because len equals exactly the number of bytes of instructions (5) that precede the desired one, which I presume was the whole point (you want to copy the instructions that will be jumped over, and maybe modify them later on?).
The jump instruction starts at gateway+len, and so EIP at the jump will be gateway+len+5. On the other hand, the address you want to jump to is src+len. So the relative address is (src+len)-(gateway+len+5), and len cancels out, so your formula is correct.
If you want to jump to an instruction that's not the next one after the ones you copied, you'll need to work out its offset from src by disassembly (call it ofs), and then set gatewayRelativeAddr to (src+ofs)-(gateway+len+5).
I am trying to understand the placement of the vector table for Cortex-M3 processor.
According to the Cortex-M3 arch ref manual, the reset behavior is like this (some parts are omitted):
So, we can see that the vectortable comes from the VTOR (Vector Table Offset Register).
According to the Cortex-M3 tech ref manual, the VTOR is defined as:
So we can see, it has a reset value of 0x0. So based on the above 2 criteria, the Cortex-M3 processor expects a vector table at the absolute address 0x0 in the Code area after reset.
But in my MDK uVision IDE, I see my application is placed in the IROM1 area, which starts at 0x8000000, which is within the 0.5G Code memory area according to the Cortex-M3 memory map.
And since it has the Starup button checked, I guess that means the IROM1 area should contain the vector table (please correct me if I am wrong about this).
So I think the vector table should lie at the beginning of IROM1 area, i.e. 0x8000000. And it is indeed so. Below pic shows that at the beginning of IROM1, it is the vector table's 1st entry, the SP value.
And what's more strange, the VTOR register (at 0xE000ED08) still holds a 0x0 value:
So, how could my vector table be found with a 0x0 VTOR reset value?
And just out of curiosity, I checked the memory content at 0x0, there contains exactly the same vector table content as IROM1. So who did this magic copy??
ADD 1 - 4:39 PM 10/9/2020
I guess there must be something I don't know about the startup check box in below pic.
ADD 2 - 5:09 PM 10/9/2020
Thanks to #RealtimeRik and #domen. I downloaded the datasheet for STM32F103x8_xB(https://www.st.com/resource/en/datasheet/stm32f103c8.pdf). In section 4 Memory mapping, I saw below diagram:
So it seems the [0x0, 0x8000000) range does get aliased to somewhere else. But I haven't found how to determine where it is aliased to...
ADD 3 - 5:39 PM 10/9/2020
Now I found it!
I downloaded the STM32Fxxx fef manual (btw it's really huge).
In section 3.4 Boot configuration, it specifies the boot mode configured through the BOOT[1:0] pins.
And with different boot mode, different address aliasing is used:
Depending on the selected boot mode, main Flash memory, system memory
or SRAM is accessible as follows:
Boot from main Flash memory: the main Flash memory is aliased in the boot memory space (0x0000 0000), but still accessible from its
original memory space (0x800 0000). In other words, the Flash memory
contents can be accessed starting from address 0x0000 0000 or 0x800 0000.
Boot from system memory: the system memory is aliased in the boot memory space (0x0000 0000), but still accessible from its original
memory space (0x1FFF B000 in connectivity line devices, 0x1FFF F000 in
other devices).
Boot from the embedded SRAM: SRAM is accessible only at address 0x2000 0000.
What I saw is Boot from main Flash memory.
Well finally I can explain why 0x800 0000 is chosen...
ADD 4 - 3:19 PM 10/15/2020
The placement/expectation of the interrupt vector table at the address 0 is similar to the IA32 processor in real mode...
There is no "Magic Copy". 0x00000000 is aliased to 0x08000000.
The actual memory is physically located at 0x08000000 but can also be access at 0x00000000.
If you look in the processor specific reference manual you should find this in the the memory map section.
Now I'm writing bootloader which starts in the middle of memory, but after it finishes I need to go to the main app, thought to try jmp 0x00, however my chip doesn't support jmp, how should I start main app?
I would use RJMP:
Relative jump to an address within PC - 2K +1 and PC + 2K (words). In
the assembler, labels are used instead of relative operands.
For example:
entry:
rjmp reset
.org 512
reset:
rjmp foo
.org 3072
foo:
rjmp entry
By the way, there are several other jump instructions (RJMP, IJMP, RCALL, ICALL, CALL, RET, RETI etc.) See this relevant discussion.
Well take a look into RET instruction. It returns to previous location, so you can try:
push 0x00
push 0x00
ret
This should work because while entering into any function you push your current location, and RET makes you go back.
As far as I remember ATmege8 has 16-bit address line, but if I'm not right you may need more push 0x00
why not simply use IJMP?
set Z to 0x00 and use IJMP. may be faster than 2xpush and ret
EOR R30, R30 ; clear ZL
EOR R31, R31 ; clear ZH
IJMP ; set PC to Z
should be 4 cycles and 3 instruction words (6 Bytes program memory)
In assembly we use the org instruction to set the location counter to a specific location in the memory. This is particularly helpful in making Operating Systems. Here's an example boot loader (From wikibooks):
org 7C00h
jmp short Start ;Jump over the data (the 'short' keyword makes the jmp instruction smaller)
Msg: db "Hello World! "
EndMsg:
Start: mov bx, 000Fh ;Page 0, colour attribute 15 (white) for the int 10 calls below
mov cx, 1 ;We will want to write 1 character
xor dx, dx ;Start at top left corner
mov ds, dx ;Ensure ds = 0 (to let us load the message)
cld ;Ensure direction flag is cleared (for LODSB)
Print: mov si, Msg ;Loads the address of the first byte of the message, 7C02h in this case
;PC BIOS Interrupt 10 Subfunction 2 - Set cursor position
;AH = 2
Char: mov ah, 2 ;BH = page, DH = row, DL = column
int 10h
lodsb ;Load a byte of the message into AL.
;Remember that DS is 0 and SI holds the
;offset of one of the bytes of the message.
;PC BIOS Interrupt 10 Subfunction 9 - Write character and colour
;AH = 9
mov ah, 9 ;BH = page, AL = character, BL = attribute, CX = character count
int 10h
inc dl ;Advance cursor
cmp dl, 80 ;Wrap around edge of screen if necessary
jne Skip
xor dl, dl
inc dh
cmp dh, 25 ;Wrap around bottom of screen if necessary
jne Skip
xor dh, dh
Skip: cmp si, EndMsg ;If we're not at end of message,
jne Char ;continue loading characters
jmp Print ;otherwise restart from the beginning of the message
times 0200h - 2 - ($ - $$) db 0 ;Zerofill up to 510 bytes
dw 0AA55h ;Boot Sector signature
;OPTIONAL:
;To zerofill up to the size of a standard 1.44MB, 3.5" floppy disk
;times 1474560 - ($ - $$) db 0
Is it possible accomplish the task with C++? Is there any command, function etc. like org where i can change the location of the program?
No it's not possible to do in any C compiler that I know of. You can however create your own linker script that places the code/data/bss segments at specific addresses.
Just for clarity, the org directive does not load the code at the specified address, it merely informs the assembler that the code will be loaded at that address. The code shown appears to be for Nasm (or similar) - in AT&T syntax, the .org directive does something different: it pads the code to that address - similar to the times line in the Nasm code.. Nasm can do this because in -f bin mode, it "acts as it's own linker".
The important thing for the code to know is the address where Msg can be found. The jmps and jnes (and call and ret which your example doesn't have, but a compiler may generate) are relative addressing mode. We code jmp target but the bytes that are actually emitted say jmp distance_to_target (plus or minus) so the address doesn't matter.
Gas doesn't do this, it emits a linkable object file. To use ld without a linker script the command line looks something like:
ld -o boot.bin boot.o -oformat binary -T text=0x7C00
(don't quote me on that exact syntax but "something like that") If you can get a linkable object file from your (16-bit capable!) C++ compiler, you might be able to do the same.
In the case of a bootsector, the code is loaded by the BIOS (or fake BIOS) at 0x7C00 - one of the few things we can assume about the bootsector. The sane thing for a bootsector to do is not fiddle-faddle around printing a message, but to load something else. You'll need to know how to find the something else on the disk and where you want to load it to (perhaps where your C++ compiler wants to put it by default) - and jmp there. This jmp will want to be a far jmp, which does need to know the address.
I'm guessing it's going to be some butt-ugly C++!