1/* $OpenBSD: subr.S,v 1.32 2024/03/08 16:18:53 miod Exp $ */ 2/* 3 * Mach Operating System 4 * Copyright (c) 1993-1992 Carnegie Mellon University 5 * Copyright (c) 1991 OMRON Corporation 6 * Copyright (c) 1996 Nivas Madhur 7 * Copyright (c) 1998 Steve Murphree, Jr. 8 * All Rights Reserved. 9 * 10 * Permission to use, copy, modify and distribute this software and its 11 * documentation is hereby granted, provided that both the copyright 12 * notice and this permission notice appear in all copies of the 13 * software, derivative works or modified versions, and any portions 14 * thereof, and that both notices appear in supporting documentation. 15 * 16 * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" 17 * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND 18 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 19 * 20 * Carnegie Mellon requests users of this software to return to 21 * 22 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 23 * School of Computer Science 24 * Carnegie Mellon University 25 * Pittsburgh PA 15213-3890 26 * 27 * any improvements or extensions that they make and grant Carnegie the 28 * rights to redistribute these changes. 29 */ 30 31#include "assym.h" 32 33#include <sys/errno.h> 34 35#include <machine/param.h> 36#include <machine/asm.h> 37#include <machine/psl.h> 38#include <machine/trap.h> 39 40#ifdef M88100 41 42/* 43 * DO_LOAD_ADDRESS 44 * 45 * unsigned int do_load_word(address, supervisor_mode) 46 * vaddr_t address; \\ in r2 47 * boolean_t supervisor_mode; \\ in r3 48 * 49 * Return the word at ADDRESS (from user space if SUPERVISOR_MODE is zero, 50 * supervisor space if non-zero). 51 * 52 */ 53 54ENTRY(do_load_word) /* do_load_word(address, supervisor) */ 55 bcnd ne0,%r3,1f 56#ifdef ERRATA__XXX_USR 57 NOP 58 ld.usr %r2,%r2,%r0 59 NOP 60 NOP 61 NOP 62 jmp %r1 63#else 64 jmp.n %r1 65 ld.usr %r2,%r2,%r0 66#endif 671: jmp.n %r1 68 ld %r2,%r2,%r0 69 70ENTRY(do_load_half) /* do_load_half(address, supervisor) */ 71 bcnd ne0,%r3,1f 72#ifdef ERRATA__XXX_USR 73 NOP 74 ld.h.usr %r2,%r2,%r0 75 NOP 76 NOP 77 NOP 78 jmp %r1 79#else 80 jmp.n %r1 81 ld.h.usr %r2,%r2,%r0 82#endif 831: jmp.n %r1 84 ld.h %r2,%r2,%r0 85 86ENTRY(do_load_byte) /* do_load_byte(address, supervisor) */ 87 bcnd ne0,%r3,1f 88#ifdef ERRATA__XXX_USR 89 NOP 90 ld.b.usr %r2,%r2,%r0 91 NOP 92 NOP 93 NOP 94 jmp %r1 95#else 96 jmp.n %r1 97 ld.b.usr %r2,%r2,%r0 98#endif 991: jmp.n %r1 100 ld.b %r2,%r2,%r0 101 102ENTRY(do_store_word) /* do_store_word(address, data, supervisor) */ 103 bcnd ne0,%r4,1f 104#ifdef ERRATA__XXX_USR 105 NOP 106 st.usr %r3,%r2,%r0 107 NOP 108 NOP 109 NOP 110 jmp %r1 111#else 112 jmp.n %r1 113 st.usr %r3,%r2,%r0 114#endif 1151: jmp.n %r1 116 st %r3,%r2,%r0 117 118ENTRY(do_store_half) /* do_store_half(address, data, supervisor) */ 119 bcnd ne0,%r4,1f 120#ifdef ERRATA__XXX_USR 121 NOP 122 st.h.usr %r3,%r2,%r0 123 NOP 124 NOP 125 NOP 126 jmp %r1 127#else 128 jmp.n %r1 129 st.h.usr %r3,%r2,%r0 130#endif 1311: jmp.n %r1 132 st.h %r3,%r2,%r0 133 134ENTRY(do_store_byte) /* do_store_byte(address, data, supervisor) */ 135 bcnd ne0,%r4,1f 136#ifdef ERRATA__XXX_USR 137 NOP 138 st.b.usr %r3,%r2,%r0 139 NOP 140 NOP 141 NOP 142 jmp %r1 143#else 144 jmp.n %r1 145 st.b.usr %r3,%r2,%r0 146#endif 1471: jmp.n %r1 148 st.b %r3,%r2,%r0 149 150ENTRY(do_xmem_word) /* do_xmem_word(address, data, supervisor) */ 151 bcnd ne0,%r4,1f 152#ifdef ERRATA__XXX_USR 153 NOP 154#endif 155 xmem.usr %r3,%r2,%r0 156#ifdef ERRATA__XXX_USR 157 NOP 158 NOP 159 NOP 160#endif 161 jmp.n %r1 162 or %r2, %r3, %r0 1631: xmem %r3,%r2,%r0 164 jmp.n %r1 165 or %r2, %r3, %r0 166 167ENTRY(do_xmem_byte) /* do_xmem_byte(address, data, supervisor) */ 168 bcnd ne0,%r4,1f 169#ifdef ERRATA__XXX_USR 170 NOP 171#endif 172 xmem.bu.usr %r3,%r2,%r0 173#ifdef ERRATA__XXX_USR 174 NOP 175 NOP 176 NOP 177#endif 178 jmp.n %r1 179 or %r2,%r3,%r0 1801: xmem.bu %r3,%r2,%r0 181 jmp.n %r1 182 or %r2,%r3,%r0 183 184#endif /* M88100 */ 185 186/* 187 * Copy specified amount of data from user space into the kernel 188 * _copyin(from, to, len) 189 * r2 == from (user source address) 190 * r3 == to (kernel destination address) 191 * r4 == length 192 */ 193 194#define SRC %r2 195#define DEST %r3 196#define LEN %r4 197 198ENTRY(_copyin) 199 /* set up fault handler */ 200 ldcr %r5, CPU 201 ld %r6, %r5, CI_CURPCB 202 or.u %r5, %r0, %hi16(Lciflt) 203 or %r5, %r5, %lo16(Lciflt) 204 st %r5, %r6, PCB_ONFAULT /* pcb_onfault = Lciflt */ 205 206 /* 207 * If it's a small length (less than 8), then do byte-by-byte. 208 * Despite not being optimal if len is 4, and from and to 209 * are word-aligned, this is still faster than doing more tests 210 * to save an hyperthetical fraction of cycle. 211 */ 212 cmp %r9, LEN, 8 213 bb1 lt, %r9, copyin_byte_only 214 215 /* If they're not aligned similarly, use byte only... */ 216 xor %r9, SRC, DEST 217 mask %r8, %r9, 0x3 218 bcnd ne0, %r8, copyin_byte_only 219 220 /* 221 * At this point, we don't know if they're word aligned or not, 222 * but we know that what needs to be done to one to align 223 * it is what's needed for the other. 224 */ 225 bb1 0, SRC, copyin_left_align_to_halfword 226ASLOCAL(copyin_left_aligned_to_halfword) 227 bb1 1, SRC, copyin_left_align_to_word 228ASLOCAL(copyin_left_aligned_to_word) 229 bb1 0, LEN, copyin_right_align_to_halfword 230ASLOCAL(copyin_right_aligned_to_halfword) 231 bb1 1, LEN, copyin_right_align_to_word 232ASLOCAL(copyin_right_aligned_to_word) 233 234 /* 235 * At this point, both SRC and DEST are aligned to a word 236 * boundary, and LEN is a multiple of 4. We want it an even 237 * multiple of 4. 238 */ 239 bb1.n 2, LEN, copyin_right_align_to_doubleword 240 or %r7, %r0, 4 241 242ASLOCAL(copyin_right_aligned_to_doubleword) 243#ifdef ERRATA__XXX_USR 244 NOP 245 ld.usr %r5, SRC, %r0 246 NOP 247 NOP 248 NOP 249 ld.usr %r6, SRC, %r7 250 NOP 251 NOP 252 NOP 253#else 254 ld.usr %r5, SRC, %r0 255 ld.usr %r6, SRC, %r7 256#endif 257 subu LEN, LEN, 8 258 st %r5, DEST, %r0 259 addu SRC, SRC, 8 260 st %r6, DEST, %r7 261 bcnd.n ne0, LEN, copyin_right_aligned_to_doubleword 262 addu DEST, DEST, 8 263 br.n Lcidone 264 or %r2, %r0, %r0 /* successful return */ 265 266ASLOCAL(copyin_left_align_to_halfword) 267#ifdef ERRATA__XXX_USR 268 NOP 269 ld.b.usr %r5, SRC, %r0 270 NOP 271 NOP 272 NOP 273#else 274 ld.b.usr %r5, SRC, %r0 275#endif 276 subu LEN, LEN, 1 277 st.b %r5, DEST, %r0 278 addu SRC, SRC, 1 279 br.n copyin_left_aligned_to_halfword 280 addu DEST, DEST, 1 281 282ASLOCAL(copyin_left_align_to_word) 283#ifdef ERRATA__XXX_USR 284 NOP 285 ld.h.usr %r5, SRC, %r0 286 NOP 287 NOP 288 NOP 289#else 290 ld.h.usr %r5, SRC, %r0 291#endif 292 subu LEN, LEN, 2 293 st.h %r5, DEST, %r0 294 addu SRC, SRC, 2 295 br.n copyin_left_aligned_to_word 296 addu DEST, DEST, 2 297 298ASLOCAL(copyin_right_align_to_halfword) 299 subu LEN, LEN, 1 300#ifdef ERRATA__XXX_USR 301 NOP 302 ld.b.usr %r5, SRC, LEN 303 NOP 304 NOP 305 NOP 306#else 307 ld.b.usr %r5, SRC, LEN 308#endif 309 br.n copyin_right_aligned_to_halfword 310 st.b %r5, DEST, LEN 311 312ASLOCAL(copyin_right_align_to_word) 313 subu LEN, LEN, 2 314#ifdef ERRATA__XXX_USR 315 NOP 316 ld.h.usr %r5, SRC, LEN 317 NOP 318 NOP 319 NOP 320#else 321 ld.h.usr %r5, SRC, LEN 322#endif 323 br.n copyin_right_aligned_to_word 324 st.h %r5, DEST, LEN 325 326ASLOCAL(copyin_right_align_to_doubleword) 327 subu LEN, LEN, 4 328#ifdef ERRATA__XXX_USR 329 NOP 330 ld.usr %r5, SRC, LEN 331 NOP 332 NOP 333 NOP 334#else 335 ld.usr %r5, SRC, LEN 336#endif 337 bcnd.n ne0, LEN, copyin_right_aligned_to_doubleword 338 st %r5, DEST, LEN 339 br.n Lcidone 340 or %r2, %r0, %r0 /* successful return */ 341 342ASLOCAL(copyin_byte_only) 343 bcnd eq0, LEN, 2f 3441: 345 subu LEN, LEN, 1 346#ifdef ERRATA__XXX_USR 347 NOP 348 ld.b.usr %r5, SRC, LEN 349 NOP 350 NOP 351 NOP 352#else 353 ld.b.usr %r5, SRC, LEN 354#endif 355 bcnd.n ne0, LEN, 1b 356 st.b %r5, DEST, LEN 3572: 358 or %r2, %r0, %r0 /* successful return */ 359 /* FALLTHROUGH */ 360 361ASLOCAL(Lcidone) 362 ldcr %r5, CPU 363 ld %r6, %r5, CI_CURPCB 364 jmp.n %r1 365 st %r0, %r6, PCB_ONFAULT 366 367ASLOCAL(Lciflt) 368 br.n Lcidone 369 or %r2, %r0, EFAULT /* return fault */ 370 371#undef SRC 372#undef DEST 373#undef LEN 374 375/* 376 * Specific flavour for a single 32-bit word copy. 377 * copyin32(from, to) 378 * r2 == from (user source address) 379 * r3 == to (kernel destination address) 380 */ 381 382#define SRC %r2 383#define DEST %r3 384 385ENTRY(copyin32) 386 /* check for source alignment */ 387 mask %r8, SRC, 0x3 388 bcnd ne0, %r8, copyin32_misaligned 389 390 /* set up fault handler */ 391 ldcr %r5, CPU 392 ld %r6, %r5, CI_CURPCB 393 or.u %r5, %r0, %hi16(Lciflt) 394 or %r5, %r5, %lo16(Lciflt) 395 st %r5, %r6, PCB_ONFAULT /* pcb_onfault = Lciflt */ 396 397#ifdef ERRATA__XXX_USR 398 NOP 399 ld.usr %r5, SRC, %r0 400 NOP 401 NOP 402 NOP 403#else 404 ld.usr %r5, SRC, %r0 405#endif 406 st %r5, DEST, %r0 407 br.n Lcidone 408 or %r2, %r0, %r0 /* successful return */ 409 410ASLOCAL(copyin32_misaligned) 411 jmp.n %r1 412 or %r2, %r0, EFAULT /* return fault */ 413 414#undef SRC 415#undef DEST 416 417/*######################################################################*/ 418/*######################################################################*/ 419 420/* 421 * Copy a null terminated string from the user space to the kernel 422 * address space. 423 * 424 * _copyinstr(from, to, maxlen, &lencopied) 425 * r2 == from 426 * r3 == to 427 * r4 == maxlen 428 * r5 == len actually transferred (includes the terminating NUL!!!) 429 * r6 & r7 - used as temporaries 430 */ 431#define SRC %r2 432#define DEST %r3 433#define CNT %r4 434#define LEN %r5 435 436ENTRY(_copyinstr) 437 438 /* setup fault handler */ 439 ldcr %r6, CPU 440 ld %r7, %r6, CI_CURPCB 441 or.u %r6, %r0, %hi16(Lcisflt) 442 or %r6, %r6, %lo16(Lcisflt) 443 st %r6, %r7, PCB_ONFAULT 444 or %r6, %r0, 0 445 bcnd lt0, CNT, Lcisflt 446 bcnd eq0, CNT, Lcistoolong 4471: 448#ifdef ERRATA__XXX_USR 449 NOP 450 ld.bu.usr %r7, SRC, %r6 451 NOP 452 NOP 453 NOP 454#else 455 ld.bu.usr %r7, SRC, %r6 456#endif 457 st.b %r7, DEST, %r6 458 bcnd.n eq0, %r7, 2f /* all done */ 459 addu %r6, %r6, 1 460 cmp %r7, %r6, CNT 461 bb1 lt, %r7, 1b 462 463ASLOCAL(Lcistoolong) 464 or %r2, %r0, ENAMETOOLONG /* overflow */ 465 466ASLOCAL(Lcisnull) 467 bcnd eq0,%r6, Lcisdone /* do not attempt to clear last byte */ 468 /* if we did not write to the string */ 469 subu %r6, %r6, 1 470 st.b %r0, DEST, %r6 /* clear last byte */ 471 br.n Lcisdone 472 addu %r6, %r6, 1 4732: /* all done */ 474 or %r2, %r0, 0 475 476ASLOCAL(Lcisdone) 477 bcnd eq0, LEN, 3f 478 st %r6, %r0, LEN 4793: 480 ldcr %r5, CPU 481 ld %r6, %r5, CI_CURPCB 482 jmp.n %r1 483 st %r0, %r6, PCB_ONFAULT /* clear the handler */ 484 485ASLOCAL(Lcisflt) 486 br.n Lcisnull 487 or %r2, %r0, EFAULT /* return fault */ 488 489#undef SRC 490#undef DEST 491#undef CNT 492#undef LEN 493 494/* 495 * Copy specified amount of data from kernel to the user space 496 * Copyout(from, to, len) 497 * r2 == from (kernel source address) 498 * r3 == to (user destination address) 499 * r4 == length 500 */ 501 502#define SRC %r2 503#define DEST %r3 504#define LEN %r4 505 506ENTRY(copyout) 507 /* setup fault handler */ 508 ldcr %r5, CPU 509 ld %r6, %r5, CI_CURPCB 510 or.u %r5, %r0, %hi16(Lcoflt) 511 or %r5, %r5, %lo16(Lcoflt) 512 st %r5, %r6, PCB_ONFAULT /* pcb_onfault = Lcoflt */ 513 514 /* 515 * If it's a small length (less than 8), then do byte-by-byte. 516 * Despite not being optimal if len is 4, and from and to 517 * are word-aligned, this is still faster than doing more tests 518 * to save an hyperthetical fraction of cycle. 519 */ 520 cmp %r9, LEN, 8 521 bb1 lt, %r9, copyout_byte_only 522 523 /* If they're not aligned similarly, use byte only... */ 524 xor %r9, SRC, DEST 525 mask %r8, %r9, 0x3 526 bcnd ne0, %r8, copyout_byte_only 527 528 /* 529 * At this point, we don't know if they're word aligned or not, 530 * but we know that what needs to be done to one to align 531 * it is what's needed for the other. 532 */ 533 bb1 0, SRC, copyout_left_align_to_halfword 534ASLOCAL(copyout_left_aligned_to_halfword) 535 bb1 1, SRC, copyout_left_align_to_word 536ASLOCAL(copyout_left_aligned_to_word) 537 bb1 0, LEN, copyout_right_align_to_halfword 538ASLOCAL(copyout_right_aligned_to_halfword) 539 bb1 1, LEN, copyout_right_align_to_word 540ASLOCAL(copyout_right_aligned_to_word) 541 542 /* 543 * At this point, both SRC and DEST are aligned to a word 544 * boundary, and LEN is a multiple of 4. We want it an even 545 * multiple of 4. 546 */ 547 bb1.n 2, LEN, copyout_right_align_to_doubleword 548 or %r7, %r0, 4 549 550ASLOCAL(copyout_right_aligned_to_doubleword) 551 ld %r5, SRC, %r0 552 ld %r6, SRC, %r7 553 subu LEN, LEN, 8 554#ifdef ERRATA__XXX_USR 555 NOP 556 st.usr %r5, DEST, %r0 557 NOP 558 NOP 559 NOP 560#else 561 st.usr %r5, DEST, %r0 562#endif 563 addu SRC, SRC, 8 564#ifdef ERRATA__XXX_USR 565 NOP 566 st.usr %r6, DEST, %r7 567 NOP 568 NOP 569 NOP 570#else 571 st.usr %r6, DEST, %r7 572#endif 573 bcnd.n ne0, LEN, copyout_right_aligned_to_doubleword 574 addu DEST, DEST, 8 575 or %r2, %r0, %r0 /* successful return */ 576 br Lcodone 577 578 /***************************************************/ 579ASLOCAL(copyout_left_align_to_halfword) 580 ld.b %r5, SRC, %r0 581 subu LEN, LEN, 1 582#ifdef ERRATA__XXX_USR 583 NOP 584 st.b.usr %r5, DEST, %r0 585 NOP 586 NOP 587 NOP 588#else 589 st.b.usr %r5, DEST, %r0 590#endif 591 addu SRC, SRC, 1 592 br.n copyout_left_aligned_to_halfword 593 addu DEST, DEST, 1 594 595ASLOCAL(copyout_left_align_to_word) 596 ld.h %r5, SRC, %r0 597 subu LEN, LEN, 2 598#ifdef ERRATA__XXX_USR 599 NOP 600 st.h.usr %r5, DEST, %r0 601 NOP 602 NOP 603 NOP 604#else 605 st.h.usr %r5, DEST, %r0 606#endif 607 addu SRC, SRC, 2 608 br.n copyout_left_aligned_to_word 609 addu DEST, DEST, 2 610 611ASLOCAL(copyout_right_align_to_halfword) 612 subu LEN, LEN, 1 613 ld.b %r5, SRC, LEN 614#ifdef ERRATA__XXX_USR 615 NOP 616 st.b.usr %r5, DEST, LEN 617 NOP 618 NOP 619 NOP 620 br copyout_right_aligned_to_halfword 621#else 622 br.n copyout_right_aligned_to_halfword 623 st.b.usr %r5, DEST, LEN 624#endif 625 626ASLOCAL(copyout_right_align_to_word) 627 subu LEN, LEN, 2 628 ld.h %r5, SRC, LEN 629#ifdef ERRATA__XXX_USR 630 NOP 631 st.h.usr %r5, DEST, LEN 632 NOP 633 NOP 634 NOP 635 br copyout_right_aligned_to_word 636#else 637 br.n copyout_right_aligned_to_word 638 st.h.usr %r5, DEST, LEN 639#endif 640 641ASLOCAL(copyout_right_align_to_doubleword) 642 subu LEN, LEN, 4 643 ld %r5, SRC, LEN 644#ifdef ERRATA__XXX_USR 645 NOP 646 st.usr %r5, DEST, LEN 647 NOP 648 NOP 649 NOP 650 bcnd ne0, LEN, copyout_right_aligned_to_doubleword 651#else 652 bcnd.n ne0, LEN, copyout_right_aligned_to_doubleword 653 st.usr %r5, DEST, LEN 654#endif 655 br.n Lcodone 656 or %r2, %r0, %r0 /* successful return */ 657 658ASLOCAL(copyout_byte_only) 659 bcnd eq0, LEN, 2f 6601: 661 subu LEN, LEN, 1 662 ld.b %r5, SRC, LEN 663#ifdef ERRATA__XXX_USR 664 NOP 665 st.b.usr %r5, DEST, LEN 666 NOP 667 NOP 668 NOP 669 bcnd ne0, LEN, 1b 670#else 671 bcnd.n ne0, LEN, 1b 672 st.b.usr %r5, DEST, LEN 673#endif 674 6752: 676 or %r2, %r0, %r0 /* successful return */ 677 /* FALLTHROUGH */ 678 679ASLOCAL(Lcodone) 680 ldcr %r5, CPU 681 ld %r6, %r5, CI_CURPCB 682 jmp.n %r1 683 st %r0, %r6, PCB_ONFAULT /* clear the handler */ 684 685ASLOCAL(Lcoflt) 686 br.n Lcodone 687 or %r2, %r0, EFAULT /* return fault */ 688 689#undef SRC 690#undef DEST 691#undef LEN 692 693/* 694 * Copy a null terminated string from the kernel space to the user 695 * address space. 696 * 697 * copyoutstr(from, to, maxlen, &lencopied) 698 * r2 == from 699 * r3 == to 700 * r4 == maxlen that can be copied 701 * r5 == len actually copied (including the terminating NUL!!!) 702 */ 703 704#define SRC %r2 705#define DEST %r3 706#define CNT %r4 707#define LEN %r5 708 709ENTRY(copyoutstr) 710 /* setup fault handler */ 711 ldcr %r6, CPU 712 ld %r7, %r6, CI_CURPCB 713 or.u %r6, %r0, %hi16(Lcosflt) 714 or %r6, %r6, %lo16(Lcosflt) 715 st %r6, %r7, PCB_ONFAULT 716 bcnd lt0, CNT, Lcosflt 717 bcnd eq0, CNT, 2f 718 or %r6, %r0, 0 7191: 720 ld.bu %r7, SRC, %r6 721#ifdef ERRATA__XXX_USR 722 NOP 723 st.b.usr %r7, DEST, %r6 724 NOP 725 NOP 726 NOP 727#else 728 st.b.usr %r7, DEST, %r6 729#endif 730 bcnd.n eq0, %r7, 3f /* all done */ 731 addu %r6, %r6, 1 732 cmp %r7, %r6, CNT 733 bb1 lt, %r7, 1b 7342: 735 br.n Lcosdone 736 or %r2, %r0, ENAMETOOLONG 7373: 738 br.n Lcosdone 739 or %r2, %r0, 0 740 741ASLOCAL(Lcosflt) 742 br.n Lcosdone 743 or %r2, %r0, EFAULT 744 745ASLOCAL(Lcosdone) 746 bcnd eq0, LEN, 3f 747 st %r6, %r0, LEN 7483: 749 ldcr %r5, CPU 750 ld %r6, %r5, CI_CURPCB 751 jmp.n %r1 752 st %r0, %r6, PCB_ONFAULT /* clear the handler */ 753 754#undef SRC 755#undef DEST 756#undef CNT 757#undef LEN 758 759/*######################################################################*/ 760 761/* 762 * kcopy(const void *src, void *dst, size_t len); 763 * 764 * Copy len bytes from src to dst, aborting if we encounter a page fault. 765 */ 766ENTRY(kcopy) 767 subu %r31, %r31, 16 768 ldcr %r5, CPU 769 ld %r6, %r5, CI_CURPCB 770 or.u %r5, %r0, %hi16(kcopy_fault) 771 ld %r7, %r6, PCB_ONFAULT 772 or %r5, %r5, %lo16(kcopy_fault) 773 st %r7, %r31, 0 /* save old pcb_onfault */ 774 st %r5, %r6, PCB_ONFAULT /* pcb_onfault = kcopy_fault */ 775 bcnd le0, %r4, kcopy_out /* nothing to do if <= 0 */ 776/* 777 * check position of source and destination data 778 */ 779 cmp %r9, %r2, %r3 /* compare source address to destination */ 780 bb1 eq, %r9, kcopy_out /* nothing to do if equal */ 781 bb1 lo, %r9, kcopy_reverse /* reverse copy if src < dest */ 782/* 783 * source address is greater than destination address, copy forward 784 */ 785 cmp %r9, %r4, 16 /* see if we have at least 16 bytes */ 786 bb1 lt, %r9, kf_byte_copy /* copy bytes for small length */ 787/* 788 * determine copy strategy based on alignment of source and destination 789 */ 790 mask %r6, %r2, 3 /* get 2 low order bits of source address */ 791 mask %r7, %r3, 3 /* get 2 low order bits of destination addr */ 792 mak %r6, %r6, 0<4>/* convert source bits to table offset */ 793 mak %r7, %r7, 0<2>/* convert destination bits to table offset */ 794 or.u %r12, %r0, %hi16(kf_strat) 795 or %r12, %r12, %lo16(kf_strat) 796 addu %r6, %r6, %r7 /* compute final table offset for strategy */ 797 ld %r12, %r12, %r6 /* load the strategy routine */ 798 jmp %r12 /* branch to strategy routine */ 799 800/* 801 * Copy three bytes from src to destination then copy words 802 */ 803ASLOCAL(kf_3byte_word_copy) 804 ld.bu %r6, %r2, 0 /* load byte from source */ 805 ld.bu %r7, %r2, 1 /* load byte from source */ 806 ld.bu %r8, %r2, 2 /* load byte from source */ 807 st.b %r6, %r3, 0 /* store byte to destination */ 808 st.b %r7, %r3, 1 /* store byte to destination */ 809 st.b %r8, %r3, 2 /* store byte to destination */ 810 addu %r2, %r2, 3 /* increment source pointer */ 811 addu %r3, %r3, 3 /* increment destination pointer */ 812 br.n kf_word_copy/* copy full words */ 813 subu %r4, %r4, 3 /* decrement length */ 814 815/* 816 * Copy 1 halfword from src to destination then copy words 817 */ 818ASLOCAL(kf_1half_word_copy) 819 ld.hu %r6, %r2, 0 /* load half-word from source */ 820 st.h %r6, %r3, 0 /* store half-word to destination */ 821 addu %r2, %r2, 2 /* increment source pointer */ 822 addu %r3, %r3, 2 /* increment destination pointer */ 823 br.n kf_word_copy/* copy full words */ 824 subu %r4, %r4, 2 /* decrement remaining length */ 825 826/* 827 * Copy 1 byte from src to destination then copy words 828 */ 829ASLOCAL(kf_1byte_word_copy) 830 ld.bu %r6, %r2, 0 /* load 1 byte from source */ 831 st.b %r6, %r3, 0 /* store 1 byte to destination */ 832 addu %r2, %r2, 1 /* increment source pointer */ 833 addu %r3, %r3, 1 /* increment destination pointer */ 834 subu %r4, %r4, 1 /* decrement remaining length */ 835 /* fall through to word copy */ 836/* 837 * Copy as many full words as possible, 4 words per loop 838 */ 839ASLOCAL(kf_word_copy) 840 cmp %r10, %r4, 16 /* see if we have 16 bytes remaining */ 841 bb1 lo, %r10, kf_byte_copy /* not enough left, copy bytes */ 842 ld %r6, %r2, 0 /* load first word */ 843 ld %r7, %r2, 4 /* load second word */ 844 ld %r8, %r2, 8 /* load third word */ 845 ld %r9, %r2, 12 /* load fourth word */ 846 st %r6, %r3, 0 /* store first word */ 847 st %r7, %r3, 4 /* store second word */ 848 st %r8, %r3, 8 /* store third word */ 849 st %r9, %r3, 12 /* store fourth word */ 850 addu %r2, %r2, 16 /* increment source pointer */ 851 addu %r3, %r3, 16 /* increment destination pointer */ 852 br.n kf_word_copy/* copy another block */ 853 subu %r4, %r4, 16 /* decrement remaining length */ 854 855ASLOCAL(kf_1byte_half_copy) 856 ld.bu %r6, %r2, 0 /* load 1 byte from source */ 857 st.b %r6, %r3, 0 /* store 1 byte to destination */ 858 addu %r2, %r2, 1 /* increment source pointer */ 859 addu %r3, %r3, 1 /* increment destination pointer */ 860 subu %r4, %r4, 1 /* decrement remaining length */ 861 /* fall through to half copy */ 862 863ASLOCAL(kf_half_copy) 864 cmp %r10, %r4, 16 /* see if we have 16 bytes remaining */ 865 bb1 lo, %r10, kf_byte_copy /* not enough left, copy bytes */ 866 ld.hu %r6, %r2, 0 /* load first half-word */ 867 ld.hu %r7, %r2, 2 /* load second half-word */ 868 ld.hu %r8, %r2, 4 /* load third half-word */ 869 ld.hu %r9, %r2, 6 /* load fourth half-word */ 870 ld.hu %r10, %r2, 8 /* load fifth half-word */ 871 ld.hu %r11, %r2, 10 /* load sixth half-word */ 872 ld.hu %r12, %r2, 12 /* load seventh half-word */ 873 ld.hu %r13, %r2, 14 /* load eighth half-word */ 874 st.h %r6, %r3, 0 /* store first half-word */ 875 st.h %r7, %r3, 2 /* store second half-word */ 876 st.h %r8, %r3, 4 /* store third half-word */ 877 st.h %r9, %r3, 6 /* store fourth half-word */ 878 st.h %r10, %r3, 8 /* store fifth half-word */ 879 st.h %r11, %r3, 10 /* store sixth half-word */ 880 st.h %r12, %r3, 12 /* store seventh half-word */ 881 st.h %r13, %r3, 14 /* store eighth half-word */ 882 addu %r2, %r2, 16 /* increment source pointer */ 883 addu %r3, %r3, 16 /* increment destination pointer */ 884 br.n kf_half_copy/* copy another block */ 885 subu %r4, %r4, 16 /* decrement remaining length */ 886 887ASLOCAL(kf_byte_copy) 888 bcnd eq0, %r4, kcopy_out /* branch if nothing left to copy */ 889 ld.bu %r6, %r2, 0 /* load byte from source */ 890 st.b %r6, %r3, 0 /* store byte in destination */ 891 addu %r2, %r2, 1 /* increment source pointer */ 892 addu %r3, %r3, 1 /* increment destination pointer */ 893 br.n kf_byte_copy/* branch for next byte */ 894 subu %r4, %r4, 1 /* decrement remaining length */ 895 896/* 897 * source address is less than destination address, copy in reverse 898 */ 899ASLOCAL(kcopy_reverse) 900/* 901 * start copy pointers at end of data 902 */ 903 addu %r2, %r2, %r4 /* start source at end of data */ 904 addu %r3, %r3, %r4 /* start destination at end of data */ 905/* 906 * check for short data 907 */ 908 cmp %r9, %r4, 16 /* see if we have at least 16 bytes */ 909 bb1 lt, %r9, kr_byte_copy /* copy bytes for small data length */ 910/* 911 * determine copy strategy based on alignment of source and destination 912 */ 913 mask %r6, %r2, 3 /* get 2 low order bits of source address */ 914 mask %r7, %r3, 3 /* get 2 low order bits of destination addr */ 915 mak %r6, %r6, 0<4>/* convert source bits to table offset */ 916 mak %r7, %r7, 0<2>/* convert destination bits to table offset */ 917 or.u %r12, %r0, %hi16(kr_strat) 918 or %r12, %r12, %lo16(kr_strat) 919 addu %r6, %r6, %r7 /* compute final table offset for strategy */ 920 ld %r12, %r12, %r6 /* load the strategy routine */ 921 jmp %r12 /* branch to strategy routine */ 922 923/* 924 * Copy three bytes from src to destination then copy words 925 */ 926ASLOCAL(kr_3byte_word_copy) 927 subu %r2, %r2, 3 /* decrement source pointer */ 928 subu %r3, %r3, 3 /* decrement destination pointer */ 929 ld.bu %r6, %r2, 0 /* load byte from source */ 930 ld.bu %r7, %r2, 1 /* load byte from source */ 931 ld.bu %r8, %r2, 2 /* load byte from source */ 932 st.b %r6, %r3, 0 /* store byte to destination */ 933 st.b %r7, %r3, 1 /* store byte to destination */ 934 st.b %r8, %r3, 2 /* store byte to destination */ 935 br.n kr_word_copy/* copy full words */ 936 subu %r4, %r4, 3 /* decrement length */ 937 938/* 939 * Copy 1 halfword from src to destination then copy words 940 */ 941ASLOCAL(kr_1half_word_copy) 942 subu %r2, %r2, 2 /* decrement source pointer */ 943 subu %r3, %r3, 2 /* decrement destination pointer */ 944 ld.hu %r6, %r2, 0 /* load half-word from source */ 945 st.h %r6, %r3, 0 /* store half-word to destination */ 946 br.n kr_word_copy/* copy full words */ 947 subu %r4, %r4, 2 /* decrement remaining length */ 948 949/* 950 * Copy 1 byte from src to destination then copy words 951 */ 952ASLOCAL(kr_1byte_word_copy) 953 subu %r2, %r2, 1 /* decrement source pointer */ 954 subu %r3, %r3, 1 /* decrement destination pointer */ 955 ld.bu %r6, %r2, 0 /* load 1 byte from source */ 956 st.b %r6, %r3, 0 /* store 1 byte to destination */ 957 subu %r4, %r4, 1 /* decrement remaining length */ 958 /* fall through to word copy */ 959/* 960 * Copy as many full words as possible, 4 words per loop 961 */ 962ASLOCAL(kr_word_copy) 963 cmp %r10, %r4, 16 /* see if we have 16 bytes remaining */ 964 bb1 lo, %r10, kr_byte_copy /* not enough left, copy bytes */ 965 subu %r2, %r2, 16 /* decrement source pointer */ 966 subu %r3, %r3, 16 /* decrement destination pointer */ 967 ld %r6, %r2, 0 /* load first word */ 968 ld %r7, %r2, 4 /* load second word */ 969 ld %r8, %r2, 8 /* load third word */ 970 ld %r9, %r2, 12 /* load fourth word */ 971 st %r6, %r3, 0 /* store first word */ 972 st %r7, %r3, 4 /* store second word */ 973 st %r8, %r3, 8 /* store third word */ 974 st %r9, %r3, 12 /* store fourth word */ 975 br.n kr_word_copy/* copy another block */ 976 subu %r4, %r4, 16 /* decrement remaining length */ 977 978ASLOCAL(kr_1byte_half_copy) 979 subu %r2, %r2, 1 /* decrement source pointer */ 980 subu %r3, %r3, 1 /* decrement destination pointer */ 981 ld.bu %r6, %r2, 0 /* load 1 byte from source */ 982 st.b %r6, %r3, 0 /* store 1 byte to destination */ 983 subu %r4, %r4, 1 /* decrement remaining length */ 984 /* fall through to half copy */ 985 986ASLOCAL(kr_half_copy) 987 cmp %r10, %r4, 16 /* see if we have 16 bytes remaining */ 988 bb1 lo, %r10, kr_byte_copy /* not enough left, copy bytes */ 989 subu %r2, %r2, 16 /* decrement source pointer */ 990 subu %r3, %r3, 16 /* decrement destination pointer */ 991 ld.hu %r6, %r2, 0 /* load first half-word */ 992 ld.hu %r7, %r2, 2 /* load second half-word */ 993 ld.hu %r8, %r2, 4 /* load third half-word */ 994 ld.hu %r9, %r2, 6 /* load fourth half-word */ 995 ld.hu %r10, %r2, 8 /* load fifth half-word */ 996 ld.hu %r11, %r2, 10 /* load sixth half-word */ 997 ld.hu %r12, %r2, 12 /* load seventh half-word */ 998 ld.hu %r13, %r2, 14 /* load eighth half-word */ 999 st.h %r6, %r3, 0 /* store first half-word */ 1000 st.h %r7, %r3, 2 /* store second half-word */ 1001 st.h %r8, %r3, 4 /* store third half-word */ 1002 st.h %r9, %r3, 6 /* store fourth half-word */ 1003 st.h %r10, %r3, 8 /* store fifth half-word */ 1004 st.h %r11, %r3, 10 /* store sixth half-word */ 1005 st.h %r12, %r3, 12 /* store seventh half-word */ 1006 st.h %r13, %r3, 14 /* store eighth half-word */ 1007 br.n kr_half_copy/* copy another block */ 1008 subu %r4, %r4, 16 /* decrement remaining length */ 1009 1010ASLOCAL(kr_byte_copy) 1011 bcnd eq0, %r4, kcopy_out /* branch if nothing left to copy */ 1012 subu %r2, %r2, 1 /* decrement source pointer */ 1013 subu %r3, %r3, 1 /* decrement destination pointer */ 1014 ld.bu %r6, %r2, 0 /* load byte from source */ 1015 st.b %r6, %r3, 0 /* store byte in destination */ 1016 br.n kr_byte_copy/* branch for next byte */ 1017 subu %r4, %r4, 1 /* decrement remaining length */ 1018 1019ASLOCAL(kcopy_out) 1020 or %r2, %r0, 0 /* return success */ 1021ASLOCAL(kcopy_out_fault) 1022 ldcr %r5, CPU 1023 ld %r7, %r31, 0 1024 ld %r6, %r5, CI_CURPCB 1025 add %r31, %r31, 16 1026 jmp.n %r1 /* all done, return to caller */ 1027 st %r7, %r6, PCB_ONFAULT /* restore previous pcb_onfault */ 1028 1029ASLOCAL(kcopy_fault) 1030 br.n kcopy_out_fault 1031 or %r2, %r0, EFAULT /* return fault */ 1032 1033 .data 1034 .align 2 1035ASLOCAL(kf_strat) 1036 .word kf_word_copy 1037 .word kf_byte_copy 1038 .word kf_half_copy 1039 .word kf_byte_copy 1040 .word kf_byte_copy 1041 .word kf_3byte_word_copy 1042 .word kf_byte_copy 1043 .word kf_1byte_half_copy 1044 .word kf_half_copy 1045 .word kf_byte_copy 1046 .word kf_1half_word_copy 1047 .word kf_byte_copy 1048 .word kf_byte_copy 1049 .word kf_1byte_half_copy 1050 .word kf_byte_copy 1051 .word kf_1byte_word_copy 1052 1053ASLOCAL(kr_strat) 1054 .word kr_word_copy 1055 .word kr_byte_copy 1056 .word kr_half_copy 1057 .word kr_byte_copy 1058 .word kr_byte_copy 1059 .word kr_1byte_word_copy 1060 .word kr_byte_copy 1061 .word kr_1byte_half_copy 1062 .word kr_half_copy 1063 .word kr_byte_copy 1064 .word kr_1half_word_copy 1065 .word kr_byte_copy 1066 .word kr_byte_copy 1067 .word kr_1byte_half_copy 1068 .word kr_byte_copy 1069 .word kr_3byte_word_copy 1070 1071#ifdef DDB 1072/* 1073 * non-local goto 1074 * int setjmp(label_t *); 1075 * void longjmp(label_t*); 1076 */ 1077ENTRY(setjmp) 1078 st %r1, %r2, 0 1079 st %r14, %r2, 4 1080 st %r15, %r2, 2*4 1081 st %r16, %r2, 3*4 1082 st %r17, %r2, 4*4 1083 st %r18, %r2, 5*4 1084 st %r19, %r2, 6*4 1085 st %r20, %r2, 7*4 1086 st %r21, %r2, 8*4 1087 st %r22, %r2, 9*4 1088 st %r23, %r2, 10*4 1089 st %r24, %r2, 11*4 1090 st %r25, %r2, 12*4 1091 st %r26, %r2, 13*4 1092 st %r27, %r2, 14*4 1093 st %r28, %r2, 15*4 1094 st %r29, %r2, 16*4 1095 st %r30, %r2, 17*4 1096 st %r31, %r2, 18*4 1097 jmp.n %r1 1098 or %r2, %r0, %r0 1099 1100ENTRY(longjmp) 1101 ld %r1, %r2, 0 1102 ld %r14, %r2, 4 1103 ld %r15, %r2, 2*4 1104 ld %r16, %r2, 3*4 1105 ld %r17, %r2, 4*4 1106 ld %r18, %r2, 5*4 1107 ld %r19, %r2, 6*4 1108 ld %r20, %r2, 7*4 1109 ld %r21, %r2, 8*4 1110 ld %r22, %r2, 9*4 1111 ld %r23, %r2, 10*4 1112 ld %r24, %r2, 11*4 1113 ld %r25, %r2, 12*4 1114 ld %r26, %r2, 13*4 1115 ld %r27, %r2, 14*4 1116 ld %r28, %r2, 15*4 1117 ld %r29, %r2, 16*4 1118 ld %r30, %r2, 17*4 1119 ld %r31, %r2, 18*4 1120 jmp.n %r1 1121 or %r2, %r0, 1 1122#endif 1123 1124/* 1125 * Signal trampoline code. 1126 * The kernel arranges for the handler to be invoked directly, and return 1127 * here. 1128 */ 1129 .section .rodata 1130 .align 3 1131 .type sigcode,@function 1132GLOBAL(sigcode) /* r31 points to sigframe */ 1133 ld %r2, %r31, 0 /* pick sigcontext* */ 1134 or %r13, %r0, SYS_sigreturn 1135GLOBAL(sigcodecall) 1136GLOBAL(sigcoderet) 1137 tb0 0, %r0, 450 /* syscall trap, calling sigreturn */ 1138 NOP | failure return 1139#ifdef dontbother /* sigreturn will not return unless it fails */ 1140 NOP | success return 1141#endif 1142GLOBAL(esigcode) 1143 /* FALLTHROUGH */ 1144GLOBAL(sigfill) 1145 tb0 0, %r0, 130 /* breakpoint */ 1146GLOBAL(sigfillsiz) 1147 .word sigfillsiz - sigfill 1148 1149/* 1150 * Helper functions for pmap_copy_page() and pmap_zero_page(). 1151 */ 1152 1153#ifdef M88100 1154 1155/* 1156 * void copypage(vaddr_t src, vaddr_t dst); 1157 * 1158 * This copies PAGE_SIZE bytes from src to dst in 32 byte chunks. 1159 */ 1160ENTRY(m8820x_copypage) 1161 addu %r12, %r2, PAGE_SIZE 11621: 1163 ld.d %r4, %r2, 0x00 1164 ld.d %r6, %r2, 0x08 1165 st.d %r4, %r3, 0x00 1166 ld.d %r8, %r2, 0x10 1167 st.d %r6, %r3, 0x08 1168 ld.d %r10, %r2, 0x18 1169 st.d %r8, %r3, 0x10 1170 addu %r2, %r2, 0x20 1171 st.d %r10, %r3, 0x18 1172 cmp %r4, %r2, %r12 1173 bb1.n ne, %r4, 1b 1174 addu %r3, %r3, 0x20 1175 jmp %r1 1176 1177/* 1178 * void zeropage(vaddr_t dst); 1179 * 1180 * This zeroes PAGE_SIZE bytes from src to dst in 64 byte chunks. 1181 */ 1182ENTRY(m8820x_zeropage) 1183 addu %r12, %r2, PAGE_SIZE 1184 or %r3, %r1, %r0 1185 or %r1, %r0, %r0 11861: 1187 st.d %r0, %r2, 0x00 1188 st.d %r0, %r2, 0x08 1189 st.d %r0, %r2, 0x10 1190 st.d %r0, %r2, 0x18 1191 st.d %r0, %r2, 0x20 1192 st.d %r0, %r2, 0x28 1193 st.d %r0, %r2, 0x30 1194 st.d %r0, %r2, 0x38 1195 addu %r2, %r2, 0x40 1196 cmp %r4, %r2, %r12 1197 bb1 ne, %r4, 1b 1198 jmp %r3 1199 1200#endif /* M88100 */ 1201 1202#ifdef M88110 1203 1204/* 1205 * void copypage(vaddr_t src, vaddr_t dst); 1206 * 1207 * This copies PAGE_SIZE bytes from src to dst in 32 byte chunks (one 1208 * cache line). 1209 */ 1210ENTRY(m88110_copypage) 1211 addu %r12, %r2, PAGE_SIZE 12121: 1213 ld.h %r0, %r2, 0x00 | load allocate 1214 ld.d %r4, %r2, 0x00 1215 ld.d %r6, %r2, 0x08 1216 st.d %r4, %r3, 0x00 1217 ld.d %r8, %r2, 0x10 1218 st.d %r6, %r3, 0x08 1219 ld.d %r10, %r2, 0x18 1220 st.d %r8, %r3, 0x10 1221 addu %r2, %r2, 0x20 1222 st.d %r10, %r3, 0x18 1223 cmp %r4, %r2, %r12 1224 addu %r3, %r3, 0x20 1225 bb1 ne, %r4, 1b 1226 jmp %r1 1227 1228/* 1229 * void zeropage(vaddr_t dst); 1230 * 1231 * This zeroes PAGE_SIZE bytes from src to dst in 32 byte chunks. 1232 */ 1233ENTRY(m88110_zeropage) 1234 addu %r12, %r2, PAGE_SIZE 1235 or %r3, %r1, %r0 1236 or %r1, %r0, %r0 12371: 1238 ld.h %r0, %r2, 0x00 | load allocate 1239 st.d %r0, %r2, 0x00 1240 st.d %r0, %r2, 0x08 1241 st.d %r0, %r2, 0x10 1242 st.d %r0, %r2, 0x18 1243 addu %r2, %r2, 0x20 1244 cmp %r4, %r2, %r12 1245 bb1 ne, %r4, 1b 1246 jmp %r3 1247 1248#endif /* M88110 */ 1249 1250/* 1251 * PSR initialization code, invoked from locore on every processor startup. 1252 */ 1253ASENTRY(setup_psr) 1254 ldcr %r2, PID 1255 extu %r3, %r2, 8<8> 1256 1257 /* 1258 * Ensure that the PSR is as we like: 1259 * supervisor mode 1260 * big-endian byte ordering 1261 * concurrent operation allowed 1262 * carry bit clear (I don't think we really care about this) 1263 * FPU enabled 1264 * misaligned access raises an exception 1265 * interrupts disabled 1266 * shadow registers frozen 1267 * 1268 * The manual says not to disable interrupts and freeze shadowing 1269 * at the same time because interrupts are not actually disabled 1270 * until after the next instruction. Well, if an interrupt 1271 * occurs now, we're in deep trouble anyway, so I'm going to do 1272 * the two together. 1273 * 1274 * Upon a reset (or poweron, I guess), the PSR indicates: 1275 * supervisor mode 1276 * interrupts, shadowing, FPU, misaligned exception: all disabled 1277 * 1278 * We'll just construct our own turning on what we want. 1279 * 1280 * jfriedl@omron.co.jp 1281 */ 1282 1283 cmp %r4, %r3, CPU_88110 1284 bb1 eq, %r4, 1f /* if it's a mc88110, skip SSBR */ 1285 stcr %r0, SSBR /* clear this for later */ 12861: 1287 stcr %r0, SR1 /* clear the CPU flags */ 1288 1289 or.u %r2, %r0, %hi16(KERNEL_PSR) 1290 or %r2, %r2, %lo16(KERNEL_PSR) 1291 stcr %r2, PSR 1292 FLUSH_PIPELINE 1293 1294 jmp %r1 1295 1296/* 1297 * Update the VBR value. 1298 * This needs to be done with interrupts and shadowing disabled. 1299 */ 1300GLOBAL(set_vbr) 1301 ldcr %r3, PSR 1302 set %r4, %r3, 1<PSR_INTERRUPT_DISABLE_BIT> 1303 set %r4, %r4, 1<PSR_SHADOW_FREEZE_BIT> 1304 stcr %r4, PSR 1305 FLUSH_PIPELINE 1306 1307 stcr %r2, VBR 1308 FLUSH_PIPELINE 1309 1310#if defined(M88100) && defined(M88110) 1311 ldcr %r2, PID 1312 extu %r5, %r2, 8<8> 1313 cmp %r4, %r5, CPU_88110 1314 bb1 eq, %r4, 1f 1315#endif 1316#ifdef M88100 1317 /* 88100 */ 1318 stcr %r3, PSR 1319 FLUSH_PIPELINE 1320 jmp %r1 1321#endif 1322#ifdef M88110 13231: 1324 /* 88110 */ 1325 stcr %r1, EXIP 1326 stcr %r3, EPSR 1327 RTE 1328#endif 1329