Linker error

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
deleted8917
Member
Member
Posts: 119
Joined: Wed Dec 12, 2018 12:16 pm

Linker error

Post by deleted8917 »

Hello, here bothering again with stupid questions.
I've been trying to add an IDT to my OS (real mode). I managed to create the PIC successfully, now I try to add the IDT but when I compile I get this error from the linker:
boot.o: In function `irq0':
kernel/irq.inc:56: undefined reference to `irq0_handler'
boot.o: In function `irq1':
kernel/irq.inc:62: undefined reference to `irq1_handler'
boot.o: In function `irq2':
kernel/irq.inc:68: undefined reference to `irq2_handler'
boot.o: In function `irq3':
kernel/irq.inc:74: undefined reference to `irq3_handler'
boot.o: In function `irq4':
kernel/irq.inc:80: undefined reference to `irq4_handler'
boot.o: In function `irq5':
kernel/irq.inc:86: undefined reference to `irq5_handler'
boot.o: In function `irq6':
kernel/irq.inc:92: undefined reference to `irq6_handler'
boot.o: In function `irq7':
kernel/irq.inc:98: undefined reference to `irq7_handler'
boot.o: In function `irq8':
kernel/irq.inc:104: undefined reference to `irq8_handler'
boot.o: In function `irq9':
kernel/irq.inc:110: undefined reference to `irq9_handler'
boot.o: In function `irq10':
kernel/irq.inc:116: undefined reference to `irq10_handler'
boot.o: In function `irq11':
kernel/irq.inc:122: undefined reference to `irq11_handler'
boot.o: In function `irq12':
kernel/irq.inc:128: undefined reference to `irq12_handler'
boot.o: In function `irq13':
kernel/irq.inc:134: undefined reference to `irq13_handler'
boot.o: In function `irq14':
kernel/irq.inc:140: undefined reference to `irq14_handler'
boot.o: In function `irq15':
kernel/irq.inc:146: undefined reference to `irq15_handler'
kmain.o: In function `kernelmain':
/mnt/c/Users/nicol/Desktop/ckernel/kmain.c:26: undefined reference to `idt_init'

Here's the entire source of my OS:
https://transfer.sh/tjoLh/ckernel.tar.gz
I need to make my own interrupts, so I can my OS MS-DOS compatible.

I borrowed the PIC and IDT code from OSDev btw
Octocontrabass
Member
Member
Posts: 5586
Joined: Mon Mar 25, 2013 7:01 pm

Re: Linker error

Post by Octocontrabass »

konniskatt wrote:I've been trying to add an IDT to my OS (real mode).
You can't. The IDT is a protected mode structure. In real mode, you must have an IVT instead.

As for those linker errors, are you sure you're including all of your code when you link it together? It looks like your build script might be missing some pieces.
deleted8917
Member
Member
Posts: 119
Joined: Wed Dec 12, 2018 12:16 pm

Re: Linker error

Post by deleted8917 »

Sorry for being stupid and confusing IDT with IVT.
So now, how can I make my own interruptions with IVT? OSDev wiki doesn't explain much about IVT
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: Linker error

Post by alexfru »

konniskatt wrote:Sorry for being stupid and confusing IDT with IVT.
So now, how can I make my own interruptions with IVT? OSDev wiki doesn't explain much about IVT
The IVT resides at physical address 0. It has 256 4-byte entries. Each entry is a far address of the ISR (far address being a pair of 16-bit values: offset (at lower address), segment (at higher address)).

IRQs (and the int instruction) would cause FLAGS, CS, IP pushed (in that order, that is, FLAGS at higher address, CS in the middle, IP at the lower address) before the corresponding ISR is entered.

FLAGS.IF is automatically cleared upon entry thus disabling nested/recursive handling of interrupts.

Each ISR should preserve all registers and return with IRET.

IRET pops IP, CS, FLAGS from the stack to resume execution of the interrupted code.

That's about it. The rest (the PIC and I/O ports) should be agnostic of the CPU mode.

