Page 1 of 3

Fixing PIT and Utilizing CPUID Features And Flags

Posted: Thu Jan 07, 2021 2:27 pm
by TheGameMaker90
Hello, I'm new to these forums and, for the most part, I'm trying to do this by myself. However, I just want to make sure I'm doing this correctly. So in my OS, I have a problem and a question.

For the problem, I have done much research on the PIT. It seems like everywhere it basically explains things the same so I mainly stuck by the "James Molloy" tutorials. I pretty much have everything setup the same as the tutorial (minus other modules I omitted for testing) and it just says "Ticks: 1" and stops.
Here's a bit of code:

pit.h:

Code: Select all

#ifndef SYSTEM_TIMER
#define SYSTEM_TIMER

#include <stdint.h>

#define PIT_BASE_FREQ       1193180

#define PIT_CHANNEL_0       0x40
#define PIT_CHANNEL_1       0x41
#define PIT_CHANNEL_2       0x42
#define PIT_CMD_PORT        0x43

// Technically these "__cplusplus" aren't needed here so I'll be removing them.
#if defined(__cplusplus)
extern "C" {
#endif

void pit_init(uint32_t fq);

#if defined(__cplusplus)
}
#endif

#endif	// !SYSTEM_TIMER

pit.c:

Code: Select all

#include <kernel/pit.h>
#include <kernel/isr.h>
#include <kernel/system/terminal.h>
#include <kernel/system/io.h>

uint32_t tick;

static void
timer_callback(registers_t regs)
{
	tick++;
	terminal_printf("Ticks: %d\n", tick);
}

void
pit_init(uint32_t freq)
{
	//terminal_printf("[INFO]: PIT is initialized!\n");

	register_interrupt_handler(IRQ0, &timer_callback);
	uint32_t divisor = 1193180 / freq;
	outb(0x43, 0x36);

	uint8_t l = (uint8_t)(divisor & 0xFF);
	uint8_t h = (uint8_t)((divisor >> 8) & 0xFF);

	outb(0x40, l);
	outb(0x40, h);
}
interrupt.asm:

Code: Select all

%macro ISR_NOERRCODE 1
  global isr%1
  isr%1:
    cli
    push byte 0                 ; Push a dummy error code.
    push byte %1                     ; Push the interrupt number.
    jmp isr_common_stub         ; Go to our common handler code.
%endmacro

; This macro creates a stub for an ISR which passes it's own
; error code.
%macro ISR_ERRCODE 1
  global isr%1
  isr%1:
    cli
    push byte %1                     ; Push the interrupt number
    jmp isr_common_stub
%endmacro

%macro IRQ 2
  global irq%1
  irq%1:
    cli
	  push byte 0
	  push byte %2
	  jmp irq_common_stub
%endmacro

ISR_NOERRCODE   0
ISR_NOERRCODE 	1
ISR_NOERRCODE 	2
ISR_NOERRCODE	  3
ISR_NOERRCODE	  4
ISR_NOERRCODE	  5
ISR_NOERRCODE	  6
ISR_NOERRCODE	  7
ISR_ERRCODE   	8
ISR_NOERRCODE	  9
ISR_ERRCODE   	10
ISR_ERRCODE   	11
ISR_ERRCODE   	12
ISR_ERRCODE   	13
ISR_ERRCODE   	14
ISR_NOERRCODE	  15
ISR_NOERRCODE	  16
ISR_NOERRCODE	  17
ISR_NOERRCODE	  18
ISR_NOERRCODE	  19
ISR_NOERRCODE	  20
ISR_NOERRCODE	  21
ISR_NOERRCODE	  22
ISR_NOERRCODE	  23
ISR_NOERRCODE	  24
ISR_NOERRCODE	  25
ISR_NOERRCODE	  26
ISR_NOERRCODE	  27
ISR_NOERRCODE	  28
ISR_NOERRCODE   29
ISR_NOERRCODE	  30
ISR_NOERRCODE	  31
IRQ	0, 32
IRQ	1, 33
IRQ	2, 34
IRQ	3, 35
IRQ	4, 36
IRQ	5, 37
IRQ	6, 38
IRQ	7, 39
IRQ	8, 40
IRQ	9, 41
IRQ	10, 42
IRQ	11, 43
IRQ	12, 44
IRQ	13, 45
IRQ	14, 46
IRQ	15, 47

; This is our common ISR stub. It saves the processor state, sets
; up for kernel mode segments, calls the C-level fault handler,
; and finally restores the stack frame.
extern isr_handler              ; In isr.c

