1 /****************************************************************************** 2 * 3 * Module Name: exmisc - ACPI AML (p-code) execution - specific opcodes 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2016, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 #include <acpi/acpi.h> 45 #include "accommon.h" 46 #include "acinterp.h" 47 #include "amlcode.h" 48 #include "amlresrc.h" 49 50 #define _COMPONENT ACPI_EXECUTER 51 ACPI_MODULE_NAME("exmisc") 52 53 /******************************************************************************* 54 * 55 * FUNCTION: acpi_ex_get_object_reference 56 * 57 * PARAMETERS: obj_desc - Create a reference to this object 58 * return_desc - Where to store the reference 59 * walk_state - Current state 60 * 61 * RETURN: Status 62 * 63 * DESCRIPTION: Obtain and return a "reference" to the target object 64 * Common code for the ref_of_op and the cond_ref_of_op. 65 * 66 ******************************************************************************/ 67 acpi_status 68 acpi_ex_get_object_reference(union acpi_operand_object *obj_desc, 69 union acpi_operand_object **return_desc, 70 struct acpi_walk_state *walk_state) 71 { 72 union acpi_operand_object *reference_obj; 73 union acpi_operand_object *referenced_obj; 74 75 ACPI_FUNCTION_TRACE_PTR(ex_get_object_reference, obj_desc); 76 77 *return_desc = NULL; 78 79 switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) { 80 case ACPI_DESC_TYPE_OPERAND: 81 82 if (obj_desc->common.type != ACPI_TYPE_LOCAL_REFERENCE) { 83 return_ACPI_STATUS(AE_AML_OPERAND_TYPE); 84 } 85 86 /* 87 * Must be a reference to a Local or Arg 88 */ 89 switch (obj_desc->reference.class) { 90 case ACPI_REFCLASS_LOCAL: 91 case ACPI_REFCLASS_ARG: 92 case ACPI_REFCLASS_DEBUG: 93 94 /* The referenced object is the pseudo-node for the local/arg */ 95 96 referenced_obj = obj_desc->reference.object; 97 break; 98 99 default: 100 101 ACPI_ERROR((AE_INFO, "Invalid Reference Class 0x%2.2X", 102 obj_desc->reference.class)); 103 return_ACPI_STATUS(AE_AML_OPERAND_TYPE); 104 } 105 break; 106 107 case ACPI_DESC_TYPE_NAMED: 108 /* 109 * A named reference that has already been resolved to a Node 110 */ 111 referenced_obj = obj_desc; 112 break; 113 114 default: 115 116 ACPI_ERROR((AE_INFO, "Invalid descriptor type 0x%X", 117 ACPI_GET_DESCRIPTOR_TYPE(obj_desc))); 118 return_ACPI_STATUS(AE_TYPE); 119 } 120 121 /* Create a new reference object */ 122 123 reference_obj = 124 acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE); 125 if (!reference_obj) { 126 return_ACPI_STATUS(AE_NO_MEMORY); 127 } 128 129 reference_obj->reference.class = ACPI_REFCLASS_REFOF; 130 reference_obj->reference.object = referenced_obj; 131 *return_desc = reference_obj; 132 133 ACPI_DEBUG_PRINT((ACPI_DB_EXEC, 134 "Object %p Type [%s], returning Reference %p\n", 135 obj_desc, acpi_ut_get_object_type_name(obj_desc), 136 *return_desc)); 137 138 return_ACPI_STATUS(AE_OK); 139 } 140 141 /******************************************************************************* 142 * 143 * FUNCTION: acpi_ex_concat_template 144 * 145 * PARAMETERS: operand0 - First source object 146 * operand1 - Second source object 147 * actual_return_desc - Where to place the return object 148 * walk_state - Current walk state 149 * 150 * RETURN: Status 151 * 152 * DESCRIPTION: Concatenate two resource templates 153 * 154 ******************************************************************************/ 155 156 acpi_status 157 acpi_ex_concat_template(union acpi_operand_object *operand0, 158 union acpi_operand_object *operand1, 159 union acpi_operand_object **actual_return_desc, 160 struct acpi_walk_state *walk_state) 161 { 162 acpi_status status; 163 union acpi_operand_object *return_desc; 164 u8 *new_buf; 165 u8 *end_tag; 166 acpi_size length0; 167 acpi_size length1; 168 acpi_size new_length; 169 170 ACPI_FUNCTION_TRACE(ex_concat_template); 171 172 /* 173 * Find the end_tag descriptor in each resource template. 174 * Note1: returned pointers point TO the end_tag, not past it. 175 * Note2: zero-length buffers are allowed; treated like one end_tag 176 */ 177 178 /* Get the length of the first resource template */ 179 180 status = acpi_ut_get_resource_end_tag(operand0, &end_tag); 181 if (ACPI_FAILURE(status)) { 182 return_ACPI_STATUS(status); 183 } 184 185 length0 = ACPI_PTR_DIFF(end_tag, operand0->buffer.pointer); 186 187 /* Get the length of the second resource template */ 188 189 status = acpi_ut_get_resource_end_tag(operand1, &end_tag); 190 if (ACPI_FAILURE(status)) { 191 return_ACPI_STATUS(status); 192 } 193 194 length1 = ACPI_PTR_DIFF(end_tag, operand1->buffer.pointer); 195 196 /* Combine both lengths, minimum size will be 2 for end_tag */ 197 198 new_length = length0 + length1 + sizeof(struct aml_resource_end_tag); 199 200 /* Create a new buffer object for the result (with one end_tag) */ 201 202 return_desc = acpi_ut_create_buffer_object(new_length); 203 if (!return_desc) { 204 return_ACPI_STATUS(AE_NO_MEMORY); 205 } 206 207 /* 208 * Copy the templates to the new buffer, 0 first, then 1 follows. One 209 * end_tag descriptor is copied from Operand1. 210 */ 211 new_buf = return_desc->buffer.pointer; 212 memcpy(new_buf, operand0->buffer.pointer, length0); 213 memcpy(new_buf + length0, operand1->buffer.pointer, length1); 214 215 /* Insert end_tag and set the checksum to zero, means "ignore checksum" */ 216 217 new_buf[new_length - 1] = 0; 218 new_buf[new_length - 2] = ACPI_RESOURCE_NAME_END_TAG | 1; 219 220 /* Return the completed resource template */ 221 222 *actual_return_desc = return_desc; 223 return_ACPI_STATUS(AE_OK); 224 } 225 226 /******************************************************************************* 227 * 228 * FUNCTION: acpi_ex_do_concatenate 229 * 230 * PARAMETERS: operand0 - First source object 231 * operand1 - Second source object 232 * actual_return_desc - Where to place the return object 233 * walk_state - Current walk state 234 * 235 * RETURN: Status 236 * 237 * DESCRIPTION: Concatenate two objects OF THE SAME TYPE. 238 * 239 ******************************************************************************/ 240 241 acpi_status 242 acpi_ex_do_concatenate(union acpi_operand_object *operand0, 243 union acpi_operand_object *operand1, 244 union acpi_operand_object **actual_return_desc, 245 struct acpi_walk_state *walk_state) 246 { 247 union acpi_operand_object *local_operand1 = operand1; 248 union acpi_operand_object *return_desc; 249 char *new_buf; 250 const char *type_string; 251 acpi_status status; 252 253 ACPI_FUNCTION_TRACE(ex_do_concatenate); 254 255 /* 256 * Convert the second operand if necessary. The first operand 257 * determines the type of the second operand, (See the Data Types 258 * section of the ACPI specification.) Both object types are 259 * guaranteed to be either Integer/String/Buffer by the operand 260 * resolution mechanism. 261 */ 262 switch (operand0->common.type) { 263 case ACPI_TYPE_INTEGER: 264 265 status = 266 acpi_ex_convert_to_integer(operand1, &local_operand1, 16); 267 break; 268 269 case ACPI_TYPE_STRING: 270 /* 271 * Per the ACPI spec, Concatenate only supports int/str/buf. 272 * However, we support all objects here as an extension. 273 * This improves the usefulness of the Printf() macro. 274 * 12/2015. 275 */ 276 switch (operand1->common.type) { 277 case ACPI_TYPE_INTEGER: 278 case ACPI_TYPE_STRING: 279 case ACPI_TYPE_BUFFER: 280 281 status = 282 acpi_ex_convert_to_string(operand1, &local_operand1, 283 ACPI_IMPLICIT_CONVERT_HEX); 284 break; 285 286 default: 287 /* 288 * Just emit a string containing the object type. 289 */ 290 type_string = 291 acpi_ut_get_type_name(operand1->common.type); 292 293 local_operand1 = acpi_ut_create_string_object(((acpi_size) strlen(type_string) + 9)); /* 9 For "[Object]" */ 294 if (!local_operand1) { 295 status = AE_NO_MEMORY; 296 goto cleanup; 297 } 298 299 strcpy(local_operand1->string.pointer, "["); 300 strcat(local_operand1->string.pointer, type_string); 301 strcat(local_operand1->string.pointer, " Object]"); 302 status = AE_OK; 303 break; 304 } 305 break; 306 307 case ACPI_TYPE_BUFFER: 308 309 status = acpi_ex_convert_to_buffer(operand1, &local_operand1); 310 break; 311 312 default: 313 314 ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", 315 operand0->common.type)); 316 status = AE_AML_INTERNAL; 317 } 318 319 if (ACPI_FAILURE(status)) { 320 goto cleanup; 321 } 322 323 /* 324 * Both operands are now known to be the same object type 325 * (Both are Integer, String, or Buffer), and we can now perform the 326 * concatenation. 327 */ 328 329 /* 330 * There are three cases to handle: 331 * 332 * 1) Two Integers concatenated to produce a new Buffer 333 * 2) Two Strings concatenated to produce a new String 334 * 3) Two Buffers concatenated to produce a new Buffer 335 */ 336 switch (operand0->common.type) { 337 case ACPI_TYPE_INTEGER: 338 339 /* Result of two Integers is a Buffer */ 340 /* Need enough buffer space for two integers */ 341 342 return_desc = acpi_ut_create_buffer_object((acpi_size) 343 ACPI_MUL_2 344 (acpi_gbl_integer_byte_width)); 345 if (!return_desc) { 346 status = AE_NO_MEMORY; 347 goto cleanup; 348 } 349 350 new_buf = (char *)return_desc->buffer.pointer; 351 352 /* Copy the first integer, LSB first */ 353 354 memcpy(new_buf, &operand0->integer.value, 355 acpi_gbl_integer_byte_width); 356 357 /* Copy the second integer (LSB first) after the first */ 358 359 memcpy(new_buf + acpi_gbl_integer_byte_width, 360 &local_operand1->integer.value, 361 acpi_gbl_integer_byte_width); 362 break; 363 364 case ACPI_TYPE_STRING: 365 366 /* Result of two Strings is a String */ 367 368 return_desc = acpi_ut_create_string_object(((acpi_size) 369 operand0->string. 370 length + 371 local_operand1-> 372 string.length)); 373 if (!return_desc) { 374 status = AE_NO_MEMORY; 375 goto cleanup; 376 } 377 378 new_buf = return_desc->string.pointer; 379 380 /* Concatenate the strings */ 381 382 strcpy(new_buf, operand0->string.pointer); 383 strcat(new_buf, local_operand1->string.pointer); 384 break; 385 386 case ACPI_TYPE_BUFFER: 387 388 /* Result of two Buffers is a Buffer */ 389 390 return_desc = acpi_ut_create_buffer_object(((acpi_size) 391 operand0->buffer. 392 length + 393 local_operand1-> 394 buffer.length)); 395 if (!return_desc) { 396 status = AE_NO_MEMORY; 397 goto cleanup; 398 } 399 400 new_buf = (char *)return_desc->buffer.pointer; 401 402 /* Concatenate the buffers */ 403 404 memcpy(new_buf, operand0->buffer.pointer, 405 operand0->buffer.length); 406 memcpy(new_buf + operand0->buffer.length, 407 local_operand1->buffer.pointer, 408 local_operand1->buffer.length); 409 break; 410 411 default: 412 413 /* Invalid object type, should not happen here */ 414 415 ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", 416 operand0->common.type)); 417 status = AE_AML_INTERNAL; 418 goto cleanup; 419 } 420 421 *actual_return_desc = return_desc; 422 423 cleanup: 424 if (local_operand1 != operand1) { 425 acpi_ut_remove_reference(local_operand1); 426 } 427 return_ACPI_STATUS(status); 428 } 429 430 /******************************************************************************* 431 * 432 * FUNCTION: acpi_ex_do_math_op 433 * 434 * PARAMETERS: opcode - AML opcode 435 * integer0 - Integer operand #0 436 * integer1 - Integer operand #1 437 * 438 * RETURN: Integer result of the operation 439 * 440 * DESCRIPTION: Execute a math AML opcode. The purpose of having all of the 441 * math functions here is to prevent a lot of pointer dereferencing 442 * to obtain the operands. 443 * 444 ******************************************************************************/ 445 446 u64 acpi_ex_do_math_op(u16 opcode, u64 integer0, u64 integer1) 447 { 448 449 ACPI_FUNCTION_ENTRY(); 450 451 switch (opcode) { 452 case AML_ADD_OP: /* Add (Integer0, Integer1, Result) */ 453 454 return (integer0 + integer1); 455 456 case AML_BIT_AND_OP: /* And (Integer0, Integer1, Result) */ 457 458 return (integer0 & integer1); 459 460 case AML_BIT_NAND_OP: /* NAnd (Integer0, Integer1, Result) */ 461 462 return (~(integer0 & integer1)); 463 464 case AML_BIT_OR_OP: /* Or (Integer0, Integer1, Result) */ 465 466 return (integer0 | integer1); 467 468 case AML_BIT_NOR_OP: /* NOr (Integer0, Integer1, Result) */ 469 470 return (~(integer0 | integer1)); 471 472 case AML_BIT_XOR_OP: /* XOr (Integer0, Integer1, Result) */ 473 474 return (integer0 ^ integer1); 475 476 case AML_MULTIPLY_OP: /* Multiply (Integer0, Integer1, Result) */ 477 478 return (integer0 * integer1); 479 480 case AML_SHIFT_LEFT_OP: /* shift_left (Operand, shift_count, Result) */ 481 482 /* 483 * We need to check if the shiftcount is larger than the integer bit 484 * width since the behavior of this is not well-defined in the C language. 485 */ 486 if (integer1 >= acpi_gbl_integer_bit_width) { 487 return (0); 488 } 489 return (integer0 << integer1); 490 491 case AML_SHIFT_RIGHT_OP: /* shift_right (Operand, shift_count, Result) */ 492 493 /* 494 * We need to check if the shiftcount is larger than the integer bit 495 * width since the behavior of this is not well-defined in the C language. 496 */ 497 if (integer1 >= acpi_gbl_integer_bit_width) { 498 return (0); 499 } 500 return (integer0 >> integer1); 501 502 case AML_SUBTRACT_OP: /* Subtract (Integer0, Integer1, Result) */ 503 504 return (integer0 - integer1); 505 506 default: 507 508 return (0); 509 } 510 } 511 512 /******************************************************************************* 513 * 514 * FUNCTION: acpi_ex_do_logical_numeric_op 515 * 516 * PARAMETERS: opcode - AML opcode 517 * integer0 - Integer operand #0 518 * integer1 - Integer operand #1 519 * logical_result - TRUE/FALSE result of the operation 520 * 521 * RETURN: Status 522 * 523 * DESCRIPTION: Execute a logical "Numeric" AML opcode. For these Numeric 524 * operators (LAnd and LOr), both operands must be integers. 525 * 526 * Note: cleanest machine code seems to be produced by the code 527 * below, rather than using statements of the form: 528 * Result = (Integer0 && Integer1); 529 * 530 ******************************************************************************/ 531 532 acpi_status 533 acpi_ex_do_logical_numeric_op(u16 opcode, 534 u64 integer0, u64 integer1, u8 *logical_result) 535 { 536 acpi_status status = AE_OK; 537 u8 local_result = FALSE; 538 539 ACPI_FUNCTION_TRACE(ex_do_logical_numeric_op); 540 541 switch (opcode) { 542 case AML_LAND_OP: /* LAnd (Integer0, Integer1) */ 543 544 if (integer0 && integer1) { 545 local_result = TRUE; 546 } 547 break; 548 549 case AML_LOR_OP: /* LOr (Integer0, Integer1) */ 550 551 if (integer0 || integer1) { 552 local_result = TRUE; 553 } 554 break; 555 556 default: 557 558 status = AE_AML_INTERNAL; 559 break; 560 } 561 562 /* Return the logical result and status */ 563 564 *logical_result = local_result; 565 return_ACPI_STATUS(status); 566 } 567 568 /******************************************************************************* 569 * 570 * FUNCTION: acpi_ex_do_logical_op 571 * 572 * PARAMETERS: opcode - AML opcode 573 * operand0 - operand #0 574 * operand1 - operand #1 575 * logical_result - TRUE/FALSE result of the operation 576 * 577 * RETURN: Status 578 * 579 * DESCRIPTION: Execute a logical AML opcode. The purpose of having all of the 580 * functions here is to prevent a lot of pointer dereferencing 581 * to obtain the operands and to simplify the generation of the 582 * logical value. For the Numeric operators (LAnd and LOr), both 583 * operands must be integers. For the other logical operators, 584 * operands can be any combination of Integer/String/Buffer. The 585 * first operand determines the type to which the second operand 586 * will be converted. 587 * 588 * Note: cleanest machine code seems to be produced by the code 589 * below, rather than using statements of the form: 590 * Result = (Operand0 == Operand1); 591 * 592 ******************************************************************************/ 593 594 acpi_status 595 acpi_ex_do_logical_op(u16 opcode, 596 union acpi_operand_object *operand0, 597 union acpi_operand_object *operand1, u8 * logical_result) 598 { 599 union acpi_operand_object *local_operand1 = operand1; 600 u64 integer0; 601 u64 integer1; 602 u32 length0; 603 u32 length1; 604 acpi_status status = AE_OK; 605 u8 local_result = FALSE; 606 int compare; 607 608 ACPI_FUNCTION_TRACE(ex_do_logical_op); 609 610 /* 611 * Convert the second operand if necessary. The first operand 612 * determines the type of the second operand, (See the Data Types 613 * section of the ACPI 3.0+ specification.) Both object types are 614 * guaranteed to be either Integer/String/Buffer by the operand 615 * resolution mechanism. 616 */ 617 switch (operand0->common.type) { 618 case ACPI_TYPE_INTEGER: 619 620 status = 621 acpi_ex_convert_to_integer(operand1, &local_operand1, 16); 622 break; 623 624 case ACPI_TYPE_STRING: 625 626 status = 627 acpi_ex_convert_to_string(operand1, &local_operand1, 628 ACPI_IMPLICIT_CONVERT_HEX); 629 break; 630 631 case ACPI_TYPE_BUFFER: 632 633 status = acpi_ex_convert_to_buffer(operand1, &local_operand1); 634 break; 635 636 default: 637 638 status = AE_AML_INTERNAL; 639 break; 640 } 641 642 if (ACPI_FAILURE(status)) { 643 goto cleanup; 644 } 645 646 /* 647 * Two cases: 1) Both Integers, 2) Both Strings or Buffers 648 */ 649 if (operand0->common.type == ACPI_TYPE_INTEGER) { 650 /* 651 * 1) Both operands are of type integer 652 * Note: local_operand1 may have changed above 653 */ 654 integer0 = operand0->integer.value; 655 integer1 = local_operand1->integer.value; 656 657 switch (opcode) { 658 case AML_LEQUAL_OP: /* LEqual (Operand0, Operand1) */ 659 660 if (integer0 == integer1) { 661 local_result = TRUE; 662 } 663 break; 664 665 case AML_LGREATER_OP: /* LGreater (Operand0, Operand1) */ 666 667 if (integer0 > integer1) { 668 local_result = TRUE; 669 } 670 break; 671 672 case AML_LLESS_OP: /* LLess (Operand0, Operand1) */ 673 674 if (integer0 < integer1) { 675 local_result = TRUE; 676 } 677 break; 678 679 default: 680 681 status = AE_AML_INTERNAL; 682 break; 683 } 684 } else { 685 /* 686 * 2) Both operands are Strings or both are Buffers 687 * Note: Code below takes advantage of common Buffer/String 688 * object fields. local_operand1 may have changed above. Use 689 * memcmp to handle nulls in buffers. 690 */ 691 length0 = operand0->buffer.length; 692 length1 = local_operand1->buffer.length; 693 694 /* Lexicographic compare: compare the data bytes */ 695 696 compare = memcmp(operand0->buffer.pointer, 697 local_operand1->buffer.pointer, 698 (length0 > length1) ? length1 : length0); 699 700 switch (opcode) { 701 case AML_LEQUAL_OP: /* LEqual (Operand0, Operand1) */ 702 703 /* Length and all bytes must be equal */ 704 705 if ((length0 == length1) && (compare == 0)) { 706 707 /* Length and all bytes match ==> TRUE */ 708 709 local_result = TRUE; 710 } 711 break; 712 713 case AML_LGREATER_OP: /* LGreater (Operand0, Operand1) */ 714 715 if (compare > 0) { 716 local_result = TRUE; 717 goto cleanup; /* TRUE */ 718 } 719 if (compare < 0) { 720 goto cleanup; /* FALSE */ 721 } 722 723 /* Bytes match (to shortest length), compare lengths */ 724 725 if (length0 > length1) { 726 local_result = TRUE; 727 } 728 break; 729 730 case AML_LLESS_OP: /* LLess (Operand0, Operand1) */ 731 732 if (compare > 0) { 733 goto cleanup; /* FALSE */ 734 } 735 if (compare < 0) { 736 local_result = TRUE; 737 goto cleanup; /* TRUE */ 738 } 739 740 /* Bytes match (to shortest length), compare lengths */ 741 742 if (length0 < length1) { 743 local_result = TRUE; 744 } 745 break; 746 747 default: 748 749 status = AE_AML_INTERNAL; 750 break; 751 } 752 } 753 754 cleanup: 755 756 /* New object was created if implicit conversion performed - delete */ 757 758 if (local_operand1 != operand1) { 759 acpi_ut_remove_reference(local_operand1); 760 } 761 762 /* Return the logical result and status */ 763 764 *logical_result = local_result; 765 return_ACPI_STATUS(status); 766 } 767