However, to program for real mode comfortably (w.r.t. segmentation and that implies interrupts too) you need a compiler that actually supports it (unless you want to write lots of asm code, which is the opposite of comfortable). AFAIK, gcc doesn't quite support it out of the box, I mean the segmentation part specifically. You want something like Open Watcom C/C++ or Smaller C instead. You'll need to learn those and how to load a DOS-style .EXE (or you could use a loader that can do that, e.g. BootProg).

It may be a good start to use OW or Smaller C in some sort of DOS, FreeDOS or DOSBox. It'll be easy to set up the IVT and try out your ISRs there. This part will be the same in DOS and your system.
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Linker error

Post by MichaelPetch »

alexfru wrote:IRQs (and the int instruction) would cause FLAGS, CS, IP pushed (in that order, that is, FLAGS at higher address, CS in the middle, IP at the lower address) before the corresponding ISR is entered.
I'd probably also point out that any reference to error codes in the OSDev Wiki being pushed by the processor after the return address doesn't apply in real mode.

Of course replacing interrupts in real mode has the downside in that if you don't chain to the old BIOS routines, BIOS function calls may no longer work as expected. In some cases chaining to some interrupts isn't straight forward (especially the keyboard) if you want to get it right. Example: If you intercept the hard disk controller interrupts without calling the old interrupt routines in your handler- the hard disk likely won't work. Replacing the timer interrupt (IRQ0) without calling the old one can cause a host of failures for other BIOS calls (like the floppy drive and hard drive access) that may need a timer.
deleted8917
Member
Member
Posts: 119
Joined: Wed Dec 12, 2018 12:16 pm

Re: Linker error

Post by deleted8917 »

Now I'm rewriting my entire OS, and now is a protected mode OS, but the linker (again) are complaining that sections overlap
section .sig loaded at [00000000000007fe,00000000000007ff] overlaps section .boot loaded at [0000000000000600,0000000000000880]

Code: Select all

OUTPUT_FORMAT("elf32-i386");
ENTRY(boot_start);

BOOTLOADER_BASE  = 0x7c00;
BOOTLOADER_RELOC = 0x600;
SECTOR_SIZE      = 512;
KERNEL_BASE      = BOOTLOADER_RELOC + SECTOR_SIZE;

SECTIONS
{
    __boot_reloc_addr = BOOTLOADER_RELOC;
    __boot_base_addr  = BOOTLOADER_BASE;
    __sector_sizew    = SECTOR_SIZE>>1;

    . = BOOTLOADER_RELOC;

    /* Code and data in boot.o placed between 0x7c00 and 0x7e00 */
    .boot : SUBALIGN(0) {
        boot.o(.text*)
        boot.o(.rodata*)
        boot.o(.data)
    }

    . = BOOTLOADER_RELOC + 0x200 - 2;
    /* Boot signature at 510th byte from beginning of bootloader's base */
    .sig : {
        SHORT(0xaa55);
    }

    KERNEL_ADJ = KERNEL_BASE - .;
    . = KERNEL_BASE;

    __disk_load_start = .;
    __disk_load_seg   = (__disk_load_start) >> 4;

    /* Kernel code and data */
    .kernel : AT(ADDR(.kernel) - KERNEL_ADJ) SUBALIGN(4) {
        *(.text*)
        *(.rodata*)
        *(.data)
    }
    __disk_load_end = .;
    __disk_load_num_sectors = (__disk_load_end - __disk_load_start + (SECTOR_SIZE - 1)) / SECTOR_SIZE;

    .kernel.bss : SUBALIGN(4) {
        __bss_start = .;
        *(COMMON);
        *(.bss)
        . = ALIGN(4);
        __bss_end = .;
    }
    __bss_sizew = SIZEOF(.kernel.bss)>>1;

    /* Remove unnecessary sections */
    /DISCARD/ : {
        *(.eh_frame);
        *(.comment);
    }
}
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Linker error

Post by MichaelPetch »