isr_common_stub:
    pusha                       ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax

    mov ax, ds                  ; Lower 16-bits of eax = ds.
    push eax                    ; save the data segment descriptor

    mov ax, 0x10                ; load the kernel data segment descriptor
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    call isr_handler

	  pop ebx
	  mov ds, bx
	  mov es, bx
	  mov fs, bx
	  mov gs, bx

    popa                        ; Pops edi,esi,ebp...
    add esp, 8                  ; Cleans up the pushed error code and pushed ISR number
    sti
    iret

extern irq_handler

irq_common_stub:
	  pusha
	
	  mov ax, ds
	  push eax
	
	  mov ax, 0x10
	  mov ds, ax
	  mov es, ax
	  mov fs, ax
	  mov gs, ax
	
	  call irq_handler
	
	  pop ebx
	  mov ds, bx
	  mov es, bx
	  mov fs, bx
	  mov gs, bx
	
	  popa
	  add esp, 8
    sti
	  iret
And yes, I've tried editing the files based on https://wiki.osdev.org/James_Molloy%27s ... Known_Bugs to no avail. Perhaps it's QEMU?

Re: Utilizing CPUID Features And Flags

Posted: Thu Jan 07, 2021 8:28 pm
by Octocontrabass
TheGameMaker90 wrote:I pretty much have everything setup the same as the tutorial (minus other modules I omitted for testing) and it just says "Ticks: 1" and stops.
What code is running that you expect to be interrupted by your timer interrupt?

What does any of this have to do with CPUID?

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 11:28 am
by TheGameMaker90
Oh yeah, the title, huh? Forgot about that, I was originally going to ask about that, until I realized I had an assembly function called "cpuid_get_property." Not "cpuid_get_feature." Not sure why my brain read it wrong but oh well. And yes, the timer is supposed to spam "Ticks: n++" in the terminal, but it gets to 1 and stops.
I'll also change the title...

Edit:
Or not, these forums are cryptic. It took a while for this post to even show up. I didn't know it was active until I just replied. Also there doesn't seem to be an "edit title" button anywhere. Anyway, follow this link to show how its supposed to work: http://jamesmolloy.co.uk/tutorial_html/ ... 20PIT.html.
It did work when I first implemented it, but now it's not. Also in my main I figured out (previously) that "asm volatile("sti");" is required before calling my pit_init function.

Edit 2:
Ok so here's a screenshot, and it makes no difference if I have the "cpu" code beforehand because that's a new addition. Also technically, I'm in a virtual machine in a virtual machine. I have to wonder if that's my issue. I'll try it on actual hardware and get back to here. (Running QEMU within Linux Mint VirtualBox).

Edit 3:
No luck at all, just stays at 1 on bare metal. Interrupts 0 through 31 work fine. It's just not working with the intended behaviour and I need the PIT to work so that I know IRQ's are working. I also need IRQ1 for the keyboard driver.

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 2:37 pm
by TheGameMaker90
Anybody? I mean that's what forums are for right? To help each other out? 70something views and two replies. Hmm.

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 3:13 pm
by nexos
Have you sent EOI to the PIC?

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 3:31 pm
by Octocontrabass
TheGameMaker90 wrote:It took a while for this post to even show up.
New users (like you!) have to be approved by a moderator before any posts will be visible. We get a lot of spambots and don't have moderators available 24/7 to clean up after them.
TheGameMaker90 wrote:Also there doesn't seem to be an "edit title" button anywhere.
I believe you can change the title by editing your first post.
TheGameMaker90 wrote:Anybody? I mean that's what forums are for right? To help each other out? 70something views and two replies. Hmm.
This isn't exactly the most active forum on the internet. Sometimes you'll have to wait for a reply.

What code is running that you expect to be interrupted by your timer interrupt? I don't think I saw that code in your post anywhere.

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 5:09 pm
by TheGameMaker90
nexos wrote:Have you sent EOI to the PIC?
Yes i have in fact. It's placed in the ldt just before the isr's and irq's idt_set_gate functions.

And that makes sense, and now i know (on the posts).
For the record, i did it exactly as it's shown in jm tutorials.

