Bootloader - LBA28 in Long Mode not working

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
binary222
Posts: 2
Joined: Thu Aug 05, 2021 11:50 pm

Bootloader - LBA28 in Long Mode not working

Post by binary222 »

After entering long mode from bootloader, copying the second sector in memory using LBA28 crashes the PC. The second sector was copied in memory in 32 bit mode. The crash happens when executing the instruction

Code: Select all

rep insw
Is there a separate instruction for 64 bit mode?

I'm using Bochs as emualtor

Code:

Code: Select all

	; -- -----------------------------------------------------------------------------------------
	; ----------------------------- Bootloader ( first sector ) ---------------------------------
	; -------------------------------------------------------------------------------------------

	org 7C00h

	; ---------------------------------------- Video Mode --------------------------------------------

	; video mode 80x25:
	 mov     ah, 00h
	 mov     al, 03h
	 int     10h

	; hide blinking
	 mov ch, 32
	 mov ah, 1
	 int 10h


	; ---------------------------------------- 64 Bit Mode --------------------------------------------

	USE16

			cli                             ; disable the interrupts, just in
											; case they are not disabled yet
			lgdt    [cs:GDTR]               ; load GDT register

			mov     eax,cr0                 ; switch to protected mode
			or      al,1
			mov     cr0,eax

			jmp     CODE_SELECTOR:pm_start


	NULL_SELECTOR = 0
	DATA_SELECTOR = 1 shl 3                 ; flat data selector (ring 0)
	CODE_SELECTOR = 2 shl 3                 ; 32-bit code selector (ring 0)
	LONG_SELECTOR = 3 shl 3                 ; 64-bit code selector (ring 0)

	GDTR:                                   ; Global Descriptors Table Register
	  dw 4*8-1                              ; limit of GDT (size minus one)
	  dq GDT                                ; linear address of GDT

	GDT rw 4                                ; null desciptor
		dw 0FFFFh,0,9200h,08Fh              ; flat data desciptor
		dw 0FFFFh,0,9A00h,0CFh              ; 32-bit code desciptor
		dw 0FFFFh,0,9A00h,0AFh              ; 64-bit code desciptor

			USE32

	pm_start:

			mov     eax,DATA_SELECTOR       ; load 4 GB data descriptor
			mov     ds,ax                   ; to all data segment registers
			mov     es,ax
			mov     fs,ax
			mov     gs,ax
			mov     ss,ax

			mov     eax,cr4
			or      eax,1 shl 5
			mov     cr4,eax                 ; enable physical-address extensions

			mov     edi,70000h
			mov     ecx,4000h shr 2
			xor     eax,eax
			rep     stosd                   ; clear the page tables

			mov     dword [70000h],71000h + 111b ; first PDP table
			mov     dword [71000h],72000h + 111b ; first page directory
			mov     dword [72000h],73000h + 111b ; first page table

			mov     edi,73000h              ; address of first page table
			mov     eax,0 + 111b
			mov     ecx,256                 ; number of pages to map (1 MB)
	  make_page_entries:
			stosd
			add     edi,4
			add     eax,1000h
			loop    make_page_entries

			mov     eax,70000h
			mov     cr3,eax                 ; load page-map level-4 base

			mov     ecx,0C0000080h          ; EFER MSR
			rdmsr
			or      eax,1 shl 8             ; enable long mode
			wrmsr

			mov     eax,cr0
			or      eax,1 shl 31
			mov     cr0,eax                 ; enable paging

			jmp     LONG_SELECTOR:long_start

			USE64

	long_start:

		   ; mov     rax,'L O N G '
			;mov     [0B8000h],rax


	; ---------------------------------------- Load Data --------------------------------------------

	mov     dx,0x1F1
	mov     al,0h
	out     dx,al

	mov     dx,1f2h
	mov     al,2          ; number of sectors to read
	out     dx,al


	mov     dx,1f3h
	mov     al,1        ; 4 8 8 [8]
	out     dx,al

	mov     dx,1f4h
	mov     al,0        ; 4 8 [8] 8
	out     dx,al

	mov     dx,1f5h        ; 4 [8] 8 8
	mov     al,0
	out     dx,al

	mov     dx,0x1F6
	mov     al,0xE0
	out     dx,al


	mov     dx,1f7h
	mov     al,20h   ; 20h read   30h write
	out     dx,al

	mov ecx, 4

	loop1:
			mov dx, 1F7h
			in al, dx

			test al, 10000000b
			jnz short retry
			test al, 00001000b
			jnz short data_ready
	retry:
			dec ecx
			jg short loop1
	loop2:
			mov dx, 1F7h
			in al, dx

			test al, 10000000b   ; Check BSY
			jnz short loop2
			test al, 00100001b   ; Check ERR and DF
			jnz short fail
	data_ready:

			mov dx, 0x1F0
			mov cx, 256*2
			mov edi, 2000000

			rep insw

			mov     rax,'L O N G '
			mov     [0B8000h],rax

		  hlt

	fail:
			mov     rax,'F A I L '
			mov     [0B8000h],rax
	hlt


	; fill witgh zeroes the rest
	times 510-($-$$) db 0
	dw 0xAA55

	; -------------------------------------------------------------------------------------------
	; ----------------------------- Kernel ( second sector ) ---------------------------------
	; -------------------------------------------------------------------------------------------

	org 0


			mov     rax,'L O N 3 '
			mov     [0B8000h],rax


		   hlt
	 times 512*3-($-$$) db 0
