README.PORT 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. Date: Tue, 11 Jan 94 13:23:11 -0800
  2. From: "pardo@cs.washington.edu" <pardo@meitner.cs.washington.edu>
  3. >[What's needed to get `qt' on an i860-based machine?]
  4. Almost certainly "some assembly required" (pun accepted).
  5. To write a cswap port, you need to understand the context switching
  6. model. Turn to figure 2 in the QT TR. Here's about what the assembly
  7. code looks like to implement that:
  8. qt_cswap:
  9. adjust stack pointer
  10. save callee-save registers on to old's stack
  11. argument register <- old sp
  12. sp <- new sp
  13. (*helper)(args...)
  14. restore callee-save registers from new's stack
  15. unadjust stack pointer
  16. return
  17. Once more in slow motion:
  18. - `old' thread calls context switch routine (new, a0, a1, h)
  19. - cswap routine saves registers that have useful values
  20. - cswap routine switches to new stack
  21. - cswap routine calls helper function (*h)(old, a0, a1)
  22. - when helper returns, cswap routine restores registers
  23. that were saved the last time `new' was suspended
  24. - cswap routine returns to whatever `new' routine called the
  25. context switch routine
  26. There's a few tricks here. First, how do you start a thread running
  27. for the very first time? Answer is: fake some stuff on the stack
  28. so it *looks* like it was called from the middle of some routine.
  29. When the new thread is restarted, it is treated like any other
  30. thread. It just so happens that it's never really run before, but
  31. you can't tell that because the saved state makes it look like like
  32. it's been run. The return pc is set to point at a little stub of
  33. assembly code that loads up registers with the right values and
  34. then calls `only'.
  35. Second, I advise you to forget about varargs routines (at least
  36. until you get single-arg routines up and running).
  37. Third, on most machines `qt_abort' is the same as `qt_cswap' except
  38. that it need not save any callee-save registers.
  39. Fourth, `qt_cswap' needs to save and restore any floating-point
  40. registers that are callee-save (see your processor handbook). On
  41. some machines, *no* floating-point registers are callee-save, so
  42. `qt_cswap' is exactly the same as the integer-only cswap routine.
  43. I suggest staring at the MIPS code for a few minutes. It's "mostly"
  44. generic RISC code, so it gets a lot of the flavor across without
  45. getting too bogged down in little nitty details.
  46. Now for a bit more detail: The stack is laid out to hold callee-save
  47. registers. On many machines, I implemented fp cswap as save fp
  48. regs, call integer cswap, and when integer cswap returns (when the
  49. thread wakes up again), restore fp regs.
  50. For thread startup, I figure out some callee-save registers that
  51. I use to hold parameters to the startup routine (`only'). When
  52. the thread is being started it doesn't have any saved registers
  53. that need to be restored, but I go ahead and let the integer context
  54. switch routine restore some registers then "return" to the stub
  55. code. The stub code then copies the "callee save" registers to
  56. argument registers and calls the startup routine. That keeps the
  57. stub code pretty darn simple.
  58. For each machine I need to know the machine's procedure calling
  59. convention before I write a port. I figure out how many callee-save
  60. registers are there and allocate enough stack space for those
  61. registers. I also figure out how parameters are passed, since I
  62. will need to call the helper function. On most RISC machines, I
  63. just need to put the old sp in the 0'th arg register and then call
  64. indirect through the 3rd arg register; the 1st and 2nd arg registers
  65. are already set up correctly. Likewise, I don't touch the return
  66. value register between the helper's return and the context switch
  67. routine's return.
  68. I have a bunch of macros set up to do the stack initialization.
  69. The easiest way to debug this stuff is to go ahead and write a C
  70. routine to do stack initialization. Once you're happy with it you
  71. can turn it in to a macro.
  72. In general there's a lot of ugly macros, but most of them do simple
  73. things like return constants, etc. Any time you're looking at it
  74. and it looks confusing you just need to remember "this is actually
  75. simple code, the only tricky thing is calling the helper between
  76. the stack switch and the new thread's register restore."
  77. You will almost certainly need to write the assembly code fragment
  78. that starts a thread. You might be able to do a lot of the context
  79. switch code with `setjmp' and `longjmp', if they *happen* to have
  80. the "right" implementation. But getting all the details right (the
  81. helper can return a value to the new thread's cswap routine caller)
  82. is probaby trickier than writing code that does the minimum and
  83. thus doesn't have any extra instructions (or generality) to cause
  84. problems.
  85. I don't know of any ports besides those included with the source
  86. code distribution. If you send me a port I will hapily add it to
  87. the distribution.
  88. Let me know as you have questions and/or comments.
  89. ;-D on ( Now *that*'s a switch... ) Pardo