My hope is to get something running for now and with a little more research, rewrite it to make it my own. As such I've done tutorial 4, 5, and 6 (gdt & idt, irqs & the pit, and paging respectively). I have a franken-heap allocator (research, and examples) and the rest is mostly my own/snippets on osdev.

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 5:39 pm
by Ethin
You need to send the EOI in the interrupt routine, not when your loading the LDT/IDT. So you'd send the EOI right after incrementing the tick counter.
Also, just a side note: you may wish to turn that tick counter into an atomic integer, especially once you get to SMP. Better to do it earlier than later though. (That's my Rust background speaking, but its generally good to use mutex/locks/atomics on static mutable variables if possible. You can wait though if you don't know how.)

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 5:42 pm
by nexos
Ethin wrote:You need to send the EOI in the interrupt routine, not when your loading the LDT/IDT. So you'd send the EOI right after incrementing the tick counter.
Also, just a side note: you may wish to turn that tick counter into an atomic integer, especially once you get to SMP. Better to do it earlier than later though. (That's my Rust background speaking, but its generally good to use mutex/locks/atomics on static mutable variables if possible. You can wait though if you don't know how.)
Using a spinlock on a uniproccesser is one of the worst performace decisions out there. For example, T1 acquires a spinlock and gets preempted by T2 while that holding spinlock. T2 then acquires that same spinlock and sitsin its whole timeslice looping. For the timer counter, it would just be better to have a per CPU timer. I think we just got waay off topic.

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 6:43 pm
by TheGameMaker90
Ethin wrote:You need to send the EOI in the interrupt routine, not when your loading the LDT/IDT. So you'd send the EOI right after incrementing the tick counter.
Also, just a side note: you may wish to turn that tick counter into an atomic integer, especially once you get to SMP. Better to do it earlier than later though. (That's my Rust background speaking, but its generally good to use mutex/locks/atomics on static mutable variables if possible. You can wait though if you don't know how.)
Ok so in other words do something like this:

Code: Select all

uint32_t tick;

static void
timer_callback(registers_t regs)
{
	tick++;
	terminal_printf("Ticks: %d\n", tick);
}

void
pit_init(uint32_t freq)
{
	//terminal_printf("[INFO]: PIT is initialized!\n");

	register_interrupt_handler(IRQ0, &timer_callback);
	uint32_t divisor = 1193180 / freq;
	outb(0x43, 0x36);

	uint8_t l = (uint8_t)(divisor & 0xFF);
	uint8_t h = (uint8_t)((divisor >> 8) & 0xFF);

	outb(0x40, l);
	outb(0x40, h);

        pic_send_eoi();     // outb(0x20, 0x20);
}
Or perhaps even before the 8 bit ints?

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 7:00 pm
by Octocontrabass
TheGameMaker90 wrote:Ok so in other words do something like this:
No, you're supposed to send the EOI in the interrupt handler, not in the setup code. (The tutorial you're following includes the EOI as part of the irq_handler() function, which is fine.)

You never did say what code the CPU is running when it's supposed to be interrupted by the timer.

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 7:23 pm
by TheGameMaker90
Octocontrabass wrote:
TheGameMaker90 wrote:Ok so in other words do something like this:
No, you're supposed to send the EOI in the interrupt handler, not in the setup code. (The tutorial you're following includes the EOI as part of the irq_handler() function, which is fine.)

You never did say what code the CPU is running when it's supposed to be interrupted by the timer.
Ohhh! The interrupt handler, i did that.
In isr.c (irq_handler code snippet):

Code: Select all

void 
irq_handler(registers_t regs) 
{
	if (regs.int_no >= 40) 
	{
		outb(0xA0, 0x20);
	}
	outb(0x20, 0x20);	/* EOI */
	
	if (interrupt_handlers[regs.int_no] != 0) 
	{
		isr_t handler = interrupt_handlers[regs.int_no];
		handler(regs);
	}
}
Now you can probably see why I've taken to the forums...
Also, i made a mistake in my last post. In the idt i did pic remap not send eoi.

Edit: so i was looking at this article: https://wiki.osdev.org/Programmable_Interval_Timer and it seems as though my pit is in "one shot mode" under the section "frequency dividers" although that doesnt make sense since i basically did everything the way james molloy said. If the pit is operating in one shot mode, how can i escape it?

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 7:31 pm
by Octocontrabass
Now that that's been cleared up, could you perhaps post the code that the CPU is running when timer interrupts should be arriving?

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 7:47 pm
by TheGameMaker90
Octocontrabass wrote:Now that that's been cleared up, could you perhaps post the code that the CPU is running when timer interrupts should be arriving?
Not sure i understand the question, ive about posted all of the code for the interrupt timer here. The intended behaviour to ensure the pit is working properly is that with my current setup it should print Ticks: 1-infinity (or until it's stopped). Where should that be, the interrupt destination? Also perhaps an idea is in my edit?

Re: Utilizing CPUID Features And Flags

Posted: Fri Jan 08, 2021 8:04 pm
by Octocontrabass
TheGameMaker90 wrote:

Code: Select all

	outb(0x43, 0x36);
That doesn't look like one-shot mode to me.
TheGameMaker90 wrote:Not sure i understand the question,
Okay, let's go back a bit. Why are interrupts called interrupts?