This is because you have put too much code into the boot sector which is 512 bytes long and it has begun to overlap the boot signature. The reason appears to be because you have included a bunch of IRQ related routine into the boot sector (boot.asm). You should be removing that irq related INC file from the boot sector, rename it to a .asm file (place a section .text at the top) and build it as a separate assembly object (with nasm) and add that object to your linking command.
deleted8917
Member
Member
Posts: 119
Joined: Wed Dec 12, 2018 12:16 pm

Re: Linker error

Post by deleted8917 »

Follow their suggestions and now compile without problems, but does not jump (boot2.asm) to kernelmain (kmain.c)

boot.asm

Code: Select all

; These symbols are defined by the linker. We use them to zero BSS section
extern __bss_start
extern __bss_sizew

; These symbols are length (in sectors) of the kernel,
; and segment in memory to start reading to
extern __disk_load_num_sectors
extern __disk_load_seg

extern __sector_sizew;

; Mmory address to relocate the bootsector from / to
extern __boot_base_addr
extern __boot_reloc_addr
extern proc_mode

; This is the C entry point defined in kmain.c
;extern kernelmain               ; kernelmain is C entry point
global boot_start               ; Make this global to suppress linker warning

KERNEL_LBA_START equ 1          ; Logical Block Address(LBA) kernel starts on
                                ;     LBA 1 = sector after boot sector
KERNEL_LBA_END   equ KERNEL_LBA_START + __disk_load_num_sectors
                                ; Logical Block Address(LBA) kernel ends at
DISK_RETRIES     equ 3          ; Number of times to retry on disk error

section .text
bits 16

; Include a BPB (1.44MB floppy with FAT12)
%include "bpb.inc"
;%include "boot2.inc"

boot_start:
    ; This code up until label .reloc must be position independent
    xor eax, eax                ; DS=0 since we use ORG 0x7c00. 0x0000<<4+0x7c00=0x7c00
    mov ds, ax
    mov es, ax
    mov ss, ax                  ; Stack at 0x0000:0x0000
    mov esp, eax                ; After first push will be 0x0000:0xfffe at top of 64kb

    ; Copy bootloader from  __boot_base_addr (0x7c00) to __boot_reloc_addr (0x600)
    ; We copy the bootloader to low memory above the BIOS Data Area (BDA) to allow
    ; more space for the kernel.
    cld
    mov cx, __sector_sizew
    mov si, __boot_base_addr
    mov di, __boot_reloc_addr
    rep movsw

    ; Jump to the relocated boot sector and set CS=0
    jmp 0x0000:.reloc
.reloc:

    ; Read kernel 1 sector at a time until kernel loaded
load_kernel:
    mov [bootDevice], dl        ; Save boot drive
    mov di, __disk_load_seg     ; DI = Current segment to read into
    mov si, KERNEL_LBA_START    ; SI = LBA that kernel starts at
    jmp .chk_for_last_lba       ; Check to see if we are last sector in kernel

.read_sector_loop:
    mov bp, DISK_RETRIES        ; Set disk retry count

    call lba_to_chs             ; Convert current LBA to CHS
    mov es, di                  ; Set ES to current segment number to read into
    xor bx, bx                  ; Offset zero in segment

.retry:
    mov ax, 0x0201              ; Call function 0x02 of int 13h (read sectors)
                                ;     AL = 1 = Sectors to read
    int 0x13                    ; BIOS Disk interrupt call
    jc .disk_error              ; If CF set then disk error

.success:
    add di, 512>>4              ; Advance to next 512 byte segment (0x20*16=512)
    inc si                      ; Next LBA

.chk_for_last_lba:
    cmp si, KERNEL_LBA_END      ; Have we reached the last kernel sector?
    jl .read_sector_loop        ;     If we haven't then read next sector

.kernel_loaded:
    jmp launch_kernel           ; Do realmode initialization and run kernel

.disk_error:
    xor ah, ah                  ; Int13h/AH=0 is drive reset
    int 0x13
    dec bp                      ; Decrease retry count
    jge .retry                  ; If retry count not exceeded then try again

error_end:
    ; Unrecoverable error; print drive error; enter infinite loop
    mov si, diskErrorMsg        ; Display disk error message
    call print_string
    cli