klange
Member
Member
Posts: 679
Joined: Wed Mar 30, 2011 12:31 am
Libera.chat IRC: klange
Discord: klange

Re: Bootloader - LBA28 in Long Mode not working

Post by klange »

Code: Select all

         mov     ecx,256                 ; number of pages to map (1 MB)

Code: Select all

         mov edi, 2000000
It sounds like you're only mapping the lowest 1MiB of memory but your destination starts around 1.9MiB.
Bochs should be able to tell you all the details of the fault that is occurring, such as if it is a page fault and what address.
binary222
Posts: 2
Joined: Thu Aug 05, 2021 11:50 pm

Re: Bootloader - LBA28 in Long Mode not working

Post by binary222 »

I don't understand mapping. After testing, mov ecx, 256*2 ( I'm loading 2 sectors 512 Bytes each ) works for 1MB address location, it doesn't work for 2MB or greater. How do I load to larger addresses and how can I use 64 bit memory locations?

Jumping to the code loaded in memory worked in 32 bit mode by using

Code: Select all

jmp CodeDescriptor:1000000
For 64 bit mode

Code: Select all

jmp LONG_SELECTOR:1000000
or

Code: Select all

jmp 1000000
doesn't work
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Bootloader - LBA28 in Long Mode not working

Post by iansjack »

binary222 wrote:I don't understand mapping.
And yet you are using page mapping.

This suggests that you are copy-pasting code without understanding what it is doing. This is not a good idea. I'd suggest that you read the appropriate Programmer's Manual, in particular the chapter(s) on memory management.
Octocontrabass
Member
Member
Posts: 5567
Joined: Mon Mar 25, 2013 7:01 pm

Re: Bootloader - LBA28 in Long Mode not working

Post by Octocontrabass »

binary222 wrote:using LBA28
And an IDE controller mapped at the legacy I/O ports. What will you do if the IDE controller is mapped at different ports, or the disk isn't attached to an IDE controller?
binary222 wrote:insw
This instruction may read from the port without writing to memory if writing to memory causes an exception (such as a page fault).
davmac314
Member
Member
Posts: 121
Joined: Mon Jul 05, 2021 6:57 pm

Re: Bootloader - LBA28 in Long Mode not working

Post by davmac314 »

binary222 wrote:I don't understand mapping. After testing, mov ecx, 256*2 ( I'm loading 2 sectors 512 Bytes each ) works for 1MB address location, it doesn't work for 2MB or greater. How do I load to larger addresses and how can I use 64 bit memory locations?
I'm not sure what you mean by "works for 1MB address location", if you mean a location within the 1st MB, then that would make sense. As klange says, you've mapped only the 1st MB; anything beyond that will fault.
binary222 wrote:The second sector was copied in memory in 32 bit mode
Without seeing the 32-bit code, it's impossible to say why it worked; if the only difference was 32-bit mode vs long mode, it shouldn't have worked for the same reason it doesn't work in long mode. Perhaps your 32-bit code was loading into <1MB range, or perhaps it hadn't enabled paging.
binary222 wrote:I don't understand mapping
It sounds like it's time to take a step back. I suggest you go and read up on page mapping, starting with the relevant pages on the wiki, and ideally reading the relevant chapter in the Intel architecture manual (vol 3), until you understand what the code that sets up the page tables is actually doing. Then you should be able to see that it's not setting up a mapping for any address beyond 1MB.

With 4kb page tables each covering a linear address range of 1MB, you're going to need at least 2 pages to cover 2MB (should be enough for your current example), and more if you want to cover the rest of available RAM (assuming there is more than 2MB, which seems likely).
Post Reply