From: Kevin O'Connor Date: Sat, 24 Oct 2009 21:57:29 +0000 (-0400) Subject: Add simple cooperative threading scheme to allow parallel hw init. X-Git-Tag: rel-0.5.0~49 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=a5826b5ad482f44d293387dc7513e5e98802a54e;p=seabios.git Add simple cooperative threading scheme to allow parallel hw init. Enable system for running hardware initialization in parallel. The yield() call can now round-robin between "threads". Rework ata controller init to use a thread per controller. Make sure internal drives are registered in a defined order. Run keyboard initialization in a thread. Rework usb init to use a thread per controller. --- diff --git a/src/ata.c b/src/ata.c index 2401557..e30f186 100644 --- a/src/ata.c +++ b/src/ata.c @@ -636,8 +636,10 @@ init_drive_ata(struct drive_s *dummy, u16 *buffer) return drive_g; } +static u64 SpinupEnd; + static int -powerup_await_non_bsy(u16 base, u64 end) +powerup_await_non_bsy(u16 base) { u8 orstatus = 0; u8 status; @@ -650,7 +652,7 @@ powerup_await_non_bsy(u16 base, u64 end) dprintf(1, "powerup IDE floating\n"); return orstatus; } - if (check_time(end)) { + if (check_time(SpinupEnd)) { dprintf(1, "powerup IDE time out\n"); return -1; } @@ -661,14 +663,15 @@ powerup_await_non_bsy(u16 base, u64 end) } static void -ata_detect() +ata_detect(void *data) { + struct ata_channel_s *atachannel = data; + int startid = (atachannel - ATA_channels) * 2; struct drive_s dummy; memset(&dummy, 0, sizeof(dummy)); // Device detection - u64 end = calc_future_tsc(IDE_TIMEOUT); int ataid, last_reset_ataid=-1; - for (ataid=0; ataidtype != BCV_TYPE_INTERNAL) + break; + struct drive_s *prevdrive = (void*)prev->vector; + if (prevdrive->type < drive_g->type + || (prevdrive->type == drive_g->type + && prevdrive->cntl_id < drive_g->cntl_id)) + break; + ie--; + } + if (ie != end) + memmove(ie+1, ie, (void*)end-(void*)ie); + } ie->type = BCV_TYPE_INTERNAL; ie->vector = (u32)drive_g; ie->description = ""; @@ -297,6 +314,8 @@ boot_prep() if (! CONFIG_BOOT) return; + // XXX - show available drives? + // Allow user to modify BCV/IPL order. interactive_bootmenu(); diff --git a/src/config.h b/src/config.h index 7b472ad..ab243de 100644 --- a/src/config.h +++ b/src/config.h @@ -22,6 +22,8 @@ // Screen writes are also sent to debug ports. #define CONFIG_SCREEN_AND_DEBUG 1 +// Support running hardware initialization in parallel +#define CONFIG_THREADS 1 // Support int13 disk/floppy drive functions #define CONFIG_DRIVES 1 // Support floppy drive access diff --git a/src/kbd.c b/src/kbd.c index 29eb29a..461d1b7 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -118,7 +118,7 @@ kbd_setup() if (! CONFIG_KEYBOARD) return; - keyboard_init(); + run_thread(keyboard_init, NULL); enable_hwirq(1, entry_09); } diff --git a/src/post.c b/src/post.c index a566b34..3024eb8 100644 --- a/src/post.c +++ b/src/post.c @@ -198,6 +198,7 @@ post() ata_setup(); ramdisk_setup(); + wait_threads(); optionrom_setup(); // Run BCVs diff --git a/src/usb-hid.c b/src/usb-hid.c index c849aa1..8cb501a 100644 --- a/src/usb-hid.c +++ b/src/usb-hid.c @@ -47,6 +47,7 @@ usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax) if (! CONFIG_USB_KEYBOARD) return -1; if (keyboard_pipe) + // XXX - this enables the first found keyboard (could be random) return -1; dprintf(2, "usb_keyboard_setup %x\n", endp); diff --git a/src/usb-ohci.c b/src/usb-ohci.c index 9e07c89..15841dc 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -121,11 +121,12 @@ shutdown: return 0; } -int -ohci_init(struct usb_s *cntl) +void +ohci_init(void *data) { if (! CONFIG_USB_OHCI) - return 0; + return; + struct usb_s *cntl = data; cntl->type = USB_TYPE_OHCI; u32 baseaddr = pci_config_readl(cntl->bdf, PCI_BASE_ADDRESS_0); @@ -150,7 +151,7 @@ ohci_init(struct usb_s *cntl) struct ohci_ed *control_ed = malloc_high(sizeof(*control_ed)); if (!hcca || !control_ed) { dprintf(1, "No ram for ohci init\n"); - return 0; + return; } memset(hcca, 0, sizeof(*hcca)); memset(control_ed, 0, sizeof(*control_ed)); @@ -164,13 +165,12 @@ ohci_init(struct usb_s *cntl) int count = check_ohci_ports(cntl); if (! count) goto err; - return count; + return; err: stop_ohci(cntl); free(hcca); free(control_ed); - return 0; } static int diff --git a/src/usb-ohci.h b/src/usb-ohci.h index e5d2127..5a4f735 100644 --- a/src/usb-ohci.h +++ b/src/usb-ohci.h @@ -3,7 +3,7 @@ // usb-ohci.c struct usb_s; -int ohci_init(struct usb_s *cntl); +void ohci_init(void *data); int ohci_control(u32 endp, int dir, const void *cmd, int cmdsize , void *data, int datasize); struct usb_pipe *ohci_alloc_intr_pipe(u32 endp, int period); diff --git a/src/usb-uhci.c b/src/usb-uhci.c index 8045375..f9fa726 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -123,11 +123,12 @@ check_ports(struct usb_s *cntl) return totalcount; } -int -uhci_init(struct usb_s *cntl) +void +uhci_init(void *data) { if (! CONFIG_USB_UHCI) - return 0; + return; + struct usb_s *cntl = data; cntl->type = USB_TYPE_UHCI; cntl->uhci.iobase = (pci_config_readl(cntl->bdf, PCI_BASE_ADDRESS_4) @@ -146,10 +147,7 @@ uhci_init(struct usb_s *cntl) int count = check_ports(cntl); if (! count) { // XXX - no devices; free data structures. - return 0; } - - return count; } static int diff --git a/src/usb-uhci.h b/src/usb-uhci.h index b284fcc..5de7da0 100644 --- a/src/usb-uhci.h +++ b/src/usb-uhci.h @@ -5,7 +5,7 @@ // usb-uhci.c struct usb_s; -int uhci_init(struct usb_s *cntl); +void uhci_init(void *data); int uhci_control(u32 endp, int dir, const void *cmd, int cmdsize , void *data, int datasize); struct usb_pipe *uhci_alloc_intr_pipe(u32 endp, int period); diff --git a/src/usb.c b/src/usb.c index cb75e78..fcc404b 100644 --- a/src/usb.c +++ b/src/usb.c @@ -216,17 +216,15 @@ usb_setup() struct usb_s *cntl = &USBControllers[count]; cntl->bdf = bdf; - int devcount = 0; if (code == PCI_CLASS_SERIAL_USB_UHCI) - devcount = uhci_init(cntl); + run_thread(uhci_init, cntl); else if (code == PCI_CLASS_SERIAL_USB_OHCI) - devcount = ohci_init(cntl); - - if (devcount > 0) { - // Success - count++; - if (count >= ARRAY_SIZE(USBControllers)) - break; - } + run_thread(ohci_init, cntl); + else + continue; + + count++; + if (count >= ARRAY_SIZE(USBControllers)) + break; } } diff --git a/src/util.c b/src/util.c index 4086948..21c7a62 100644 --- a/src/util.c +++ b/src/util.c @@ -9,6 +9,12 @@ #include "farptr.h" // GET_FLATPTR #include "biosvar.h" // get_ebda_seg +static inline u32 getesp() { + u32 esp; + asm("movl %%esp, %0" : "=rm"(esp)); + return esp; +} + /**************************************************************** * 16bit calls @@ -19,6 +25,8 @@ inline void call16(struct bregs *callregs) { + if (!MODE16 && getesp() > BUILD_STACK_ADDR) + panic("call16 with invalid stack\n"); asm volatile( #if MODE16 == 1 "calll __call16\n" @@ -36,6 +44,8 @@ inline void call16big(struct bregs *callregs) { ASSERT32(); + if (getesp() > BUILD_STACK_ADDR) + panic("call16 with invalid stack\n"); asm volatile( "calll __call16big_from32" : "+a" (callregs), "+m" (*callregs) @@ -83,32 +93,153 @@ stack_hop(u32 eax, u32 edx, u32 ecx, void *func) // 16bit trampoline for enabling irqs from 32bit mode. ASM16( - " .global trampoline_yield\n" - "trampoline_yield:\n" + " .global trampoline_checkirqs\n" + "trampoline_checkirqs:\n" " rep ; nop\n" " lretw" ); +static void +check_irqs32() +{ + extern void trampoline_checkirqs(); + struct bregs br; + br.flags = F_IF; + br.code.seg = SEG_BIOS; + br.code.offset = (u32)&trampoline_checkirqs; + call16big(&br); +} + +static void +check_irqs16() +{ + asm volatile( + "sti\n" + "nop\n" + "rep ; nop\n" + "cli\n" + "cld\n" + : : :"memory"); +} + + +/**************************************************************** + * Threads + ****************************************************************/ + +#define THREADSTACKSIZE 4096 + +struct thread_info { + struct thread_info *next; + void *stackpos; +}; + +static struct thread_info MainThread = {&MainThread, NULL}; + +static struct thread_info * +getCurThread() +{ + u32 esp = getesp(); + if (esp <= BUILD_STACK_ADDR) + return &MainThread; + return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE); +} + // Briefly permit irqs to occur. void yield() { if (MODE16) { - asm volatile( - "sti\n" - "nop\n" - "rep ; nop\n" - "cli\n" - "cld\n" - : : :"memory"); + // In 16bit mode, just directly check irqs. + check_irqs16(); return; } - extern void trampoline_yield(); - struct bregs br; - br.flags = F_IF; - br.code.seg = SEG_BIOS; - br.code.offset = (u32)&trampoline_yield; - call16big(&br); + if (! CONFIG_THREADS) { + check_irqs32(); + return; + } + struct thread_info *cur = getCurThread(); + if (cur == &MainThread) + // Permit irqs to fire + check_irqs32(); + + // 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"); +} + +// Last thing called from a thread (called on "next" stack). +static void +__end_thread(struct thread_info *old) +{ + struct thread_info *pos = &MainThread; + while (pos->next != old) + pos = pos->next; + pos->next = old->next; + free(old); + dprintf(2, "=========== end thread %p\n", old); +} + +void +run_thread(void (*func)(void*), void *data) +{ + ASSERT32(); + if (! CONFIG_THREADS) + goto fail; + struct thread_info *thread; + thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE); + if (!thread) + goto fail; + + thread->stackpos = (void*)thread + THREADSTACKSIZE; + struct thread_info *cur = getCurThread(); + thread->next = cur->next; + cur->next = thread; + + dprintf(2, "=========== start thread %p\n", thread); + asm volatile( + // Start thread + " pushl $1f\n" // store return pc + " pushl %%ebp\n" // backup %ebp + " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp + " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos + " calll *%%ecx\n" // Call func + + // End thread + " movl (%%ebx), %%ecx\n" // %ecx = thread->next + " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos + " movl %%ebx, %%eax\n" + " calll %4\n" // call __end_thread(thread) + " popl %%ebp\n" // restore %ebp + " retl\n" // restore pc + "1:\n" + : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur) + : "m"(*(u8*)__end_thread) + : "esi", "edi", "cc", "memory"); + return; + +fail: + func(data); +} + +void +wait_threads() +{ + ASSERT32(); + if (! CONFIG_THREADS) + return; + while (MainThread.next != &MainThread) + yield(); } diff --git a/src/util.h b/src/util.h index 01f46c0..c2214c9 100644 --- a/src/util.h +++ b/src/util.h @@ -139,6 +139,8 @@ static inline u8 readb(const void *addr) { // util.c inline u32 stack_hop(u32 eax, u32 edx, u32 ecx, void *func); +void run_thread(void (*func)(void*), void *data); +void wait_threads(); u8 checksum_far(u16 buf_seg, void *buf_far, u32 len); u8 checksum(void *buf, u32 len); int memcmp(const void *s1, const void *s2, size_t n);