.error_loop:
    hlt
    jmp .error_loop

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:
    mov ah, 0x0e                ; BIOS tty Print
    xor bx, bx                  ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 0x10                    ; print character
.getch:
    lodsb                       ; Get character from string
    test al,al                  ; Have we reached end of string?
    jnz .repeat                 ;     if not process next character
.end:
    ret

;    Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
;              Works for all valid FAT12 compatible disk geometries.
;
;   Resources: http://www.ctyme.com/intr/rb-0607.htm
;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
;              https://stackoverflow.com/q/45434899/3857942
;              Sector    = (LBA mod SPT) + 1
;              Head      = (LBA / SPT) mod HEADS
;              Cylinder  = (LBA / SPT) / HEADS
;
;      Inputs: SI = LBA
;     Outputs: DL = Boot Drive Number
;              DH = Head
;              CH = Cylinder (lower 8 bits of 10-bit cylinder)
;              CL = Sector/Cylinder
;                   Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
;                   Sector in lower 6 bits of CL
;
;       Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
    push ax                     ; Preserve AX
    mov ax, si                  ; Copy LBA to AX
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [sectorsPerTrack]  ; 32-bit by 16-bit DIV : LBA / SPT
    mov cl, dl                  ; CL = S = LBA mod SPT
    inc cl                      ; CL = S = (LBA mod SPT) + 1
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [numHeads]         ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
    mov dh, dl                  ; DH = H = (LBA / SPT) mod HEADS
    mov dl, [bootDevice]        ; boot device, not necessary to set but convenient
    mov ch, al                  ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
    shl ah, 6                   ; Store upper 2 bits of 10-bit Cylinder into
    or  cl, ah                  ;     upper 2 bits of Sector (CL)
    pop ax                      ; Restore scratch registers
    ret

get_memsz:
    int 12h
    mov si, ax
    call print_string

APM_init:
    ; Checking if APM is supported
    mov ah, 53h
    mov al, 00h
    xor bx, bx
    int 15h
    jc APM_error
    ; Disconnect to any APM interface
    mov ah, 53h
    mov al, 04h
    xor bx, bx
    int 15h
    jc .APM_discerr
    jmp .APM_noerr
    ; Connect to the real mode interface
    mov ah, 53h
    mov al, [01h]
    xor bx, bx
    int 15h
    jc APM_error
    ; Enable power management
    mov ah, 53h
    mov al, 08h
    mov bx, 0001h
    mov cx, 0001h
    int 15h
    jc APM_error

.APM_discerr:
    cmp ah, 03h
    jne APM_error

.APM_noerr:


APM_error:
    mov si, APMerrorMsg
    call print_string
    cli
    hlt

; Set up segments so they are 0, zero out the BSS memory and transfer
; control to the function kernelmain
launch_kernel:
    mov si, bootloaderMsg
    call print_string
    ;jmp APM_init
    xor ax, ax
    mov es, ax
    mov fs, ax
    mov gs, ax                  ; ES=FS=GS=0 (we set DS=SS=0 previously)

    ; We need to zero out the BSS section. We'll do it a WORD at a time
    mov edi, __bss_start        ; Start address of BSS
    mov ecx, __bss_sizew        ; Length of BSS in WORDS
                                ; Clear memory with value in AX (0x0000)
    rep stosw                   ; Do clear using string store instruction
                                ;     Clear 2 bytes at a time
    ;call dword kernelmain       ; Call kernel's "C" main entry point
	jmp proc_mode

.end_loop:                      ; Loop forever to terminate when kernel main is finished
    hlt
    jmp .end_loop


section .data
; Uncomment these lines if not using a BPB (via bpb.inc)
; numHeads:        dw 2         ; 1.44MB Floppy has 2 heads & 18 sector per track
; sectorsPerTrack: dw 18

bootDevice:      db 0x00
diskErrorMsg:    db "panic: Unrecoverable disk error!", 0x0A, 0x0D, 0
bootloaderMsg:   db "Starting...", 0x0A, 0x0D, 0
APMerrorMsg:     db "panic: APM interface not supported or failed to initialize.", 0x0A, 0x0D, 0
boot2.asm

Code: Select all

