1/* 2 * core/fs/pxe/isr.c 3 * 4 * Stub invoked on return from real mode including from an interrupt. 5 * Interrupts are locked out on entry. 6 */ 7 8#include "core.h" 9#include "thread.h" 10#include "pxe.h" 11#include <string.h> 12#include <sys/cpu.h> 13#include <sys/io.h> 14 15extern uint8_t pxe_irq_pending; 16extern volatile uint8_t pxe_need_poll; 17static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0); 18static DECLARE_INIT_SEMAPHORE(pxe_poll_thread_sem, 0); 19static struct thread *pxe_thread, *poll_thread; 20 21#ifndef PXE_POLL_FORCE 22# define PXE_POLL_FORCE 0 23#endif 24 25#ifndef PXE_POLL_BY_MODEL 26# define PXE_POLL_BY_MODEL 1 27#endif 28 29/* 30 * Note: this *must* be called with interrupts enabled. 31 */ 32static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) 33{ 34 far_ptr_t *entry; 35 unsigned int vec; 36 uint8_t mask, mymask; 37 uint32_t now; 38 bool ok; 39 40 if (irq < 8) 41 vec = irq + 0x08; 42 else if (irq < 16) 43 vec = (irq - 8) + 0x70; 44 else 45 return false; 46 47 cli(); 48 49 if (pxe_need_poll) { 50 sti(); 51 return false; 52 } 53 54 entry = (far_ptr_t *)(vec << 2); 55 *old = *entry; 56 entry->ptr = (uint32_t)isr; 57 58 /* Enable this interrupt at the PIC level, just in case... */ 59 mymask = ~(1 << (irq & 7)); 60 if (irq >= 8) { 61 mask = inb(0x21); 62 mask &= ~(1 << 2); /* Enable cascade */ 63 outb(mask, 0x21); 64 mask = inb(0xa1); 65 mask &= mymask; 66 outb(mask, 0xa1); 67 } else { 68 mask = inb(0x21); 69 mask &= mymask; 70 outb(mask, 0x21); 71 } 72 73 sti(); 74 75 now = jiffies(); 76 77 /* Some time to watch for stuck interrupts */ 78 while (jiffies() - now < 4 && (ok = !pxe_need_poll)) 79 hlt(); 80 81 if (!ok) 82 *entry = *old; /* Restore the old vector */ 83 84 ddprintf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec, 85 old->seg, old->offs, entry->seg, entry->offs); 86 87 return ok; 88} 89 90static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old) 91{ 92 far_ptr_t *entry; 93 unsigned int vec; 94 bool rv; 95 96 if (!irq) 97 return true; /* Nothing to uninstall */ 98 99 if (irq < 8) 100 vec = irq + 0x08; 101 else if (irq < 16) 102 vec = (irq - 8) + 0x70; 103 else 104 return false; 105 106 cli(); 107 108 entry = (far_ptr_t *)(vec << 2); 109 110 if (entry->ptr != (uint32_t)isr) { 111 rv = false; 112 } else { 113 *entry = *old; 114 rv = true; 115 } 116 117 sti(); 118 return rv; 119} 120 121static void pxe_poll_wakeups(void) 122{ 123 static jiffies_t last_jiffies = 0; 124 jiffies_t now = jiffies(); 125 126 if (pxe_need_poll == 1) { 127 /* If we need polling now, activate polling */ 128 pxe_need_poll = 3; 129 sem_up(&pxe_poll_thread_sem); 130 } 131 132 if (now != last_jiffies) { 133 last_jiffies = now; 134 __thread_process_timeouts(); 135 } 136 137 if (pxe_irq_pending) { 138 pxe_irq_pending = 0; 139 sem_up(&pxe_receive_thread_sem); 140 } 141} 142 143static void pxe_process_irq(void) 144{ 145 static __lowmem t_PXENV_UNDI_ISR isr; 146 147 uint16_t func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */ 148 bool done = false; 149 150 while (!done) { 151 memset(&isr, 0, sizeof isr); 152 isr.FuncFlag = func; 153 func = PXENV_UNDI_ISR_IN_GET_NEXT; /* Next time */ 154 155 pxe_call(PXENV_UNDI_ISR, &isr); 156 157 switch (isr.FuncFlag) { 158 case PXENV_UNDI_ISR_OUT_DONE: 159 done = true; 160 break; 161 162 case PXENV_UNDI_ISR_OUT_TRANSMIT: 163 /* Transmit complete - nothing for us to do */ 164 break; 165 166 case PXENV_UNDI_ISR_OUT_RECEIVE: 167 undiif_input(&isr); 168 break; 169 170 case PXENV_UNDI_ISR_OUT_BUSY: 171 /* ISR busy, this should not happen */ 172 done = true; 173 break; 174 175 default: 176 /* Invalid return code, this should not happen */ 177 done = true; 178 break; 179 } 180 } 181} 182 183static void pxe_receive_thread(void *dummy) 184{ 185 (void)dummy; 186 187 for (;;) { 188 sem_down(&pxe_receive_thread_sem, 0); 189 pxe_process_irq(); 190 } 191} 192 193static bool pxe_isr_poll(void) 194{ 195 static __lowmem t_PXENV_UNDI_ISR isr; 196 197 isr.FuncFlag = PXENV_UNDI_ISR_IN_START; 198 pxe_call(PXENV_UNDI_ISR, &isr); 199 200 return isr.FuncFlag == PXENV_UNDI_ISR_OUT_OURS; 201} 202 203static void pxe_poll_thread(void *dummy) 204{ 205 (void)dummy; 206 207 /* Block indefinitely unless activated */ 208 sem_down(&pxe_poll_thread_sem, 0); 209 210 for (;;) { 211 cli(); 212 if (pxe_receive_thread_sem.count < 0 && pxe_isr_poll()) 213 sem_up(&pxe_receive_thread_sem); 214 else 215 __schedule(); 216 sti(); 217 cpu_relax(); 218 } 219} 220 221/* 222 * This does preparations and enables the PXE thread 223 */ 224void pxe_init_isr(void) 225{ 226 start_idle_thread(); 227 sched_hook_func = pxe_poll_wakeups; 228 /* 229 * Run the pxe receive thread at elevated priority, since the UNDI 230 * stack is likely to have very limited memory available; therefore to 231 * avoid packet loss we need to move it into memory that we ourselves 232 * manage, as soon as possible. 233 */ 234 core_pm_hook = __schedule; 235 236 pxe_thread = start_thread("pxe receive", 16384, -20, 237 pxe_receive_thread, NULL); 238} 239 240/* 241 * Actually start the interrupt routine inside the UNDI stack 242 */ 243void pxe_start_isr(void) 244{ 245 int irq = pxe_undi_info.IntNumber; 246 247 if (irq == 2) 248 irq = 9; /* IRQ 2 is really IRQ 9 */ 249 else if (irq > 15) 250 irq = 0; /* Invalid IRQ */ 251 252 pxe_irq_vector = irq; 253 254 if (irq) { 255 if (!install_irq_vector(irq, pxe_isr, &pxe_irq_chain)) 256 irq = 0; /* Install failed or stuck interrupt */ 257 } 258 259 poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY, 260 pxe_poll_thread, NULL); 261 262 if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) { 263 asm volatile("orb $1,%0" : "+m" (pxe_need_poll)); 264 dprintf("pxe_start_isr: forcing pxe_need_poll\n"); 265 } else if (PXE_POLL_BY_MODEL) { 266 dprintf("pxe_start_isr: trying poll by model\n"); 267 int hwad = ((int)MAC[0] << 16) + ((int)MAC[1] << 8) + MAC[2]; 268 dprintf("pxe_start_isr: got %06x %04x\n", hwad, pxe_undi_iface.ServiceFlags); 269 if ((hwad == 0x000023ae) && (pxe_undi_iface.ServiceFlags == 0xdc1b) || 270 (hwad == 0x005c260a) && (pxe_undi_iface.ServiceFlags == 0xdc1b) || 271 (hwad == 0x00180373) && (pxe_undi_iface.ServiceFlags == 0xdc1b)) { 272 asm volatile("orb $1,%0" : "+m" (pxe_need_poll)); 273 dprintf("pxe_start_isr: forcing pxe_need_poll by model\n"); 274 } 275 } 276} 277 278int reset_pxe(void) 279{ 280 static __lowmem struct s_PXENV_UNDI_CLOSE undi_close; 281 282 sched_hook_func = NULL; 283 core_pm_hook = core_pm_null_hook; 284 kill_thread(pxe_thread); 285 286 memset(&undi_close, 0, sizeof(undi_close)); 287 pxe_call(PXENV_UNDI_CLOSE, &undi_close); 288 289 if (undi_close.Status) 290 printf("PXENV_UNDI_CLOSE failed: 0x%x\n", undi_close.Status); 291 292 if (pxe_irq_vector) 293 uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); 294 if (poll_thread) 295 kill_thread(poll_thread); 296 297 return undi_close.Status; 298} 299