OUT=out/
# Source files
-SRCBOTH=misc.c pmm.c output.c util.c stacks.c block.c floppy.c ata.c mouse.c \
+SRCBOTH=misc.c pmm.c stacks.c output.c util.c block.c floppy.c ata.c mouse.c \
kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \
pnpbios.c pirtable.c vgahooks.c ramdisk.c \
usb.c usb-uhci.c usb-ohci.c usb-hid.c paravirt.c
// 0x121 - Begin custom storage.
u8 ps2ctr;
+ int RTCusers;
// El Torito Emulation data
struct cdemu_s cdemu;
* Periodic timer
****************************************************************/
+void
+useRTC()
+{
+ u16 ebda_seg = get_ebda_seg();
+ int count = GET_EBDA2(ebda_seg, RTCusers);
+ SET_EBDA2(ebda_seg, RTCusers, count+1);
+ if (count)
+ return;
+ // Turn on the Periodic Interrupt timer
+ u8 bRegister = inb_cmos(CMOS_STATUS_B);
+ outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B);
+}
+
+void
+releaseRTC()
+{
+ u16 ebda_seg = get_ebda_seg();
+ int count = GET_EBDA2(ebda_seg, RTCusers);
+ SET_EBDA2(ebda_seg, RTCusers, count-1);
+ if (count != 1)
+ return;
+ // Clear the Periodic Interrupt.
+ u8 bRegister = inb_cmos(CMOS_STATUS_B);
+ outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B);
+}
+
static int
set_usertimer(u32 usecs, u16 seg, u16 offset)
{
SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte.
SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset));
SET_BDA(user_wait_timeout, usecs);
-
- // Turn on the Periodic Interrupt timer
- u8 bRegister = inb_cmos(CMOS_STATUS_B);
- outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B);
-
+ useRTC();
return 0;
}
static void
clear_usertimer()
{
+ if (!(GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING))
+ return;
// Turn off status byte.
SET_BDA(rtc_wait_flag, 0);
- // Clear the Periodic Interrupt.
- u8 bRegister = inb_cmos(CMOS_STATUS_B);
- outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B);
+ releaseRTC();
}
#define RET_ECLOCKINUSE 0x83
// Handle Periodic Interrupt.
+ check_preempt();
+
if (!GET_BDA(rtc_wait_flag))
goto done;
* GDT and IDT tables
****************************************************************/
-struct descloc_s {
- u16 length;
- u32 addr;
-} PACKED;
-
// Real mode IDT descriptor
struct descloc_s rmode_IDT_info VAR16VISIBLE = {
.length = sizeof(struct rmode_IVT) - 1,
br.es = SEG_BIOS;
br.di = get_pnp_offset();
br.code = SEGOFF(seg, offset);
+ start_preempt();
call16big(&br);
+ finish_preempt();
debug_serial_setup();
}
ramdisk_setup();
// Run option roms
- if (CONFIG_THREADS && CONFIG_THREAD_OPTIONROMS)
- // Run vga option rom (if running asynchronously)
+ if (CONFIG_THREADS && CONFIG_THREAD_OPTIONROMS) {
+ // Run option roms while hw init still in progress.
vga_setup();
- wait_threads();
- optionrom_setup();
+ optionrom_setup();
+ wait_threads();
+ } else {
+ // Wait for hw init to finish and run non-vga option roms.
+ wait_threads();
+ optionrom_setup();
+ }
// Run BCVs and show optional boot menu
boot_prep();
****************************************************************/
// Place CPU into 32bit mode from 16bit mode.
-// Clobbers: flags, segment registers, cr0, idt/gdt
+// Clobbers: ecx, flags, segment registers, cr0, idt/gdt
DECLFUNC transition32
transition32:
- pushl %eax
+ movl %eax, %ecx
// Disable irqs (and clear direction flag)
cli
movw %ax, %fs
movw %ax, %gs
- popl %eax
+ movl %ecx, %eax
retl
// Place CPU into 16bit mode from 32bit mode.
-// Clobbers: flags, segment registers, cr0, idt/gdt
+// Clobbers: ecx, flags, segment registers, cr0, idt/gdt
DECLFUNC transition16
.global transition16big
transition16:
- pushl %eax
+ movl %eax, %ecx
// restore data segment limits to 0xffff
movl $SEG32_MODE16_DS, %eax
ljmpw $SEG32_MODE16_CS, $1f
transition16big:
- pushl %eax
+ movl %eax, %ecx
movl $SEG32_MODE16BIG_DS, %eax
movw %ax, %ds
movw %ax, %ds
movw %ax, %ss // Assume stack is in segment 0
- popl %eax
+ movl %ecx, %eax
retl
// Call a 16bit function from 16bit mode with a specified cpu register state
#include "biosvar.h" // get_ebda_seg
#include "util.h" // dprintf
+#include "bregs.h" // CR0_PE
/****************************************************************
void *stackpos;
};
-struct thread_info MainThread;
+struct thread_info VAR16VISIBLE MainThread;
+int VAR16VISIBLE CanPreempt;
void
thread_setup()
{
MainThread.next = &MainThread;
MainThread.stackpos = NULL;
+ CanPreempt = 0;
}
+// Return the 'struct thread_info' for the currently running thread.
struct thread_info *
getCurThread()
{
return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
}
+// Switch to next thread stack.
+static void
+switch_next(struct thread_info *cur)
+{
+ struct thread_info *next = cur->next;
+ asm volatile(
+ " pushl $1f\n" // store return pc
+ " pushl %%ebp\n" // backup %ebp
+ " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
+ " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
+ " popl %%ebp\n" // restore %ebp
+ " retl\n" // restore pc
+ "1:\n"
+ : "+a"(cur), "+c"(next)
+ :
+ : "ebx", "edx", "esi", "edi", "cc", "memory");
+}
+
// Briefly permit irqs to occur.
void
yield()
check_irqs();
// Switch to the next thread
- struct thread_info *next = cur->next;
- asm volatile(
- " pushl $1f\n" // store return pc
- " pushl %%ebp\n" // backup %ebp
- " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
- " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
- " popl %%ebp\n" // restore %ebp
- " retl\n" // restore pc
- "1:\n"
- : "+a"(cur), "+c"(next)
- :
- : "ebx", "edx", "esi", "edi", "cc", "memory");
+ switch_next(cur);
}
// Last thing called from a thread (called on "next" stack).
dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
}
+// Create a new thread and start executing 'func' in it.
void
run_thread(void (*func)(void*), void *data)
{
func(data);
}
+// Wait for all threads (other than the main thread) to complete.
void
wait_threads()
{
while (MainThread.next != &MainThread)
yield();
}
+
+
+/****************************************************************
+ * Thread preemption
+ ****************************************************************/
+
+static u32 PreemptCount;
+
+// Turn on RTC irqs and arrange for them to check the 32bit threads.
+void
+start_preempt()
+{
+ if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
+ return;
+ CanPreempt = 1;
+ PreemptCount = 0;
+ useRTC();
+}
+
+// Turn off RTC irqs / stop checking for thread execution.
+void
+finish_preempt()
+{
+ if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
+ return;
+ CanPreempt = 0;
+ releaseRTC();
+ dprintf(1, "Done preempt - %d checks\n", PreemptCount);
+}
+
+static inline u32 getcr0() {
+ u32 cr0;
+ asm("movl %%cr0, %0" : "=r"(cr0));
+ return cr0;
+}
+static inline void sgdt(struct descloc_s *desc) {
+ asm("sgdtl %0" : "=m"(*desc));
+}
+static inline void lgdt(struct descloc_s *desc) {
+ asm("lgdtl %0" : : "m"(*desc) : "memory");
+}
+
+#if !MODE16
+// Try to execute 32bit threads.
+void VISIBLE32
+yield_preempt()
+{
+ PreemptCount++;
+ switch_next(&MainThread);
+}
+#endif
+
+// 16bit code that checks if threads are pending and executes them if so.
+void
+check_preempt()
+{
+ ASSERT16();
+ if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
+ || !GET_GLOBAL(CanPreempt)
+ || GET_GLOBAL(MainThread.next) == &MainThread)
+ return;
+ u32 cr0 = getcr0();
+ if (cr0 & CR0_PE)
+ // Called in 16bit protected mode?!
+ return;
+
+ // Backup cmos index register and disable nmi
+ u8 cmosindex = inb(PORT_CMOS_INDEX);
+ outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
+ inb(PORT_CMOS_DATA);
+
+ // Backup fs/gs and gdt
+ u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
+ struct descloc_s gdt;
+ sgdt(&gdt);
+
+ u32 bkup_ss, bkup_esp;
+ asm volatile(
+ // Backup ss/esp / set esp to flat stack location
+ " movl %%ss, %0\n"
+ " movl %%esp, %1\n"
+ " shll $4, %0\n"
+ " addl %0, %%esp\n"
+ " movl %%ss, %0\n"
+
+ // Transition to 32bit mode, call yield_preempt, return to 16bit
+ " pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
+ " jmp transition32\n"
+ " .code32\n"
+ "1:calll (yield_preempt - " __stringify(BUILD_BIOS_ADDR) ")\n"
+ " pushl $2f\n"
+ " jmp transition16big\n"
+ " .code16gcc\n"
+ "2:\n"
+
+ // Restore ds/ss/esp
+ " movl %0, %%ds\n"
+ " movl %0, %%ss\n"
+ " movl %1, %%esp\n"
+ : "=&r" (bkup_ss), "=&r" (bkup_esp)
+ : : "eax", "ecx", "edx", "cc", "memory");
+
+ // Restore gdt and fs/gs
+ lgdt(&gdt);
+ SET_SEG(FS, fs);
+ SET_SEG(GS, gs);
+
+ // Restore cmos index register
+ outb(cmosindex, PORT_CMOS_INDEX);
+ inb(PORT_CMOS_DATA);
+}
return;
struct usb_s *cntl = data;
+ // XXX - don't call pci_config_XXX from a thread
cntl->type = USB_TYPE_OHCI;
u32 baseaddr = pci_config_readl(cntl->bdf, PCI_BASE_ADDRESS_0);
cntl->ohci.regs = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK);
int ret = wait_ed(ed);
ed->hwINFO = ED_SKIP;
- usleep(1); // XXX - in case controller still accessing tds
+ if (ret)
+ usleep(1); // XXX - in case controller still accessing tds
free(tds);
return ret;
}
return;
struct usb_s *cntl = data;
+ // XXX - don't call pci_config_XXX from a thread
cntl->type = USB_TYPE_UHCI;
cntl->uhci.iobase = (pci_config_readl(cntl->bdf, PCI_BASE_ADDRESS_4)
& PCI_BASE_ADDRESS_IO_MASK);
struct uhci_qh *data_qh = cntl->uhci.qh;
data_qh->element = (u32)&tds[0];
int ret = wait_qh(cntl, data_qh);
- if (ret)
+ if (ret) {
+ data_qh->element = UHCI_PTR_TERM;
// XXX - leak tds
return ret;
- // XXX - free(tds);
+ }
+ free(tds);
return 0;
}
yield();
while (len) {
u32 copylen = len;
- if (copylen > 1024)
- copylen = 1024;
+ if (copylen > 2048)
+ copylen = 2048;
len -= copylen;
copylen /= 4;
asm volatile(
return *(volatile const u8 *)addr;
}
-// GDT bit manipulation
-#define GDT_BASE(v) ((((u64)(v) & 0xff000000) << 32) \
- | (((u64)(v) & 0x00ffffff) << 16))
-#define GDT_LIMIT(v) ((((u64)(v) & 0x000f0000) << 32) \
- | (((u64)(v) & 0x0000ffff) << 0))
-#define GDT_CODE (0x9bULL << 40) // Code segment - P,R,A bits also set
-#define GDT_DATA (0x93ULL << 40) // Data segment - W,A bits also set
-#define GDT_B (0x1ULL << 54) // Big flag
-#define GDT_G (0x1ULL << 55) // Granularity flag
-
#define call16_simpint(nr, peax, pflags) do { \
ASSERT16(); \
asm volatile( \
: "cc", "memory"); \
} while (0)
+// GDT bit manipulation
+#define GDT_BASE(v) ((((u64)(v) & 0xff000000) << 32) \
+ | (((u64)(v) & 0x00ffffff) << 16))
+#define GDT_LIMIT(v) ((((u64)(v) & 0x000f0000) << 32) \
+ | (((u64)(v) & 0x0000ffff) << 0))
+#define GDT_CODE (0x9bULL << 40) // Code segment - P,R,A bits also set
+#define GDT_DATA (0x93ULL << 40) // Data segment - W,A bits also set
+#define GDT_B (0x1ULL << 54) // Big flag
+#define GDT_G (0x1ULL << 55) // Granularity flag
+
+struct descloc_s {
+ u16 length;
+ u32 addr;
+} PACKED;
+
// util.c
struct bregs;
inline void call16(struct bregs *callregs);
void yield();
void run_thread(void (*func)(void*), void *data);
void wait_threads();
+void start_preempt();
+void finish_preempt();
+void check_preempt();
// output.c
void debug_serial_setup();
u64 calc_future_tsc_usec(u32 usecs);
void handle_1583(struct bregs *regs);
void handle_1586(struct bregs *regs);
+void useRTC();
+void releaseRTC();
// apm.c
void VISIBLE16 handle_1553(struct bregs *regs);