%include "gdt.inc"
%include "a20.inc"

global proc_mode
extern kernelmain

proc_mode:
	cli
	xor ax, ax
	mov ds, ax
	mov es, ax
	mov ax, 0x9000
	mov ss, ax
	mov sp, 0xFFFF
	sti

	;cli
	mov eax, cr0
	or eax, 1
	mov cr0, eax
	jmp check_a20 ;check_a20 returns 0 to ax if a20 is not activated
	cmp ax, 0 ; IF ax == 0 THEN
	je enable_A20 ; jump to enable_A20
	jmp InstallGDT ; Starts GDT
	
	jmp 08h:proc_mode2
	
bits 32 ; Tell to NASM that were gonna use 32 bits code (protected mode)
proc_mode2:
	mov ax, 0x10
	mov ds, ax
	mov ss, ax
	mov es, ax
	mov esp, 90000h
	call dword kernelmain
	
a20.inc

Code: Select all

check_a20:
    pushf
    push ds
    push es
    push di
    push si
 
    cli
 
    xor ax, ax ; ax = 0
    mov es, ax
 
    not ax ; ax = 0xFFFF
    mov ds, ax
 
    mov di, 0x0500
    mov si, 0x0510
 
    mov al, byte [es:di]
    push ax
 
    mov al, byte [ds:si]
    push ax
 
    mov byte [es:di], 0x00
    mov byte [ds:si], 0xFF
 
    cmp byte [es:di], 0xFF
 
    pop ax
    mov byte [ds:si], al
 
    pop ax
    mov byte [es:di], al
 
    mov ax, 0
    je check_a20__exit
 
    mov ax, 1
 
check_a20__exit:
    pop si
    pop di
    pop es
    pop ds
    popf
 
    ret
	
enable_A20:
        cli
 
        call    a20wait
        mov     al,0xAD
        out     0x64,al
 
        call    a20wait
        mov     al,0xD0
        out     0x64,al
 
        call    a20wait2
        in      al,0x60
        push    eax
 
        call    a20wait
        mov     al,0xD1
        out     0x64,al
 
        call    a20wait
        pop     eax
        or      al,2
        out     0x60,al
 
        call    a20wait
        mov     al,0xAE
        out     0x64,al
 
        call    a20wait
        sti
        ret
 
a20wait:
        in      al,0x64
        test    al,2
        jnz     a20wait
        ret
 
 
a20wait2:
        in      al,0x64
        test    al,1
        jz      a20wait2
        ret
gdt.inc

Code: Select all

InstallGDT:
	cli				; clear interrupts
	pusha				; save registers
	lgdt[toc]			; load GDT into GDTR
	sti				; enable interrupts
	popa				; restore registers
	ret				; All done!
 
;*******************************************
; Global Descriptor Table (GDT)
;*******************************************
 
gdt_data: 
	dd 0 				; null descriptor
	dd 0 
 
; gdt code:				; code descriptor
	dw 0FFFFh 			; limit low
	dw 0 				; base low
	db 0 				; base middle
	db 10011010b 			; access
	db 11001111b 			; granularity
	db 0 				; base high
 
; gdt data:				; data descriptor
	dw 0FFFFh 			; limit low (Same as code)
	dw 0 				; base low
	db 0 				; base middle
	db 10010010b 			; access
	db 11001111b 			; granularity
	db 0				; base high

 
	
end_of_gdt:
toc: 
	dw end_of_gdt - gdt_data - 1 	; limit (Size of GDT)
	dd gdt_data 			; base of GDT
kmain.c

Code: Select all

#include <stdint.h>
#define nl "\r\n"

// taken from osdev wiki
void putch(unsigned char c, unsigned char forecolour, unsigned char backcolour, int x, int y)
{
     uint16_t attrib = (backcolour << 4) | (forecolour & 0x0F);
     volatile uint16_t * where;
     where = (volatile uint16_t *)0xB8000 + (y * 80 + x) ;
     *where = c | (attrib << 8);
}

void println(char* str)
{
	static int x = 0;
	static int y = 0;
	while (*str)
	{
		putch(*str++, 7, 0, x, y);
		++y;
	}
}

