Possible problem in reading e820 output

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
algawi86
Posts: 10
Joined: Wed Feb 01, 2017 6:48 am
Libera.chat IRC: asaf_algawi

Possible problem in reading e820 output

Post by algawi86 »

hi,
I'm using the code from osdev wiki to read e820 output as follows:

Code: Select all

[BITS 16]
e820_start:
	mov di, 0
	xor ebx, ebx		; ebx must be 0 to start
	xor bp, bp		; keep an entry count in bp
	mov edx, 0x0534D4150	; Place "SMAP" into edx
	mov eax, 0xe820
	mov [es:di + 20], dword 1	; force a valid ACPI 3.X entry
	mov ecx, 24		; ask for 24 bytes
	int 0x15
	jc short .failed	; carry set on first call means "unsupported function"
	mov edx, 0x0534D4150	; Some BIOSes apparently trash this register?
	cmp eax, edx		; on success, eax must have been reset to "SMAP"
	jne short .failed
	test ebx, ebx		; ebx = 0 implies list is only 1 entry long (worthless)
	je short .failed
	jmp short .jmpin
.e820lp:
	mov eax, 0xe820		; eax, ecx get trashed on every int 0x15 call
	mov [es:di + 20], dword 1	; force a valid ACPI 3.X entry
	mov ecx, 24		; ask for 24 bytes again
	int 0x15
	jc short .e820f		; carry set means "end of list already reached"
	mov edx, 0x0534D4150	; repair potentially trashed register
.jmpin:
	jcxz .skipent		; skip any 0 length entries
	cmp cl, 20		; got a 24 byte ACPI 3.X response?
	jbe short .notext
	test byte [es:di + 20], 1	; if so: is the "ignore this data" bit clear?
	je short .skipent
.notext:
	mov ecx, [es:di + 8]	; get lower uint32_t of memory region length
	or ecx, [es:di + 12]	; "or" it with upper uint32_t to test for zero
	jz .skipent		; if length uint64_t is 0, skip entry
	inc bp			; got a good entry: ++count, move to next storage spot
	add di, 24
.skipent:
	test ebx, ebx		; if ebx resets to 0, list is complete
	jne short .e820lp
.e820f:
	xor eax,eax
	mov ax,bp
	push eax
	xor eax,eax
	mov eax, es
	shl eax, 4
	add eax, 0
	push eax         ; i will use the address in this register later in 32bit mode to access the list.
	clc			; there is "jc" on end of list to this point, so the carry must be cleared
	jmp 0:(r16_tail_start-r16_header_start + 0x7e00)
.failed:
	stc			; "function unsupported" error exit
	ret
e820_end:
and when using it in vmware i get the following output:
(read as base, limit, type)

Code: Select all

0000000000009F400 00000000000A0000 0002
000000000000CE000 00000000000D0000 0002
000000000000DC000 0000000000100000 0002
00000000000100000 00000000BFEE0000 0001
000000000BFEE0000 00000000BFEFF000 0003
000000000BFEFF000 00000000BFF00000 0004
000000000BFF00000 00000000C0000000 0001
000000000F0000000 00000000F8000000 0002
000000000FEC00000 00000000FEC10000 0002
000000000FEE00000 00000000FEE01000 0002
000000000FFFE0000 0000000100000000 0002
00000000100000000 0000000140000000 0001
which is kind of weird, since it does not report the base of memory as usable.
i tried to a live ubuntu cd and used it's print of e820 (from dmesg) and of course i got correct results (with base of memory accesible)
the final test i did was to run my exact code in kvm and again got correct results:

Code: Select all

00000000000000000 000000000009FC00 0001
0000000000009FC00 00000000000A0000 0002
000000000000F0000 0000000000100000 0002
00000000000100000 00000000BFFE0000 0001
000000000BFFE0000 00000000C0000000 0002
000000000FEFFC000 00000000FF000000 0002
000000000FFFC0000 0000000100000000 0002
00000000100000000 0000000140000000 0001
is there anything i'm missing in regards to how vmware handles those calls ?
User avatar
TightCoderEx
Member
Member
Posts: 90
Joined: Sun Jan 13, 2013 6:24 pm
Location: Grande Prairie AB

Re: Possible problem in reading e820 output

Post by TightCoderEx »

ES:DI = Destination, so ES needs to be set before you start routine. All I did to your code was add

Code: Select all

			mov		ax, 800h
			mov		es, ax
before e820_start: and it returned a map in BOCHs the same as this code does.

Code: Select all

; Read E820 map into a temporary buffer just above boot sector @ 7E0:0

			mov    	ax, BOOT_SEG + 32	; So MSB of EAX is nullified
			mov		es, ax
			mov		ds, ax				; So segment overrides are not required

			xor		di, di				; ES:DI = Pointer to base address of map
			mov		bx, di				; Initial continuation value
			mov	   edx, 'PAMS'			; Function signature
			push	edx

	; Top of loop to read first or next map entry

ReadNext:	inc		byte [MAP_ENTR]		; Bump number of map entries = 0 first iteration.
   .skip:	mov		cl, 48				; Let function call know how big entry can be.
			mov		ax, 0xe820			; System Service function number.
			int		SYS_SERVICE

	; Assert the possible error and termination conditions

			jc		.done				; CY = 1 can happen in all cases
			cmp		bl, 1				; Is this the first entry
			jb		.done				; If zero, no more entries
			ja		.J0					; Next code only needs happen on first iteration

		; This need only happen on first iteration

			pop		edx
			sub		eax, edx			; Does BIOS even support this function
			jz		.J0 - 3
			dec		byte [MAP_ENTR]		; Bump value back to -1
			jmp		.done

			mov		[MAP_SIZE], cl		; Save actual size of entry returned by function.

	.J0:	jcxz	.skip				; Ignore any null length entries
			cmp		cl, 20
			jbe		.J1

			test	byte [di + 20], 1	; Ignore ACPI entries
			jz		.skip

		; Test 64 bit value representing length for zero

	.J1:	mov		eax, [di + 8]		; Get low order DWORD of length
			or		eax, [di + 12]		; Determine if QWORD value is zero
			jz		.skip

		; Bump ES:DI pointer to next entry

			add		di, cx
			jmp		ReadNext

	.done:	or		di, di				; Was a map even created
Post Reply