Given that you have ENTRY(kernel_main) in your linker file: yes.Schol-R-LEA wrote: It is also showing the entry point as 0xc0000008, which is definitely not what I intended. Or am I mistaken about that? Is it showing the entry point for kernel_main() rather than kstart?
triple-fault on jumping to kernel
- Demindiro
- Member
- Posts: 96
- Joined: Fri Jun 11, 2021 6:02 am
- Libera.chat IRC: demindiro
- Location: Belgium
- Contact:
Re: triple-fault on jumping to kernel
-
- Member
- Posts: 5563
- Joined: Mon Mar 25, 2013 7:01 pm
Re: triple-fault on jumping to kernel
According to readelf, the linker has combined .text and .rodata into a single segment, so the memory footprint is correct. It's only a problem if you want .rodata to be in a segment that's read-only instead if read/executeSchol-R-LEA wrote:Based on readelf, that is in fact the size of the memory footprint. This is concerning to me, I wonder if this reflects a problem in the linker script.
I think you accidentally put the wrong symbol in your linker script.Schol-R-LEA wrote:It is also showing the entry point as 0xc0000008, which is definitely not what I intended. Or am I mistaken about that? Is it showing the entry point for kernel_main() rather than kstart?
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: triple-fault on jumping to kernel
headdesk Yes, that was a mistake. Let me correct that.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
- Demindiro
- Member
- Posts: 96
- Joined: Fri Jun 11, 2021 6:02 am
- Libera.chat IRC: demindiro
- Location: Belgium
- Contact:
Re: triple-fault on jumping to kernel
I figured it out. Very devious
You overwrite ds, then load from [section_offset_buffer].
There is still other stuff wrong with the loading code but at least the entry point looks right:
Diff:
You overwrite ds, then load from [section_offset_buffer].
There is still other stuff wrong with the loading code but at least the entry point looks right:
Code: Select all
(gdb) disas $rip,$rip+50
Dump of assembler code from 0xc0000000 to 0xc0000032:
=> 0x00000000c0000000: call 0xc0000008
0x00000000c0000005: hlt
0x00000000c0000006: jmp 0xc0000005
0x00000000c0000008: push rbp
0x00000000c0000009: mov ebp,esp
0x00000000c000000b: call 0xc00001c0
0x00000000c0000010: add eax,0x1ff8
0x00000000c0000015: nop
0x00000000c0000016: pop rbp
0x00000000c0000017: ret
0x00000000c0000018: push rbp
0x00000000c0000019: mov ebp,esp
0x00000000c000001b: call 0xc00001c0
0x00000000c0000020: add eax,0x1fe8
0x00000000c0000025: mov edx,DWORD PTR [rax-0x4]
0x00000000c000002b: add edx,0x2
0x00000000c000002e: mov DWORD PTR [rax-0x4],edx
Code: Select all
@@ -250,20 +250,22 @@ find_kernel_code_block:
push es
mov ax, kernel_base
mov es, ax
- mov dx, gs:[bx + ELF32_Program_Header.p_memsz]
- add [section_offset_buffer], dx ; dx = total size to allocate to the kernel code memory area
push bx
memset_rm 0, bx, dx
pop bx
; move the code section of the file to the kernel code memory area
+ mov dx, [section_offset_buffer]
push ds
mov ax, gs
mov ds, ax
- memcopy_rm [section_offset_buffer], [bx + ELF32_Program_Header.p_offset], [bx + ELF32_Program_Header.p_filesz]
+ memcopy_rm dx, [bx + ELF32_Program_Header.p_offset], [bx + ELF32_Program_Header.p_filesz]
pop ds
pop es
+ mov dx, gs:[bx + ELF32_Program_Header.p_memsz]
+ add [section_offset_buffer], dx ; dx = total size to allocate to the kernel code memory area^M
+
.loop_continue:
; advance the pointer through the header array
add bx, ELF32_Program_Header_size
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: triple-fault on jumping to kernel
OK, that did in fact fix the immediate problem of how the code section is getting written to. The current version of the loading code (which I will push to the repo ASAP) is
The disassembly is now:
It looks as if the code should now run, and it appears that it does when I step into it - except that I now need to debug the terminal-handling code.
Code: Select all
load_kernel_code:
push ax
push es
mov si, kernel_filename
mov di, word [bp - stg2_parameters.directory_buffer]
mov cx, Root_Entries
mov bx, dir_entry_size
call near seek_directory_entry
cmp di, word 0
jnz .read_directory
write no_kernel
jmp local_halt_loop
.read_directory:
call read_directory_details
write kernel_file_found
; reset the disk drive
call near reset_disk
mov di, word [bp - stg2_parameters.fat_0]
mov ax, kernel_raw_base
mov es, ax
mov si, kernel_raw_offset
call near fat_to_file
pop es
pop ax
write kernel_loaded
find_kernel_code_block:
push gs
mov ax, kernel_raw_base
mov gs, ax
mov al, byte gs:[kernel_raw_offset + ELF32_Header.magic]
cmp al, byte ELF_Magic
je .test_signature
write invalid_elf_magic
jmp local_halt_loop
.test_signature:
mov cx, 3
mov di, kernel_raw_offset + ELF32_Header.sig
push es
mov ax, kernel_raw_base
mov es, ax
mov si, ELF_Sig
repe cmpsb
pop es
je .test_elf_endianness
write invalid_elf_sig
jmp local_halt_loop
.test_elf_endianness:
mov al, byte gs:[kernel_raw_offset + ELF32_Header.endianness]
cmp al, ELF_little_endian
je .test_elf_isa
write elf_big_endian
jmp local_halt_loop
.test_elf_isa:
mov al, byte gs:[kernel_raw_offset + ELF32_Header.isa]
cmp al, ELF_ISA_x86
je .test_elf_executable
write elf_not_x86
jmp local_halt_loop
.test_elf_executable:
mov ax, word gs:[kernel_raw_offset + ELF32_Header.type]
cmp ax, ELF_type_executable
je .read_elf_header_table
write non_executable_elf_file
jmp local_halt_loop
.read_elf_header_table:
write valid_elf_file
; set up an offset for the code sections in memory
mov [section_offset], word kcode_offset
mov cx, gs:[kernel_raw_offset + ELF32_Header.program_table_entry_count]
write number_of_sections
mov ax, cx
call print_decimal_word
write newline
; while most of the size fields in the ELF32 header are 32-bit dwords,
; a number of places in this code assume that the amounts in question
; are under 64KiB, and can be read with a 16-bit word. This is a reasonable
; assumption with the current code, but may not hold if the kernel grows
; beyond it's current <64KiB size.
; bx = pointer to the first program header - location must be under 64KiB to work
mov bx, word gs:[kernel_raw_offset + ELF32_Header.program_header_table]
.program_header_loop:
; check to see if the section is loadable
mov ax, gs:[bx + ELF32_Program_Header.p_type]
cmp ax, ELF_Header_loadable_type
jne .loop_continue ; not a loadable section, skip
; found a loadable section
; first, clear the region of memory to load to
push es
mov ax, kernel_base
mov es, ax ; set es to the segment to later map to higher half
mov dx, gs:[bx + ELF32_Program_Header.p_memsz]
memset_rm 0, dx, [section_offset]
; move the code section of the file to the kernel code memory area
; keep ES set to the destination segment
mov dx, [section_offset]
push ds ; temporarily set DS = GS so the macro works on the right segments
mov ax, gs
mov ds, ax
memcopy_rm dx, [bx + ELF32_Program_Header.p_offset], [bx + ELF32_Program_Header.p_filesz]
pop ds
pop es
mov dx, gs:[bx + ELF32_Program_Header.p_memsz]
add [section_offset], dx ; dx = total size to allocate to the kernel code memory area
.loop_continue:
; advance the pointer through the header array
add bx, ELF32_Program_Header_size
add [section_offset], dx ; dx = total size to allocate to the kernel code memory area
loop .program_header_loop
pop gs
Code: Select all
0xc0000000: call 0xc0000008
0xc0000005: hlt
0xc0000006: jmp 0xc0000005
0xc0000008: push %ebp
0xc0000009: mov %esp,%ebp
0xc000000b: sub $0x8,%esp
0xc000000e: call 0xc0000158
0xc0000013: sub $0x8,%esp
0xc0000016: push $0x7
0xc0000018: push $0xc0001000
0xc000001d: call 0xc0000110
0xc0000022: add $0x10,%esp
0xc0000025: nop
0xc0000026: leave
0xc0000027: ret
0xc0000028: push %ebp
0xc0000029: mov %esp,%ebp
0xc000002b: mov 0xc0002004,%eax
0xc0000030: add $0x2,%eax
0xc0000033: mov %eax,0xc0002004
0xc0000038: movzwl 0xc0003002,%eax
0xc000003f: mov $0x50,%edx
0xc0000044: cmp %dx,%ax
0xc0000047: jae 0xc000005b
0xc0000049: movzwl 0xc0003002,%eax
0xc0000050: add $0x1,%eax
0xc0000053: mov %ax,0xc0003002
0xc0000059: jmp 0xc0000085
0xc000005b: movw $0x0,0xc0003002
0xc0000064: movzwl 0xc0003000,%eax
0xc000006b: mov $0x19,%edx
0xc0000070: cmp %dx,%ax
0xc0000073: jae 0xc0000085
0xc0000075: movzwl 0xc0003000,%eax
0xc000007c: add $0x1,%eax
0xc000007f: mov %ax,0xc0003000
0xc0000085: nop
0xc0000086: pop %ebp
0xc0000087: ret
0xc0000088: push %ebp
0xc0000089: mov %esp,%ebp
0xc000008b: sub $0x18,%esp
0xc000008e: mov 0x8(%ebp),%edx
0xc0000091: mov 0xc(%ebp),%eax
0xc0000094: mov %dx,-0x14(%ebp)
0xc0000098: mov %ax,-0x18(%ebp)
0xc000009c: mov $0x19,%eax
0xc00000a1: movzwl %ax,%edx
0xc00000a4: mov $0x50,%eax
0xc00000a9: movzwl %ax,%eax
0xc00000ac: imul %edx,%eax
0xc00000af: add $0xb8000,%eax
0xc00000b4: mov %eax,-0x4(%ebp)
0xc00000b7: mov 0xc0002004,%eax
0xc00000bc: mov %eax,%edx
0xc00000be: mov -0x4(%ebp),%eax
0xc00000c1: add %edx,%eax
0xc00000c3: mov %eax,0xc0002004
0xc00000c8: movzwl -0x14(%ebp),%eax
0xc00000cc: mov %ax,0xc0003002
0xc00000d2: movzwl -0x18(%ebp),%eax
0xc00000d6: mov %ax,0xc0003000
0xc00000dc: nop
0xc00000dd: leave
0xc00000de: ret
0xc00000df: push %ebp
0xc00000e0: mov %esp,%ebp
0xc00000e2: sub $0x8,%esp
0xc00000e5: mov 0x8(%ebp),%edx
0xc00000e8: mov 0xc(%ebp),%eax
0xc00000eb: mov %dl,-0x4(%ebp)
0xc00000ee: mov %al,-0x8(%ebp)
0xc00000f1: mov 0xc0002004,%eax
0xc00000f6: movzbl -0x4(%ebp),%edx
0xc00000fa: mov %dl,(%eax)
0xc00000fc: mov 0xc0002004,%eax
0xc0000101: movzbl -0x8(%ebp),%edx
0xc0000105: mov %dl,0x1(%eax)
0xc0000108: call 0xc0000028
0xc000010d: nop
0xc000010e: leave
0xc000010f: ret
0xc0000110: push %ebp
0xc0000111: mov %esp,%ebp
0xc0000113: sub $0x14,%esp
0xc0000116: mov 0xc(%ebp),%eax
0xc0000119: mov %al,-0x14(%ebp)
0xc000011c: movl $0x0,-0x4(%ebp)
0xc0000123: jmp 0xc0000145
0xc0000125: movzbl -0x14(%ebp),%edx
0xc0000129: mov -0x4(%ebp),%ecx
0xc000012c: mov 0x8(%ebp),%eax
0xc000012f: add %ecx,%eax
0xc0000131: movzbl (%eax),%eax
0xc0000134: movsbl %al,%eax
0xc0000137: push %edx
0xc0000138: push %eax
0xc0000139: call 0xc00000df
0xc000013e: add $0x8,%esp
0xc0000141: addl $0x1,-0x4(%ebp)
0xc0000145: mov -0x4(%ebp),%edx
0xc0000148: mov 0x8(%ebp),%eax
0xc000014b: add %edx,%eax
0xc000014d: movzbl (%eax),%eax
0xc0000150: test %al,%al
0xc0000152: jne 0xc0000125
0xc0000154: nop
0xc0000155: nop
0xc0000156: leave
0xc0000157: ret
0xc0000158: push %ebp
0xc0000159: mov %esp,%ebp
0xc000015b: sub $0x10,%esp
0xc000015e: movl $0x0,-0x4(%ebp)
0xc0000165: jmp 0xc0000177
0xc0000167: push $0x0
0xc0000169: push $0x20
0xc000016b: call 0xc00000df
0xc0000170: add $0x8,%esp
0xc0000173: addl $0x1,-0x4(%ebp)
0xc0000177: mov $0x7d0,%eax
0xc000017c: movzwl %ax,%eax
0xc000017f: cmp %eax,-0x4(%ebp)
0xc0000182: jl 0xc0000167
0xc0000184: nop
0xc0000185: nop
0xc0000186: leave
0xc0000187: ret
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: triple-fault on jumping to kernel
OK, while that was not a critical issue in the moment as I was trying to get the loader's basic functionality working, it is definitely something I will want to address now. I assume that the solution will be to set up a separate set of pages for the .rodata mapped to someplace other than where it is getting loaded right now, correct? That part is straightforward, I would think; it is the changes to the linker script and the ELF loader itself that I am not clear about.Octocontrabass wrote:According to readelf, the linker has combined .text and .rodata into a single segment, so the memory footprint is correct. It's only a problem if you want .rodata to be in a segment that's read-only instead if read/executeSchol-R-LEA wrote:Based on readelf, that is in fact the size of the memory footprint. This is concerning to me, I wonder if this reflects a problem in the linker script.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
-
- Member
- Posts: 5563
- Joined: Mon Mar 25, 2013 7:01 pm
Re: triple-fault on jumping to kernel
No need, the current location is fine.Schol-R-LEA wrote:OK, while that was not a critical issue in the moment as I was trying to get the loader's basic functionality working, it is definitely something I will want to address now. I assume that the solution will be to set up a separate set of pages for the .rodata mapped to someplace other than where it is getting loaded right now, correct?
For removing the write permission, just clear the "writable" bit in the PTEs for the pages that aren't supposed to be writable, and set CR0.WP. (If you do this to pages that are already marked present while paging is enabled, you also need to flush those pages from the TLB.) No changes to your linker script are necessary for this.Schol-R-LEA wrote:That part is straightforward, I would think; it is the changes to the linker script and the ELF loader itself that I am not clear about.
For removing the execute permission, it's not so simple. The PTEs don't have a "not executable" bit unless you're using PAE, so the only way to limit which addresses are executable is the code segment limit. Since your kernel is in the higher half, everything in userspace will remain executable, and fast system call instructions don't support a non-flat code segment, so it may not be worth the effort. You also need to change how you link the kernel... but I have no idea what needs to be changed because I've never gotten this far. (Maybe add "-z separate-code" to your linker flags? Or "-Wl,-z,separate-code" if you link with GCC.)
Re: triple-fault on jumping to kernel
Honestly, I wouldn't bother. .rodata and .text can live very well together, united by their lack of a writable flag. As long as they are mapped read-only (you do not use self-modifying code, right?), you will reap most of the benefits.Schol-R-LEA wrote:OK, while that was not a critical issue in the moment as I was trying to get the loader's basic functionality working, it is definitely something I will want to address now. I assume that the solution will be to set up a separate set of pages for the .rodata mapped to someplace other than where it is getting loaded right now, correct? That part is straightforward, I would think; it is the changes to the linker script and the ELF loader itself that I am not clear about.Octocontrabass wrote:According to readelf, the linker has combined .text and .rodata into a single segment, so the memory footprint is correct. It's only a problem if you want .rodata to be in a segment that's read-only instead if read/execute
But if you must positively have .rodata mapped without execute permissions (which is difficult without PAE, as Octo already said), then you can put a page alignment into your linker script between the .text and .rodata sections.
Code: Select all
. = ALIGN(4096);
You have different CS selectors for kernel and user space. So you can have a kernel execute limit and a user execute limit. That won't help you with the kernel jumping into userspace, but it does help with the execution of read-only data. Biggest problem is going to be the granularity. The segment limit is a twenty-bit number, and with the base being zero, it should encode the highest executable address. For higher-half addresses it can only do so at page granularity. So the break between .text and .rodata needs to be page aligned.Octocontrabass wrote:For removing the execute permission, it's not so simple. The PTEs don't have a "not executable" bit unless you're using PAE, so the only way to limit which addresses are executable is the code segment limit. Since your kernel is in the higher half, everything in userspace will remain executable, and fast system call instructions don't support a non-flat code segment, so it may not be worth the effort.
You can simply use a linker script. Linker scripts are recommended to kernels anyway, because you can set a lot of details in them, add symbols around certain sections of your data (custom data sections really help with collecting a bunch of different things from various object files). And in a linker script, you can add the above mentioned alignment, and even force the use of another program header.Octocontrabass wrote:You also need to change how you link the kernel... but I have no idea what needs to be changed because I've never gotten this far. (Maybe add "-z separate-code" to your linker flags? Or "-Wl,-z,separate-code" if you link with GCC.)
Carpe diem!
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: triple-fault on jumping to kernel
Fair enough, though in the current version I haven't actually marked the pages as r/o yet (oops). Though since the actual flag (bit 2 in a Page Table Entry) is r/w on set, I guess that the default is correct.nullplan wrote:Honestly, I wouldn't bother. .rodata and .text can live very well together, united by their lack of a writable flag. As long as they are mapped read-only (you do not use self-modifying code, right?), you will reap most of the benefits.Schol-R-LEA wrote:OK, while that was not a critical issue in the moment as I was trying to get the loader's basic functionality working, it is definitely something I will want to address now. I assume that the solution will be to set up a separate set of pages for the .rodata mapped to someplace other than where it is getting loaded right now, correct? That part is straightforward, I would think; it is the changes to the linker script and the ELF loader itself that I am not clear about.Octocontrabass wrote:According to readelf, the linker has combined .text and .rodata into a single segment, so the memory footprint is correct. It's only a problem if you want .rodata to be in a segment that's read-only instead if read/execute
As for self-modifying code, that will have to wait until I can write a suitable means of defining quajects, and that's not part of my plans for this particular OS project.
Note that I was able to get the (very simple and limited) console-handling code working enough to clear the screen and write a string (though I haven't handled scrolling or newlines yet), and have pushed that to the repo.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Re: triple-fault on jumping to kernel
SMEP can help with that, but kernel jumping into userspace would be a rare bug. Out of bound access trashing large chunks of code seems way more likely.nullplan wrote:You have different CS selectors for kernel and user space. So you can have a kernel execute limit and a user execute limit. That won't help you with the kernel jumping into userspace
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: triple-fault on jumping to kernel
I'm not entirely sure where to go from here; I would say I'll probably need to address the IDT and ISRs next, but I am not sure if there are any other things I need to address first.
EDIT: I just thought of something else I need to do: retrieve the BIOS and disk directory data that I put into the upper portion of the HMA while in real mode, which is now mapped to somewhere like 0xC000BF6A. Assuming that the code for storing it there works correctly in the first place.
EDIT: I just thought of something else I need to do: retrieve the BIOS and disk directory data that I put into the upper portion of the HMA while in real mode, which is now mapped to somewhere like 0xC000BF6A. Assuming that the code for storing it there works correctly in the first place.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
-
- Member
- Posts: 5563
- Joined: Mon Mar 25, 2013 7:01 pm
Re: triple-fault on jumping to kernel
Personally, I would want some basic memory management to dynamically allocate the IDT. That might go beyond your goals of a simple kernel, though. (And it might be harder to debug since you can't set up exception handlers without an IDT.)Schol-R-LEA wrote:I'm not entirely sure where to go from here; I would say I'll probably need to address the IDT and ISRs next, but I am not sure if there are any other things I need to address first.
I would also want to set up a new GDT, just to get away from depending on the bootloader's GDT. That might be important later if you decide your kernel needs to use different selectors for its code and data segments.
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: triple-fault on jumping to kernel
Oddly enough, memory management - or more specifically, recovering the memory map - is in fact what I've been working on, though I've run into a serious problem with that, in that it isn't getting saved to the memory locations I intended to put the memory map into. I really can't do memory management until I know what is valid system memory. Right now, I just need to figure out how to successfully pass that and some other data structures to the kernel.Octocontrabass wrote:Personally, I would want some basic memory management to dynamically allocate the IDT. That might go beyond your goals of a simple kernel, though. (And it might be harder to debug since you can't set up exception handlers without an IDT.)Schol-R-LEA wrote:I'm not entirely sure where to go from here; I would say I'll probably need to address the IDT and ISRs next, but I am not sure if there are any other things I need to address first.
I do intend to do that, yes, but it isn't a priority at the moment.Octocontrabass wrote:I would also want to set up a new GDT, just to get away from depending on the bootloader's GDT. That might be important later if you decide your kernel needs to use different selectors for its code and data segments.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.