int kernelmain()
{
	println("Hello protected mode!"nl);
	return 0;
}
Last edited by deleted8917 on Mon Dec 17, 2018 9:13 am, edited 1 time in total.
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Linker error

Post by MichaelPetch »

You don't show us the contents of a20.inc and gdt.inc . Hard to say without seeing everything. At this point I highly recommend using BOCHs to step through the code with the built in debugger to see where (and why) things go wrong. There is no reason to set up the stack here:

Code: Select all

mov ax, 0x9000
   mov ss, ax
   mov sp, 0xFFFF
. You already set the real mode stack right at the beginning of the boot.asm. You should delete these lines. Secondly SS:SP of 0x9000:0xFFFF may conflict with the EBDA (Extended BIOS Data Area) just below physical address 0xA0000 and that can cause issues, which is yet another reason to remove these. PS:setting SP to 0xFFFF can potentially degrade performance on some processors since it is an odd offset.
deleted8917
Member
Member
Posts: 119
Joined: Wed Dec 12, 2018 12:16 pm

Re: Linker error

Post by deleted8917 »

MichaelPetch wrote:You don't show us the contents of a20.inc and gdt.inc . Hard to say without seeing everything. At this point I highly recommend using BOCHs to step through the code with the built in debugger to see where (and why) things go wrong. There is no reason to set up the stack here:

Code: Select all

mov ax, 0x9000
   mov ss, ax
   mov sp, 0xFFFF
. You already set the real mode stack right at the beginning of the boot.asm. You should delete these lines. Secondly SS:SP of 0x9000:0xFFFF may conflict with the EBDA (Extended BIOS Data Area) just below physical address 0xA0000 and that can cause issues, which is yet another reason to remove these. PS:setting SP to 0xFFFF can potentially degrade performance on some processors since it is an odd offset.
added gdt.inc, a20.inc, kmain.c
btw, the OS is rebooting constantly.
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Linker error

Post by MichaelPetch »

A lot wrong with this.
- When going into protected you need to do it with interrupts off CLI
- boot2.asm is missing a bits 16 at the top
- boot2.asm you need to set up the GDT before you turn on the protected mode bit in CR0
- boot2.asm preferable to enable a20 before entering protected mode
- In gdt.inc and a20.inc it is cleaner to put code and data in their proper sections like .data and .text (not required in this case though)
- You incorrectly transfer control to A20 and InstallGDT routines with jmp rather than call
- You branch to enable_a20 which is a JMP, not a CALL. You need to CALL enable_a20 if A20 check says A20 is off
- You unnecessarily reload the segment registers while in real mode in boot,asm
- You set up the real mode stack more than once
- You don't do anything like an infinite loop after the kernel returns from the C code
- In the kernel you do ++y when advancing to next character and it should be ++x
- Your putch routine needs to handle \r and \n characters and alter x and y accordingly. Writing those characters to the screen doesn't advance to the next line of the display automatically. I've put a quick fix in to handle \r and \n but it up to you to handle wrapping at the end of a line to the next, scrolling etc

boot.asm:

Code: Select all

; These symbols are defined by the linker. We use them to zero BSS section
extern __bss_start
extern __bss_sizew

; These symbols are length (in sectors) of the kernel,
; and segment in memory to start reading to
extern __disk_load_num_sectors
extern __disk_load_seg

extern __sector_sizew;

; Mmory address to relocate the bootsector from / to
extern __boot_base_addr
extern __boot_reloc_addr
extern proc_mode

; This is the C entry point defined in kmain.c
;extern kernelmain               ; kernelmain is C entry point
global boot_start               ; Make this global to suppress linker warning

KERNEL_LBA_START equ 1          ; Logical Block Address(LBA) kernel starts on
                                ;     LBA 1 = sector after boot sector
KERNEL_LBA_END   equ KERNEL_LBA_START + __disk_load_num_sectors
                                ; Logical Block Address(LBA) kernel ends at
DISK_RETRIES     equ 3          ; Number of times to retry on disk error

section .text
bits 16

