1------------------------------------------------------------------------------ 2-- -- 3-- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS -- 4-- -- 5-- S Y S T E M . I N T E R R U P T S -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 1992-2018, Free Software Foundation, Inc. -- 10-- -- 11-- GNARL is free software; you can redistribute it and/or modify it under -- 12-- terms of the GNU General Public License as published by the Free Soft- -- 13-- ware Foundation; either version 3, or (at your option) any later ver- -- 14-- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- 15-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- 16-- or FITNESS FOR A PARTICULAR PURPOSE. -- 17-- -- 18-- As a special exception under Section 7 of GPL version 3, you are granted -- 19-- additional permissions described in the GCC Runtime Library Exception, -- 20-- version 3.1, as published by the Free Software Foundation. -- 21-- -- 22-- You should have received a copy of the GNU General Public License and -- 23-- a copy of the GCC Runtime Library Exception along with this program; -- 24-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- 25-- <http://www.gnu.org/licenses/>. -- 26-- -- 27-- GNARL was developed by the GNARL team at Florida State University. -- 28-- Extensive contributions were provided by Ada Core Technologies, Inc. -- 29-- -- 30------------------------------------------------------------------------------ 31 32-- Invariants: 33 34-- All user-handlable signals are masked at all times in all tasks/threads 35-- except possibly for the Interrupt_Manager task. 36 37-- When a user task wants to have the effect of masking/unmasking an signal, 38-- it must call Block_Interrupt/Unblock_Interrupt, which will have the effect 39-- of unmasking/masking the signal in the Interrupt_Manager task. These 40-- comments do not apply to vectored hardware interrupts, which may be masked 41-- or unmasked using routined interfaced to the relevant embedded RTOS system 42-- calls. 43 44-- Once we associate a Signal_Server_Task with an signal, the task never goes 45-- away, and we never remove the association. On the other hand, it is more 46-- convenient to terminate an associated Interrupt_Server_Task for a vectored 47-- hardware interrupt (since we use a binary semaphore for synchronization 48-- with the umbrella handler). 49 50-- There is no more than one signal per Signal_Server_Task and no more than 51-- one Signal_Server_Task per signal. The same relation holds for hardware 52-- interrupts and Interrupt_Server_Task's at any given time. That is, only 53-- one non-terminated Interrupt_Server_Task exists for a give interrupt at 54-- any time. 55 56-- Within this package, the lock L is used to protect the various status 57-- tables. If there is a Server_Task associated with a signal or interrupt, 58-- we use the per-task lock of the Server_Task instead so that we protect the 59-- status between Interrupt_Manager and Server_Task. Protection among service 60-- requests are ensured via user calls to the Interrupt_Manager entries. 61 62-- This is reasonably generic version of this package, supporting vectored 63-- hardware interrupts using non-RTOS specific adapter routines which should 64-- easily implemented on any RTOS capable of supporting GNAT. 65 66with Ada.Unchecked_Conversion; 67with Ada.Task_Identification; 68 69with Interfaces.C; use Interfaces.C; 70with System.OS_Interface; use System.OS_Interface; 71with System.Interrupt_Management; 72with System.Task_Primitives.Operations; 73with System.Storage_Elements; 74with System.Tasking.Utilities; 75 76with System.Tasking.Rendezvous; 77pragma Elaborate_All (System.Tasking.Rendezvous); 78 79package body System.Interrupts is 80 81 use Tasking; 82 83 package POP renames System.Task_Primitives.Operations; 84 85 function To_Ada is new Ada.Unchecked_Conversion 86 (System.Tasking.Task_Id, Ada.Task_Identification.Task_Id); 87 88 function To_System is new Ada.Unchecked_Conversion 89 (Ada.Task_Identification.Task_Id, Task_Id); 90 91 ----------------- 92 -- Local Tasks -- 93 ----------------- 94 95 -- WARNING: System.Tasking.Stages performs calls to this task with low- 96 -- level constructs. Do not change this spec without synchronizing it. 97 98 task Interrupt_Manager is 99 entry Detach_Interrupt_Entries (T : Task_Id); 100 101 entry Attach_Handler 102 (New_Handler : Parameterless_Handler; 103 Interrupt : Interrupt_ID; 104 Static : Boolean; 105 Restoration : Boolean := False); 106 107 entry Exchange_Handler 108 (Old_Handler : out Parameterless_Handler; 109 New_Handler : Parameterless_Handler; 110 Interrupt : Interrupt_ID; 111 Static : Boolean); 112 113 entry Detach_Handler 114 (Interrupt : Interrupt_ID; 115 Static : Boolean); 116 117 entry Bind_Interrupt_To_Entry 118 (T : Task_Id; 119 E : Task_Entry_Index; 120 Interrupt : Interrupt_ID); 121 122 pragma Interrupt_Priority (System.Interrupt_Priority'First); 123 end Interrupt_Manager; 124 125 task type Interrupt_Server_Task 126 (Interrupt : Interrupt_ID; 127 Int_Sema : Binary_Semaphore_Id) 128 is 129 -- Server task for vectored hardware interrupt handling 130 131 pragma Interrupt_Priority (System.Interrupt_Priority'First + 2); 132 end Interrupt_Server_Task; 133 134 type Interrupt_Task_Access is access Interrupt_Server_Task; 135 136 ------------------------------- 137 -- Local Types and Variables -- 138 ------------------------------- 139 140 type Entry_Assoc is record 141 T : Task_Id; 142 E : Task_Entry_Index; 143 end record; 144 145 type Handler_Assoc is record 146 H : Parameterless_Handler; 147 Static : Boolean; -- Indicates static binding; 148 end record; 149 150 User_Handler : array (Interrupt_ID) of Handler_Assoc := 151 (others => (null, Static => False)); 152 pragma Volatile_Components (User_Handler); 153 -- Holds the protected procedure handler (if any) and its Static 154 -- information for each interrupt or signal. A handler is static iff it 155 -- is specified through the pragma Attach_Handler. 156 157 User_Entry : array (Interrupt_ID) of Entry_Assoc := 158 (others => (T => Null_Task, E => Null_Task_Entry)); 159 pragma Volatile_Components (User_Entry); 160 -- Holds the task and entry index (if any) for each interrupt / signal 161 162 -- Type and Head, Tail of the list containing Registered Interrupt 163 -- Handlers. These definitions are used to register the handlers 164 -- specified by the pragma Interrupt_Handler. 165 166 type Registered_Handler; 167 type R_Link is access all Registered_Handler; 168 169 type Registered_Handler is record 170 H : System.Address := System.Null_Address; 171 Next : R_Link := null; 172 end record; 173 174 Registered_Handler_Head : R_Link := null; 175 Registered_Handler_Tail : R_Link := null; 176 177 Server_ID : array (Interrupt_ID) of System.Tasking.Task_Id := 178 (others => System.Tasking.Null_Task); 179 pragma Atomic_Components (Server_ID); 180 -- Holds the Task_Id of the Server_Task for each interrupt / signal. 181 -- Task_Id is needed to accomplish locking per interrupt base. Also 182 -- is needed to determine whether to create a new Server_Task. 183 184 Semaphore_ID_Map : array 185 (Interrupt_ID range 0 .. System.OS_Interface.Max_HW_Interrupt) of 186 Binary_Semaphore_Id := (others => 0); 187 -- Array of binary semaphores associated with vectored interrupts. Note 188 -- that the last bound should be Max_HW_Interrupt, but this will raise 189 -- Storage_Error if Num_HW_Interrupts is null so use extra 4 bytes instead. 190 191 Interrupt_Access_Hold : Interrupt_Task_Access; 192 -- Variable for allocating an Interrupt_Server_Task 193 194 Handler_Installed : array (HW_Interrupt) of Boolean := (others => False); 195 -- True if Notify_Interrupt was connected to the interrupt. Handlers can 196 -- be connected but disconnection is not possible on VxWorks. Therefore 197 -- we ensure Notify_Installed is connected at most once. 198 199 ----------------------- 200 -- Local Subprograms -- 201 ----------------------- 202 203 procedure Check_Reserved_Interrupt (Interrupt : Interrupt_ID); 204 -- Check if Id is a reserved interrupt, and if so raise Program_Error 205 -- with an appropriate message, otherwise return. 206 207 procedure Finalize_Interrupt_Servers; 208 -- Unbind the handlers for hardware interrupt server tasks at program 209 -- termination. 210 211 function Is_Registered (Handler : Parameterless_Handler) return Boolean; 212 -- See if Handler has been "pragma"ed using Interrupt_Handler. 213 -- Always consider a null handler as registered. 214 215 procedure Notify_Interrupt (Param : System.Address); 216 pragma Convention (C, Notify_Interrupt); 217 -- Umbrella handler for vectored interrupts (not signals) 218 219 procedure Install_Umbrella_Handler 220 (Interrupt : HW_Interrupt; 221 Handler : System.OS_Interface.Interrupt_Handler); 222 -- Install the runtime umbrella handler for a vectored hardware 223 -- interrupt 224 225 procedure Unimplemented (Feature : String); 226 pragma No_Return (Unimplemented); 227 -- Used to mark a call to an unimplemented function. Raises Program_Error 228 -- with an appropriate message noting that Feature is unimplemented. 229 230 -------------------- 231 -- Attach_Handler -- 232 -------------------- 233 234 -- Calling this procedure with New_Handler = null and Static = True 235 -- means we want to detach the current handler regardless of the previous 236 -- handler's binding status (i.e. do not care if it is a dynamic or static 237 -- handler). 238 239 -- This option is needed so that during the finalization of a PO, we can 240 -- detach handlers attached through pragma Attach_Handler. 241 242 procedure Attach_Handler 243 (New_Handler : Parameterless_Handler; 244 Interrupt : Interrupt_ID; 245 Static : Boolean := False) is 246 begin 247 Check_Reserved_Interrupt (Interrupt); 248 Interrupt_Manager.Attach_Handler (New_Handler, Interrupt, Static); 249 end Attach_Handler; 250 251 ----------------------------- 252 -- Bind_Interrupt_To_Entry -- 253 ----------------------------- 254 255 -- This procedure raises a Program_Error if it tries to 256 -- bind an interrupt to which an Entry or a Procedure is 257 -- already bound. 258 259 procedure Bind_Interrupt_To_Entry 260 (T : Task_Id; 261 E : Task_Entry_Index; 262 Int_Ref : System.Address) 263 is 264 Interrupt : constant Interrupt_ID := 265 Interrupt_ID (Storage_Elements.To_Integer (Int_Ref)); 266 begin 267 Check_Reserved_Interrupt (Interrupt); 268 Interrupt_Manager.Bind_Interrupt_To_Entry (T, E, Interrupt); 269 end Bind_Interrupt_To_Entry; 270 271 --------------------- 272 -- Block_Interrupt -- 273 --------------------- 274 275 procedure Block_Interrupt (Interrupt : Interrupt_ID) is 276 begin 277 Unimplemented ("Block_Interrupt"); 278 end Block_Interrupt; 279 280 ------------------------------ 281 -- Check_Reserved_Interrupt -- 282 ------------------------------ 283 284 procedure Check_Reserved_Interrupt (Interrupt : Interrupt_ID) is 285 begin 286 if Is_Reserved (Interrupt) then 287 raise Program_Error with 288 "interrupt" & Interrupt_ID'Image (Interrupt) & " is reserved"; 289 else 290 return; 291 end if; 292 end Check_Reserved_Interrupt; 293 294 --------------------- 295 -- Current_Handler -- 296 --------------------- 297 298 function Current_Handler 299 (Interrupt : Interrupt_ID) return Parameterless_Handler 300 is 301 begin 302 Check_Reserved_Interrupt (Interrupt); 303 304 -- ??? Since Parameterless_Handler is not Atomic, the current 305 -- implementation is wrong. We need a new service in Interrupt_Manager 306 -- to ensure atomicity. 307 308 return User_Handler (Interrupt).H; 309 end Current_Handler; 310 311 -------------------- 312 -- Detach_Handler -- 313 -------------------- 314 315 -- Calling this procedure with Static = True means we want to Detach the 316 -- current handler regardless of the previous handler's binding status 317 -- (i.e. do not care if it is a dynamic or static handler). 318 319 -- This option is needed so that during the finalization of a PO, we can 320 -- detach handlers attached through pragma Attach_Handler. 321 322 procedure Detach_Handler 323 (Interrupt : Interrupt_ID; 324 Static : Boolean := False) 325 is 326 begin 327 Check_Reserved_Interrupt (Interrupt); 328 Interrupt_Manager.Detach_Handler (Interrupt, Static); 329 end Detach_Handler; 330 331 ------------------------------ 332 -- Detach_Interrupt_Entries -- 333 ------------------------------ 334 335 procedure Detach_Interrupt_Entries (T : Task_Id) is 336 begin 337 Interrupt_Manager.Detach_Interrupt_Entries (T); 338 end Detach_Interrupt_Entries; 339 340 ---------------------- 341 -- Exchange_Handler -- 342 ---------------------- 343 344 -- Calling this procedure with New_Handler = null and Static = True 345 -- means we want to detach the current handler regardless of the previous 346 -- handler's binding status (i.e. we do not care if it is a dynamic or 347 -- static handler). 348 349 -- This option is needed so that during the finalization of a PO, we can 350 -- detach handlers attached through pragma Attach_Handler. 351 352 procedure Exchange_Handler 353 (Old_Handler : out Parameterless_Handler; 354 New_Handler : Parameterless_Handler; 355 Interrupt : Interrupt_ID; 356 Static : Boolean := False) 357 is 358 begin 359 Check_Reserved_Interrupt (Interrupt); 360 Interrupt_Manager.Exchange_Handler 361 (Old_Handler, New_Handler, Interrupt, Static); 362 end Exchange_Handler; 363 364 -------------- 365 -- Finalize -- 366 -------------- 367 368 procedure Finalize (Object : in out Static_Interrupt_Protection) is 369 begin 370 -- ??? loop to be executed only when we're not doing library level 371 -- finalization, since in this case all interrupt / signal tasks are 372 -- gone. 373 374 if not Interrupt_Manager'Terminated then 375 for N in reverse Object.Previous_Handlers'Range loop 376 Interrupt_Manager.Attach_Handler 377 (New_Handler => Object.Previous_Handlers (N).Handler, 378 Interrupt => Object.Previous_Handlers (N).Interrupt, 379 Static => Object.Previous_Handlers (N).Static, 380 Restoration => True); 381 end loop; 382 end if; 383 384 Tasking.Protected_Objects.Entries.Finalize 385 (Tasking.Protected_Objects.Entries.Protection_Entries (Object)); 386 end Finalize; 387 388 -------------------------------- 389 -- Finalize_Interrupt_Servers -- 390 -------------------------------- 391 392 -- Restore default handlers for interrupt servers 393 394 -- This is called by the Interrupt_Manager task when it receives the abort 395 -- signal during program finalization. 396 397 procedure Finalize_Interrupt_Servers is 398 HW_Interrupts : constant Boolean := HW_Interrupt'Last >= 0; 399 begin 400 if HW_Interrupts then 401 for Int in HW_Interrupt loop 402 if Server_ID (Interrupt_ID (Int)) /= null 403 and then 404 not Ada.Task_Identification.Is_Terminated 405 (To_Ada (Server_ID (Interrupt_ID (Int)))) 406 then 407 Interrupt_Manager.Attach_Handler 408 (New_Handler => null, 409 Interrupt => Interrupt_ID (Int), 410 Static => True, 411 Restoration => True); 412 end if; 413 end loop; 414 end if; 415 end Finalize_Interrupt_Servers; 416 417 ------------------------------------- 418 -- Has_Interrupt_Or_Attach_Handler -- 419 ------------------------------------- 420 421 function Has_Interrupt_Or_Attach_Handler 422 (Object : access Dynamic_Interrupt_Protection) 423 return Boolean 424 is 425 pragma Unreferenced (Object); 426 begin 427 return True; 428 end Has_Interrupt_Or_Attach_Handler; 429 430 function Has_Interrupt_Or_Attach_Handler 431 (Object : access Static_Interrupt_Protection) 432 return Boolean 433 is 434 pragma Unreferenced (Object); 435 begin 436 return True; 437 end Has_Interrupt_Or_Attach_Handler; 438 439 ---------------------- 440 -- Ignore_Interrupt -- 441 ---------------------- 442 443 procedure Ignore_Interrupt (Interrupt : Interrupt_ID) is 444 begin 445 Unimplemented ("Ignore_Interrupt"); 446 end Ignore_Interrupt; 447 448 ---------------------- 449 -- Install_Handlers -- 450 ---------------------- 451 452 procedure Install_Handlers 453 (Object : access Static_Interrupt_Protection; 454 New_Handlers : New_Handler_Array) 455 is 456 begin 457 for N in New_Handlers'Range loop 458 459 -- We need a lock around this ??? 460 461 Object.Previous_Handlers (N).Interrupt := New_Handlers (N).Interrupt; 462 Object.Previous_Handlers (N).Static := User_Handler 463 (New_Handlers (N).Interrupt).Static; 464 465 -- We call Exchange_Handler and not directly Interrupt_Manager. 466 -- Exchange_Handler so we get the Is_Reserved check. 467 468 Exchange_Handler 469 (Old_Handler => Object.Previous_Handlers (N).Handler, 470 New_Handler => New_Handlers (N).Handler, 471 Interrupt => New_Handlers (N).Interrupt, 472 Static => True); 473 end loop; 474 end Install_Handlers; 475 476 --------------------------------- 477 -- Install_Restricted_Handlers -- 478 --------------------------------- 479 480 procedure Install_Restricted_Handlers 481 (Prio : Any_Priority; 482 Handlers : New_Handler_Array) 483 is 484 pragma Unreferenced (Prio); 485 begin 486 for N in Handlers'Range loop 487 Attach_Handler (Handlers (N).Handler, Handlers (N).Interrupt, True); 488 end loop; 489 end Install_Restricted_Handlers; 490 491 ------------------------------ 492 -- Install_Umbrella_Handler -- 493 ------------------------------ 494 495 procedure Install_Umbrella_Handler 496 (Interrupt : HW_Interrupt; 497 Handler : System.OS_Interface.Interrupt_Handler) 498 is 499 Vec : constant Interrupt_Vector := 500 Interrupt_Number_To_Vector (int (Interrupt)); 501 502 Status : int; 503 504 begin 505 -- Only install umbrella handler when no Ada handler has already been 506 -- installed. Note that the interrupt number is passed as a parameter 507 -- when an interrupt occurs, so the umbrella handler has a different 508 -- wrapper generated by intConnect for each interrupt number. 509 510 if not Handler_Installed (Interrupt) then 511 Status := 512 Interrupt_Connect (Vec, Handler, System.Address (Interrupt)); 513 pragma Assert (Status = 0); 514 515 Handler_Installed (Interrupt) := True; 516 end if; 517 end Install_Umbrella_Handler; 518 519 ---------------- 520 -- Is_Blocked -- 521 ---------------- 522 523 function Is_Blocked (Interrupt : Interrupt_ID) return Boolean is 524 begin 525 Unimplemented ("Is_Blocked"); 526 return False; 527 end Is_Blocked; 528 529 ----------------------- 530 -- Is_Entry_Attached -- 531 ----------------------- 532 533 function Is_Entry_Attached (Interrupt : Interrupt_ID) return Boolean is 534 begin 535 Check_Reserved_Interrupt (Interrupt); 536 return User_Entry (Interrupt).T /= Null_Task; 537 end Is_Entry_Attached; 538 539 ------------------------- 540 -- Is_Handler_Attached -- 541 ------------------------- 542 543 function Is_Handler_Attached (Interrupt : Interrupt_ID) return Boolean is 544 begin 545 Check_Reserved_Interrupt (Interrupt); 546 return User_Handler (Interrupt).H /= null; 547 end Is_Handler_Attached; 548 549 ---------------- 550 -- Is_Ignored -- 551 ---------------- 552 553 function Is_Ignored (Interrupt : Interrupt_ID) return Boolean is 554 begin 555 Unimplemented ("Is_Ignored"); 556 return False; 557 end Is_Ignored; 558 559 ------------------- 560 -- Is_Registered -- 561 ------------------- 562 563 function Is_Registered (Handler : Parameterless_Handler) return Boolean is 564 type Fat_Ptr is record 565 Object_Addr : System.Address; 566 Handler_Addr : System.Address; 567 end record; 568 569 function To_Fat_Ptr is new Ada.Unchecked_Conversion 570 (Parameterless_Handler, Fat_Ptr); 571 572 Ptr : R_Link; 573 Fat : Fat_Ptr; 574 575 begin 576 if Handler = null then 577 return True; 578 end if; 579 580 Fat := To_Fat_Ptr (Handler); 581 582 Ptr := Registered_Handler_Head; 583 while Ptr /= null loop 584 if Ptr.H = Fat.Handler_Addr then 585 return True; 586 end if; 587 588 Ptr := Ptr.Next; 589 end loop; 590 591 return False; 592 end Is_Registered; 593 594 ----------------- 595 -- Is_Reserved -- 596 ----------------- 597 598 function Is_Reserved (Interrupt : Interrupt_ID) return Boolean is 599 use System.Interrupt_Management; 600 begin 601 return Reserve (System.Interrupt_Management.Interrupt_ID (Interrupt)); 602 end Is_Reserved; 603 604 ---------------------- 605 -- Notify_Interrupt -- 606 ---------------------- 607 608 -- Umbrella handler for vectored hardware interrupts (as opposed to signals 609 -- and exceptions). As opposed to the signal implementation, this handler 610 -- is installed in the vector table when the first Ada handler is attached 611 -- to the interrupt. However because VxWorks don't support disconnecting 612 -- handlers, this subprogram always test whether or not an Ada handler is 613 -- effectively attached. 614 615 -- Otherwise, the handler that existed prior to program startup is in the 616 -- vector table. This ensures that handlers installed by the BSP are active 617 -- unless explicitly replaced in the program text. 618 619 -- Each Interrupt_Server_Task has an associated binary semaphore on which 620 -- it pends once it's been started. This routine determines The appropriate 621 -- semaphore and issues a semGive call, waking the server task. When 622 -- a handler is unbound, System.Interrupts.Unbind_Handler issues a 623 -- Binary_Semaphore_Flush, and the server task deletes its semaphore 624 -- and terminates. 625 626 procedure Notify_Interrupt (Param : System.Address) is 627 Interrupt : constant Interrupt_ID := Interrupt_ID (Param); 628 Id : constant Binary_Semaphore_Id := Semaphore_ID_Map (Interrupt); 629 Status : int; 630 begin 631 if Id /= 0 then 632 Status := Binary_Semaphore_Release (Id); 633 pragma Assert (Status = 0); 634 end if; 635 end Notify_Interrupt; 636 637 --------------- 638 -- Reference -- 639 --------------- 640 641 function Reference (Interrupt : Interrupt_ID) return System.Address is 642 begin 643 Check_Reserved_Interrupt (Interrupt); 644 return Storage_Elements.To_Address 645 (Storage_Elements.Integer_Address (Interrupt)); 646 end Reference; 647 648 -------------------------------- 649 -- Register_Interrupt_Handler -- 650 -------------------------------- 651 652 procedure Register_Interrupt_Handler (Handler_Addr : System.Address) is 653 New_Node_Ptr : R_Link; 654 655 begin 656 -- This routine registers a handler as usable for dynamic interrupt 657 -- handler association. Routines attaching and detaching handlers 658 -- dynamically should determine whether the handler is registered. 659 -- Program_Error should be raised if it is not registered. 660 661 -- Pragma Interrupt_Handler can only appear in a library level PO 662 -- definition and instantiation. Therefore, we do not need to implement 663 -- an unregister operation. Nor do we need to protect the queue 664 -- structure with a lock. 665 666 pragma Assert (Handler_Addr /= System.Null_Address); 667 668 New_Node_Ptr := new Registered_Handler; 669 New_Node_Ptr.H := Handler_Addr; 670 671 if Registered_Handler_Head = null then 672 Registered_Handler_Head := New_Node_Ptr; 673 Registered_Handler_Tail := New_Node_Ptr; 674 else 675 Registered_Handler_Tail.Next := New_Node_Ptr; 676 Registered_Handler_Tail := New_Node_Ptr; 677 end if; 678 end Register_Interrupt_Handler; 679 680 ----------------------- 681 -- Unblock_Interrupt -- 682 ----------------------- 683 684 procedure Unblock_Interrupt (Interrupt : Interrupt_ID) is 685 begin 686 Unimplemented ("Unblock_Interrupt"); 687 end Unblock_Interrupt; 688 689 ------------------ 690 -- Unblocked_By -- 691 ------------------ 692 693 function Unblocked_By 694 (Interrupt : Interrupt_ID) return System.Tasking.Task_Id 695 is 696 begin 697 Unimplemented ("Unblocked_By"); 698 return Null_Task; 699 end Unblocked_By; 700 701 ------------------------ 702 -- Unignore_Interrupt -- 703 ------------------------ 704 705 procedure Unignore_Interrupt (Interrupt : Interrupt_ID) is 706 begin 707 Unimplemented ("Unignore_Interrupt"); 708 end Unignore_Interrupt; 709 710 ------------------- 711 -- Unimplemented -- 712 ------------------- 713 714 procedure Unimplemented (Feature : String) is 715 begin 716 raise Program_Error with Feature & " not implemented on VxWorks"; 717 end Unimplemented; 718 719 ----------------------- 720 -- Interrupt_Manager -- 721 ----------------------- 722 723 task body Interrupt_Manager is 724 -- By making this task independent of any master, when the process goes 725 -- away, the Interrupt_Manager will terminate gracefully. 726 727 Ignore : constant Boolean := System.Tasking.Utilities.Make_Independent; 728 pragma Unreferenced (Ignore); 729 730 -------------------- 731 -- Local Routines -- 732 -------------------- 733 734 procedure Bind_Handler (Interrupt : Interrupt_ID); 735 -- This procedure does not do anything if a signal is blocked. 736 -- Otherwise, we have to interrupt Server_Task for status change 737 -- through a wakeup signal. 738 739 procedure Unbind_Handler (Interrupt : Interrupt_ID); 740 -- This procedure does not do anything if a signal is blocked. 741 -- Otherwise, we have to interrupt Server_Task for status change 742 -- through an abort signal. 743 744 procedure Unprotected_Exchange_Handler 745 (Old_Handler : out Parameterless_Handler; 746 New_Handler : Parameterless_Handler; 747 Interrupt : Interrupt_ID; 748 Static : Boolean; 749 Restoration : Boolean := False); 750 751 procedure Unprotected_Detach_Handler 752 (Interrupt : Interrupt_ID; 753 Static : Boolean); 754 755 ------------------ 756 -- Bind_Handler -- 757 ------------------ 758 759 procedure Bind_Handler (Interrupt : Interrupt_ID) is 760 begin 761 Install_Umbrella_Handler 762 (HW_Interrupt (Interrupt), Notify_Interrupt'Access); 763 end Bind_Handler; 764 765 -------------------- 766 -- Unbind_Handler -- 767 -------------------- 768 769 procedure Unbind_Handler (Interrupt : Interrupt_ID) is 770 Status : int; 771 772 begin 773 -- Flush server task off semaphore, allowing it to terminate 774 775 Status := Binary_Semaphore_Flush (Semaphore_ID_Map (Interrupt)); 776 pragma Assert (Status = 0); 777 end Unbind_Handler; 778 779 -------------------------------- 780 -- Unprotected_Detach_Handler -- 781 -------------------------------- 782 783 procedure Unprotected_Detach_Handler 784 (Interrupt : Interrupt_ID; 785 Static : Boolean) 786 is 787 Old_Handler : Parameterless_Handler; 788 begin 789 if User_Entry (Interrupt).T /= Null_Task then 790 791 -- If an interrupt entry is installed raise Program_Error 792 -- (propagate it to the caller). 793 794 raise Program_Error with 795 "an interrupt entry is already installed"; 796 end if; 797 798 -- Note : Static = True will pass the following check. This is the 799 -- case when we want to detach a handler regardless of the static 800 -- status of the Current_Handler. 801 802 if not Static and then User_Handler (Interrupt).Static then 803 804 -- Trying to detach a static Interrupt Handler, raise 805 -- Program_Error. 806 807 raise Program_Error with 808 "trying to detach a static Interrupt Handler"; 809 end if; 810 811 Old_Handler := User_Handler (Interrupt).H; 812 813 -- The new handler 814 815 User_Handler (Interrupt).H := null; 816 User_Handler (Interrupt).Static := False; 817 818 if Old_Handler /= null then 819 Unbind_Handler (Interrupt); 820 end if; 821 end Unprotected_Detach_Handler; 822 823 ---------------------------------- 824 -- Unprotected_Exchange_Handler -- 825 ---------------------------------- 826 827 procedure Unprotected_Exchange_Handler 828 (Old_Handler : out Parameterless_Handler; 829 New_Handler : Parameterless_Handler; 830 Interrupt : Interrupt_ID; 831 Static : Boolean; 832 Restoration : Boolean := False) 833 is 834 begin 835 if User_Entry (Interrupt).T /= Null_Task then 836 837 -- If an interrupt entry is already installed, raise 838 -- Program_Error (propagate it to the caller). 839 840 raise Program_Error with "an interrupt is already installed"; 841 end if; 842 843 -- Note : A null handler with Static = True will pass the following 844 -- check. This is the case when we want to detach a handler 845 -- regardless of the Static status of Current_Handler. 846 847 -- We don't check anything if Restoration is True, since we may be 848 -- detaching a static handler to restore a dynamic one. 849 850 if not Restoration and then not Static 851 and then (User_Handler (Interrupt).Static 852 853 -- Trying to overwrite a static Interrupt Handler with a dynamic 854 -- Handler 855 856 -- The new handler is not specified as an Interrupt Handler by a 857 -- pragma. 858 859 or else not Is_Registered (New_Handler)) 860 then 861 raise Program_Error with 862 "trying to overwrite a static interrupt handler with a " 863 & "dynamic handler"; 864 end if; 865 866 -- Save the old handler 867 868 Old_Handler := User_Handler (Interrupt).H; 869 870 -- The new handler 871 872 User_Handler (Interrupt).H := New_Handler; 873 874 if New_Handler = null then 875 876 -- The null handler means we are detaching the handler 877 878 User_Handler (Interrupt).Static := False; 879 880 else 881 User_Handler (Interrupt).Static := Static; 882 end if; 883 884 -- Invoke a corresponding Server_Task if not yet created. Place 885 -- Task_Id info in Server_ID array. 886 887 if New_Handler /= null 888 and then 889 (Server_ID (Interrupt) = Null_Task 890 or else 891 Ada.Task_Identification.Is_Terminated 892 (To_Ada (Server_ID (Interrupt)))) 893 then 894 Interrupt_Access_Hold := 895 new Interrupt_Server_Task (Interrupt, Binary_Semaphore_Create); 896 Server_ID (Interrupt) := 897 To_System (Interrupt_Access_Hold.all'Identity); 898 end if; 899 900 if (New_Handler = null) and then Old_Handler /= null then 901 902 -- Restore default handler 903 904 Unbind_Handler (Interrupt); 905 906 elsif Old_Handler = null then 907 908 -- Save default handler 909 910 Bind_Handler (Interrupt); 911 end if; 912 end Unprotected_Exchange_Handler; 913 914 -- Start of processing for Interrupt_Manager 915 916 begin 917 loop 918 -- A block is needed to absorb Program_Error exception 919 920 declare 921 Old_Handler : Parameterless_Handler; 922 923 begin 924 select 925 accept Attach_Handler 926 (New_Handler : Parameterless_Handler; 927 Interrupt : Interrupt_ID; 928 Static : Boolean; 929 Restoration : Boolean := False) 930 do 931 Unprotected_Exchange_Handler 932 (Old_Handler, New_Handler, Interrupt, Static, Restoration); 933 end Attach_Handler; 934 935 or 936 accept Exchange_Handler 937 (Old_Handler : out Parameterless_Handler; 938 New_Handler : Parameterless_Handler; 939 Interrupt : Interrupt_ID; 940 Static : Boolean) 941 do 942 Unprotected_Exchange_Handler 943 (Old_Handler, New_Handler, Interrupt, Static); 944 end Exchange_Handler; 945 946 or 947 accept Detach_Handler 948 (Interrupt : Interrupt_ID; 949 Static : Boolean) 950 do 951 Unprotected_Detach_Handler (Interrupt, Static); 952 end Detach_Handler; 953 954 or 955 accept Bind_Interrupt_To_Entry 956 (T : Task_Id; 957 E : Task_Entry_Index; 958 Interrupt : Interrupt_ID) 959 do 960 -- If there is a binding already (either a procedure or an 961 -- entry), raise Program_Error (propagate it to the caller). 962 963 if User_Handler (Interrupt).H /= null 964 or else User_Entry (Interrupt).T /= Null_Task 965 then 966 raise Program_Error with 967 "a binding for this interrupt is already present"; 968 end if; 969 970 User_Entry (Interrupt) := Entry_Assoc'(T => T, E => E); 971 972 -- Indicate the attachment of interrupt entry in the ATCB. 973 -- This is needed so when an interrupt entry task terminates 974 -- the binding can be cleaned. The call to unbinding must be 975 -- make by the task before it terminates. 976 977 T.Interrupt_Entry := True; 978 979 -- Invoke a corresponding Server_Task if not yet created. 980 -- Place Task_Id info in Server_ID array. 981 982 if Server_ID (Interrupt) = Null_Task 983 or else 984 Ada.Task_Identification.Is_Terminated 985 (To_Ada (Server_ID (Interrupt))) 986 then 987 Interrupt_Access_Hold := new Interrupt_Server_Task 988 (Interrupt, Binary_Semaphore_Create); 989 Server_ID (Interrupt) := 990 To_System (Interrupt_Access_Hold.all'Identity); 991 end if; 992 993 Bind_Handler (Interrupt); 994 end Bind_Interrupt_To_Entry; 995 996 or 997 accept Detach_Interrupt_Entries (T : Task_Id) do 998 for Int in Interrupt_ID'Range loop 999 if not Is_Reserved (Int) then 1000 if User_Entry (Int).T = T then 1001 User_Entry (Int) := 1002 Entry_Assoc' 1003 (T => Null_Task, E => Null_Task_Entry); 1004 Unbind_Handler (Int); 1005 end if; 1006 end if; 1007 end loop; 1008 1009 -- Indicate in ATCB that no interrupt entries are attached 1010 1011 T.Interrupt_Entry := False; 1012 end Detach_Interrupt_Entries; 1013 end select; 1014 1015 exception 1016 -- If there is a Program_Error we just want to propagate it to 1017 -- the caller and do not want to stop this task. 1018 1019 when Program_Error => 1020 null; 1021 1022 when others => 1023 pragma Assert (False); 1024 null; 1025 end; 1026 end loop; 1027 1028 exception 1029 when Standard'Abort_Signal => 1030 1031 -- Flush interrupt server semaphores, so they can terminate 1032 1033 Finalize_Interrupt_Servers; 1034 raise; 1035 end Interrupt_Manager; 1036 1037 --------------------------- 1038 -- Interrupt_Server_Task -- 1039 --------------------------- 1040 1041 -- Server task for vectored hardware interrupt handling 1042 1043 task body Interrupt_Server_Task is 1044 Ignore : constant Boolean := System.Tasking.Utilities.Make_Independent; 1045 1046 Self_Id : constant Task_Id := Self; 1047 Tmp_Handler : Parameterless_Handler; 1048 Tmp_ID : Task_Id; 1049 Tmp_Entry_Index : Task_Entry_Index; 1050 Status : int; 1051 1052 begin 1053 Semaphore_ID_Map (Interrupt) := Int_Sema; 1054 1055 loop 1056 -- Pend on semaphore that will be triggered by the umbrella handler 1057 -- when the associated interrupt comes in. 1058 1059 Status := Binary_Semaphore_Obtain (Int_Sema); 1060 pragma Assert (Status = 0); 1061 1062 if User_Handler (Interrupt).H /= null then 1063 1064 -- Protected procedure handler 1065 1066 Tmp_Handler := User_Handler (Interrupt).H; 1067 Tmp_Handler.all; 1068 1069 elsif User_Entry (Interrupt).T /= Null_Task then 1070 1071 -- Interrupt entry handler 1072 1073 Tmp_ID := User_Entry (Interrupt).T; 1074 Tmp_Entry_Index := User_Entry (Interrupt).E; 1075 System.Tasking.Rendezvous.Call_Simple 1076 (Tmp_ID, Tmp_Entry_Index, System.Null_Address); 1077 1078 else 1079 -- Semaphore has been flushed by an unbind operation in the 1080 -- Interrupt_Manager. Terminate the server task. 1081 1082 -- Wait for the Interrupt_Manager to complete its work 1083 1084 POP.Write_Lock (Self_Id); 1085 1086 -- Unassociate the interrupt handler 1087 1088 Semaphore_ID_Map (Interrupt) := 0; 1089 1090 -- Delete the associated semaphore 1091 1092 Status := Binary_Semaphore_Delete (Int_Sema); 1093 1094 pragma Assert (Status = 0); 1095 1096 -- Set status for the Interrupt_Manager 1097 1098 Server_ID (Interrupt) := Null_Task; 1099 POP.Unlock (Self_Id); 1100 1101 exit; 1102 end if; 1103 end loop; 1104 end Interrupt_Server_Task; 1105 1106begin 1107 -- Get Interrupt_Manager's ID so that Abort_Interrupt can be sent 1108 1109 Interrupt_Manager_ID := To_System (Interrupt_Manager'Identity); 1110end System.Interrupts; 1111