; Include a BPB (1.44MB floppy with FAT12)
%include "bpb.inc"

boot_start:
    ; This code up until label .reloc must be position independent
    xor eax, eax                ; DS=0 since we use ORG 0x7c00. 0x0000<<4+0x7c00=0x7c00
    mov ds, ax
    mov es, ax
    mov ss, ax                  ; Stack at 0x0000:0x0000
    mov esp, eax                ; After first push will be 0x0000:0xfffe at top of 64kb

    ; Copy bootloader from  __boot_base_addr (0x7c00) to __boot_reloc_addr (0x600)
    ; We copy the bootloader to low memory above the BIOS Data Area (BDA) to allow
    ; more space for the kernel.
    cld
    mov cx, __sector_sizew
    mov si, __boot_base_addr
    mov di, __boot_reloc_addr
    rep movsw

    ; Jump to the relocated boot sector and set CS=0
    jmp 0x0000:.reloc
.reloc:

    ; Read kernel 1 sector at a time until kernel loaded
load_kernel:
    mov [bootDevice], dl        ; Save boot drive
    mov di, __disk_load_seg     ; DI = Current segment to read into
    mov si, KERNEL_LBA_START    ; SI = LBA that kernel starts at
    jmp .chk_for_last_lba       ; Check to see if we are last sector in kernel

.read_sector_loop:
    mov bp, DISK_RETRIES        ; Set disk retry count

    call lba_to_chs             ; Convert current LBA to CHS
    mov es, di                  ; Set ES to current segment number to read into
    xor bx, bx                  ; Offset zero in segment

.retry:
    mov ax, 0x0201              ; Call function 0x02 of int 13h (read sectors)
                                ;     AL = 1 = Sectors to read
    int 0x13                    ; BIOS Disk interrupt call
    jc .disk_error              ; If CF set then disk error

.success:
    add di, 512>>4              ; Advance to next 512 byte segment (0x20*16=512)
    inc si                      ; Next LBA

.chk_for_last_lba:
    cmp si, KERNEL_LBA_END      ; Have we reached the last kernel sector?
    jl .read_sector_loop        ;     If we haven't then read next sector

.kernel_loaded:
    jmp launch_kernel           ; Do realmode initialization and run kernel

.disk_error:
    xor ah, ah                  ; Int13h/AH=0 is drive reset
    int 0x13
    dec bp                      ; Decrease retry count
    jge .retry                  ; If retry count not exceeded then try again

error_end:
    ; Unrecoverable error; print drive error; enter infinite loop
    mov si, diskErrorMsg        ; Display disk error message
    call print_string
    cli
.error_loop:
    hlt
    jmp .error_loop

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:
    mov ah, 0x0e                ; BIOS tty Print
    xor bx, bx                  ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 0x10                    ; print character
.getch:
    lodsb                       ; Get character from string
    test al,al                  ; Have we reached end of string?
    jnz .repeat                 ;     if not process next character
.end:
    ret

;    Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
;              Works for all valid FAT12 compatible disk geometries.
;
;   Resources: http://www.ctyme.com/intr/rb-0607.htm
;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
;              https://stackoverflow.com/q/45434899/3857942
;              Sector    = (LBA mod SPT) + 1
;              Head      = (LBA / SPT) mod HEADS
;              Cylinder  = (LBA / SPT) / HEADS
;
;      Inputs: SI = LBA
;     Outputs: DL = Boot Drive Number
;              DH = Head
;              CH = Cylinder (lower 8 bits of 10-bit cylinder)
;              CL = Sector/Cylinder
;                   Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
;                   Sector in lower 6 bits of CL
;
;       Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
    push ax                     ; Preserve AX
    mov ax, si                  ; Copy LBA to AX
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [sectorsPerTrack]  ; 32-bit by 16-bit DIV : LBA / SPT
    mov cl, dl                  ; CL = S = LBA mod SPT
    inc cl                      ; CL = S = (LBA mod SPT) + 1
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [numHeads]         ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
    mov dh, dl                  ; DH = H = (LBA / SPT) mod HEADS
    mov dl, [bootDevice]        ; boot device, not necessary to set but convenient
    mov ch, al                  ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
    shl ah, 6                   ; Store upper 2 bits of 10-bit Cylinder into
    or  cl, ah                  ;     upper 2 bits of Sector (CL)
    pop ax                      ; Restore scratch registers
    ret

get_memsz:
    int 12h
    mov si, ax
    call print_string

APM_init:
    ; Checking if APM is supported
    mov ah, 53h
    mov al, 00h
    xor bx, bx
    int 15h
    jc APM_error
    ; Disconnect to any APM interface
    mov ah, 53h
    mov al, 04h
    xor bx, bx
    int 15h
    jc .APM_discerr
    jmp .APM_noerr
    ; Connect to the real mode interface
    mov ah, 53h
    mov al, [01h]
    xor bx, bx
    int 15h
    jc APM_error
    ; Enable power management
    mov ah, 53h
    mov al, 08h
    mov bx, 0001h
    mov cx, 0001h
    int 15h
    jc APM_error

.APM_discerr:
    cmp ah, 03h
    jne APM_error

.APM_noerr:


APM_error:
    mov si, APMerrorMsg
    call print_string
    cli
    hlt

; Set up segments so they are 0, zero out the BSS memory and transfer
; control to the function kernelmain
launch_kernel:
    mov si, bootloaderMsg
    call print_string
    ;jmp APM_init
    xor ax, ax
    mov es, ax                  ; ES became non-zero during the disk reads
                                ;     so we have to set it to zero again

    ; We need to zero out the BSS section. We'll do it a WORD at a time
    mov edi, __bss_start        ; Start address of BSS
    mov ecx, __bss_sizew        ; Length of BSS in WORDS
                                ; Clear memory with value in AX (0x0000)
    rep stosw                   ; Do clear using string store instruction
                                ;     Clear 2 bytes at a time
    jmp proc_mode


section .data
; Uncomment these lines if not using a BPB (via bpb.inc)
; numHeads:        dw 2         ; 1.44MB Floppy has 2 heads & 18 sector per track
; sectorsPerTrack: dw 18

bootDevice:      db 0x00
diskErrorMsg:    db "panic: Unrecoverable disk error!", 0x0A, 0x0D, 0
bootloaderMsg:   db "Starting...", 0x0A, 0x0D, 0
APMerrorMsg:     db "panic: APM interface not supported or failed to initialize.", 0x0A, 0x0D, 0
boot2.asm:

Code: Select all

bits 16
%include "gdt.inc"
%include "a20.inc"

global proc_mode
extern kernelmain

section .text

proc_mode:

    call check_a20  ; check_a20 returns 0 to ax if a20 is not activated
    test ax, ax
    jnz load_gdt    ; If A20 already enable skip to load GDT
    call enable_A20 ; call enable_A20 to enable A20 since it appears to be off
load_gdt:
    call InstallGDT ; Starts GDT

    cli
    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp 08h:proc_mode2

bits 32 ; Tell to NASM that were gonna use 32 bits code (protected mode)
proc_mode2:
    mov ax, 0x10
    mov ds, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov es, ax
    mov ss, ax
    mov esp, 90000h
    call kernelmain

.end_loop:                      ; Loop forever to terminate when kernel main is finished
    hlt
    jmp .end_loop
kmain.c:

Code: Select all

#include <stdint.h>
#define nl "\r\n"

static int x = 0;
static int y = 0;

// taken from osdev wiki
void putch(unsigned char c, unsigned char forecolour, unsigned char backcolour)
{
    uint16_t attrib = (backcolour << 4) | (forecolour & 0x0F);
    volatile uint16_t * where;
    if (c == '\r')
       x = 0;
    else if (c == '\n')
       y++;
    else {
        where = (volatile uint16_t *)0xB8000 + (y * 80 + x) ;
        *where = c | (attrib << 8);
        x++;
    }
}

void println(char* str)
{
   while (*str)
      putch(*str++, 7, 0);
}

int kernelmain()
{
   println("Hello protected mode!"nl);
   return 0;
}
Post Reply