blob: e6277c940860b8b301a3bd8cc683e2737b73442b [file] [log] [blame]
wdenk47d1a6e2002-11-03 00:01:44 +00001/*
2 * BedBug Functions
3 */
4
5#include <common.h>
6#include <command.h>
7#include <linux/ctype.h>
8#include <net.h>
wdenk8bde7f72003-06-27 21:31:46 +00009#include <bedbug/type.h>
wdenk47d1a6e2002-11-03 00:01:44 +000010#include <bedbug/bedbug.h>
11#include <bedbug/regs.h>
12#include <bedbug/ppc.h>
wdenk47d1a6e2002-11-03 00:01:44 +000013
Wolfgang Denkd87080b2006-03-31 18:32:53 +020014DECLARE_GLOBAL_DATA_PTR;
15
Wolfgang Denkd87080b2006-03-31 18:32:53 +020016extern void show_regs __P ((struct pt_regs *));
17extern int run_command __P ((const char *, int));
wdenk47d1a6e2002-11-03 00:01:44 +000018extern char console_buffer[];
19
Wolfgang Denkd87080b2006-03-31 18:32:53 +020020ulong dis_last_addr = 0; /* Last address disassembled */
21ulong dis_last_len = 20; /* Default disassembler length */
22CPU_DEBUG_CTX bug_ctx; /* Bedbug context structure */
wdenk47d1a6e2002-11-03 00:01:44 +000023
Wolfgang Denkd87080b2006-03-31 18:32:53 +020024
wdenk47d1a6e2002-11-03 00:01:44 +000025/* ======================================================================
26 * U-Boot's puts function does not append a newline, so the bedbug stuff
27 * will use this for the output of the dis/assembler.
28 * ====================================================================== */
29
Wolfgang Denkd87080b2006-03-31 18:32:53 +020030int bedbug_puts (const char *str)
wdenk47d1a6e2002-11-03 00:01:44 +000031{
Wolfgang Denkd87080b2006-03-31 18:32:53 +020032 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +000033
Wolfgang Denkd87080b2006-03-31 18:32:53 +020034 printf ("%s\r\n", str);
35 return 0;
36} /* bedbug_puts */
wdenk47d1a6e2002-11-03 00:01:44 +000037
Wolfgang Denkd87080b2006-03-31 18:32:53 +020038
39
wdenk47d1a6e2002-11-03 00:01:44 +000040/* ======================================================================
41 * Initialize the bug_ctx structure used by the bedbug debugger. This is
42 * specific to the CPU since each has different debug registers and
43 * settings.
44 * ====================================================================== */
45
Wolfgang Denkd87080b2006-03-31 18:32:53 +020046void bedbug_init (void)
wdenk47d1a6e2002-11-03 00:01:44 +000047{
Wolfgang Denkd87080b2006-03-31 18:32:53 +020048 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +000049
50#if defined(CONFIG_4xx)
Wolfgang Denkd87080b2006-03-31 18:32:53 +020051 void bedbug405_init (void);
52
53 bedbug405_init ();
wdenkd126bfb2003-04-10 11:18:18 +000054#elif defined(CONFIG_8xx)
Wolfgang Denkd87080b2006-03-31 18:32:53 +020055 void bedbug860_init (void);
56
57 bedbug860_init ();
wdenk47d1a6e2002-11-03 00:01:44 +000058#endif
59
60#if defined(CONFIG_MPC824X) || defined(CONFIG_MPC8260)
Wolfgang Denkd87080b2006-03-31 18:32:53 +020061 /* Processors that are 603e core based */
62 void bedbug603e_init (void);
wdenk47d1a6e2002-11-03 00:01:44 +000063
Wolfgang Denkd87080b2006-03-31 18:32:53 +020064 bedbug603e_init ();
wdenk47d1a6e2002-11-03 00:01:44 +000065#endif
66
Wolfgang Denkd87080b2006-03-31 18:32:53 +020067 return;
68} /* bedbug_init */
wdenk47d1a6e2002-11-03 00:01:44 +000069
Wolfgang Denkd87080b2006-03-31 18:32:53 +020070
71
wdenk47d1a6e2002-11-03 00:01:44 +000072/* ======================================================================
73 * Entry point from the interpreter to the disassembler. Repeated calls
74 * will resume from the last disassembled address.
75 * ====================================================================== */
Wolfgang Denkd87080b2006-03-31 18:32:53 +020076int do_bedbug_dis (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenk47d1a6e2002-11-03 00:01:44 +000077{
Wolfgang Denkd87080b2006-03-31 18:32:53 +020078 ulong addr; /* Address to start disassembly from */
79 ulong len; /* # of instructions to disassemble */
wdenk47d1a6e2002-11-03 00:01:44 +000080
Wolfgang Denkd87080b2006-03-31 18:32:53 +020081 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +000082
Wolfgang Denkd87080b2006-03-31 18:32:53 +020083 /* Setup to go from the last address if none is given */
84 addr = dis_last_addr;
85 len = dis_last_len;
wdenk47d1a6e2002-11-03 00:01:44 +000086
Wolfgang Denkd87080b2006-03-31 18:32:53 +020087 if (argc < 2) {
88 printf ("Usage:\n%s\n", cmdtp->usage);
89 return 1;
90 }
wdenk47d1a6e2002-11-03 00:01:44 +000091
Wolfgang Denkd87080b2006-03-31 18:32:53 +020092 if ((flag & CMD_FLAG_REPEAT) == 0) {
93 /* New command */
94 addr = simple_strtoul (argv[1], NULL, 16);
wdenk47d1a6e2002-11-03 00:01:44 +000095
Wolfgang Denkd87080b2006-03-31 18:32:53 +020096 /* If an extra param is given then it is the length */
97 if (argc > 2)
98 len = simple_strtoul (argv[2], NULL, 16);
99 }
wdenk47d1a6e2002-11-03 00:01:44 +0000100
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200101 /* Run the disassembler */
102 disppc ((unsigned char *) addr, 0, len, bedbug_puts, F_RADHEX);
103
104 dis_last_addr = addr + (len * 4);
105 dis_last_len = len;
106 return 0;
107} /* do_bedbug_dis */
108
109U_BOOT_CMD (ds, 3, 1, do_bedbug_dis,
110 "ds - disassemble memory\n",
111 "ds <address> [# instructions]\n");
wdenk47d1a6e2002-11-03 00:01:44 +0000112
113/* ======================================================================
114 * Entry point from the interpreter to the assembler. Assembles
115 * instructions in consecutive memory locations until a '.' (period) is
116 * entered on a line by itself.
117 * ====================================================================== */
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200118int do_bedbug_asm (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenk47d1a6e2002-11-03 00:01:44 +0000119{
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200120 long mem_addr; /* Address to assemble into */
121 unsigned long instr; /* Machine code for text */
122 char prompt[15]; /* Prompt string for user input */
123 int asm_err; /* Error code from the assembler */
wdenk47d1a6e2002-11-03 00:01:44 +0000124
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200125 /* -------------------------------------------------- */
126 int rcode = 0;
wdenk47d1a6e2002-11-03 00:01:44 +0000127
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200128 if (argc < 2) {
129 printf ("Usage:\n%s\n", cmdtp->usage);
130 return 1;
131 }
wdenk47d1a6e2002-11-03 00:01:44 +0000132
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200133 printf ("\nEnter '.' when done\n");
134 mem_addr = simple_strtoul (argv[1], NULL, 16);
wdenk47d1a6e2002-11-03 00:01:44 +0000135
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200136 while (1) {
137 putc ('\n');
138 disppc ((unsigned char *) mem_addr, 0, 1, bedbug_puts,
139 F_RADHEX);
wdenk47d1a6e2002-11-03 00:01:44 +0000140
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200141 sprintf (prompt, "%08lx: ", mem_addr);
142 readline (prompt);
143
144 if (console_buffer[0] && strcmp (console_buffer, ".")) {
145 if ((instr =
146 asmppc (mem_addr, console_buffer,
147 &asm_err)) != 0) {
148 *(unsigned long *) mem_addr = instr;
149 mem_addr += 4;
150 } else {
151 printf ("*** Error: %s ***\n",
152 asm_error_str (asm_err));
153 rcode = 1;
154 }
155 } else {
156 break;
157 }
158 }
159 return rcode;
160} /* do_bedbug_asm */
161
162U_BOOT_CMD (as, 2, 0, do_bedbug_asm,
163 "as - assemble memory\n", "as <address>\n");
wdenk47d1a6e2002-11-03 00:01:44 +0000164
165/* ======================================================================
166 * Used to set a break point from the interpreter. Simply calls into the
167 * CPU-specific break point set routine.
168 * ====================================================================== */
169
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200170int do_bedbug_break (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenk47d1a6e2002-11-03 00:01:44 +0000171{
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200172 /* -------------------------------------------------- */
173 if (bug_ctx.do_break)
174 (*bug_ctx.do_break) (cmdtp, flag, argc, argv);
175 return 0;
wdenk47d1a6e2002-11-03 00:01:44 +0000176
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200177} /* do_bedbug_break */
178
179U_BOOT_CMD (break, 3, 0, do_bedbug_break,
180 "break - set or clear a breakpoint\n",
181 " - Set or clear a breakpoint\n"
182 "break <address> - Break at an address\n"
183 "break off <bp#> - Disable breakpoint.\n"
184 "break show - List breakpoints.\n");
wdenk47d1a6e2002-11-03 00:01:44 +0000185
186/* ======================================================================
187 * Called from the debug interrupt routine. Simply calls the CPU-specific
188 * breakpoint handling routine.
189 * ====================================================================== */
190
191void do_bedbug_breakpoint (struct pt_regs *regs)
192{
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200193 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +0000194
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200195 if (bug_ctx.break_isr)
196 (*bug_ctx.break_isr) (regs);
wdenk47d1a6e2002-11-03 00:01:44 +0000197
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200198 return;
199} /* do_bedbug_breakpoint */
wdenk47d1a6e2002-11-03 00:01:44 +0000200
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200201
202
wdenk47d1a6e2002-11-03 00:01:44 +0000203/* ======================================================================
204 * Called from the CPU-specific breakpoint handling routine. Enter a
205 * mini main loop until the stopped flag is cleared from the breakpoint
206 * context.
207 *
208 * This handles the parts of the debugger that are common to all CPU's.
209 * ====================================================================== */
210
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200211void bedbug_main_loop (unsigned long addr, struct pt_regs *regs)
wdenk47d1a6e2002-11-03 00:01:44 +0000212{
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200213 int len; /* Length of command line */
214 int flag; /* Command flags */
215 int rc = 0; /* Result from run_command */
216 char prompt_str[20]; /* Prompt string */
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200217 static char lastcommand[CONFIG_SYS_CBSIZE] = { 0 }; /* previous command */
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200218 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +0000219
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200220 if (bug_ctx.clear)
221 (*bug_ctx.clear) (bug_ctx.current_bp);
wdenk47d1a6e2002-11-03 00:01:44 +0000222
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200223 printf ("Breakpoint %d: ", bug_ctx.current_bp);
224 disppc ((unsigned char *) addr, 0, 1, bedbug_puts, F_RADHEX);
wdenk47d1a6e2002-11-03 00:01:44 +0000225
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200226 bug_ctx.stopped = 1;
227 bug_ctx.regs = regs;
wdenk47d1a6e2002-11-03 00:01:44 +0000228
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200229 sprintf (prompt_str, "BEDBUG.%d =>", bug_ctx.current_bp);
wdenk47d1a6e2002-11-03 00:01:44 +0000230
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200231 /* A miniature main loop */
232 while (bug_ctx.stopped) {
233 len = readline (prompt_str);
wdenk47d1a6e2002-11-03 00:01:44 +0000234
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200235 flag = 0; /* assume no special flags for now */
wdenk47d1a6e2002-11-03 00:01:44 +0000236
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200237 if (len > 0)
238 strcpy (lastcommand, console_buffer);
239 else if (len == 0)
240 flag |= CMD_FLAG_REPEAT;
wdenk47d1a6e2002-11-03 00:01:44 +0000241
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200242 if (len == -1)
243 printf ("<INTERRUPT>\n");
244 else
245 rc = run_command (lastcommand, flag);
wdenk47d1a6e2002-11-03 00:01:44 +0000246
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200247 if (rc <= 0) {
248 /* invalid command or not repeatable, forget it */
249 lastcommand[0] = 0;
250 }
251 }
wdenk47d1a6e2002-11-03 00:01:44 +0000252
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200253 bug_ctx.regs = NULL;
254 bug_ctx.current_bp = 0;
wdenk47d1a6e2002-11-03 00:01:44 +0000255
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200256 return;
257} /* bedbug_main_loop */
wdenk47d1a6e2002-11-03 00:01:44 +0000258
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200259
260
wdenk47d1a6e2002-11-03 00:01:44 +0000261/* ======================================================================
262 * Interpreter command to continue from a breakpoint. Just clears the
263 * stopped flag in the context so that the breakpoint routine will
264 * return.
265 * ====================================================================== */
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200266int do_bedbug_continue (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenk47d1a6e2002-11-03 00:01:44 +0000267{
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200268 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +0000269
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200270 if (!bug_ctx.stopped) {
271 printf ("Not at a breakpoint\n");
272 return 1;
273 }
wdenk47d1a6e2002-11-03 00:01:44 +0000274
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200275 bug_ctx.stopped = 0;
276 return 0;
277} /* do_bedbug_continue */
278
279U_BOOT_CMD (continue, 1, 0, do_bedbug_continue,
280 "continue- continue from a breakpoint\n",
281 " - continue from a breakpoint.\n");
wdenk47d1a6e2002-11-03 00:01:44 +0000282
283/* ======================================================================
284 * Interpreter command to continue to the next instruction, stepping into
285 * subroutines. Works by calling the find_next_addr() routine to compute
286 * the address passes control to the CPU-specific set breakpoint routine
287 * for the current breakpoint number.
288 * ====================================================================== */
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200289int do_bedbug_step (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenk47d1a6e2002-11-03 00:01:44 +0000290{
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200291 unsigned long addr; /* Address to stop at */
wdenk47d1a6e2002-11-03 00:01:44 +0000292
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200293 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +0000294
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200295 if (!bug_ctx.stopped) {
296 printf ("Not at a breakpoint\n");
297 return 1;
298 }
wdenk47d1a6e2002-11-03 00:01:44 +0000299
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200300 if (!find_next_address ((unsigned char *) &addr, FALSE, bug_ctx.regs))
301 return 1;
wdenk47d1a6e2002-11-03 00:01:44 +0000302
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200303 if (bug_ctx.set)
304 (*bug_ctx.set) (bug_ctx.current_bp, addr);
305
306 bug_ctx.stopped = 0;
307 return 0;
308} /* do_bedbug_step */
309
310U_BOOT_CMD (step, 1, 1, do_bedbug_step,
311 "step - single step execution.\n",
312 " - single step execution.\n");
wdenk47d1a6e2002-11-03 00:01:44 +0000313
314/* ======================================================================
315 * Interpreter command to continue to the next instruction, stepping over
316 * subroutines. Works by calling the find_next_addr() routine to compute
317 * the address passes control to the CPU-specific set breakpoint routine
318 * for the current breakpoint number.
319 * ====================================================================== */
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200320int do_bedbug_next (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenk47d1a6e2002-11-03 00:01:44 +0000321{
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200322 unsigned long addr; /* Address to stop at */
wdenk47d1a6e2002-11-03 00:01:44 +0000323
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200324 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +0000325
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200326 if (!bug_ctx.stopped) {
327 printf ("Not at a breakpoint\n");
328 return 1;
329 }
wdenk47d1a6e2002-11-03 00:01:44 +0000330
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200331 if (!find_next_address ((unsigned char *) &addr, TRUE, bug_ctx.regs))
332 return 1;
wdenk47d1a6e2002-11-03 00:01:44 +0000333
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200334 if (bug_ctx.set)
335 (*bug_ctx.set) (bug_ctx.current_bp, addr);
336
337 bug_ctx.stopped = 0;
338 return 0;
339} /* do_bedbug_next */
340
341U_BOOT_CMD (next, 1, 1, do_bedbug_next,
342 "next - single step execution, stepping over subroutines.\n",
343 " - single step execution, stepping over subroutines.\n");
wdenk47d1a6e2002-11-03 00:01:44 +0000344
345/* ======================================================================
346 * Interpreter command to print the current stack. This assumes an EABI
347 * architecture, so it starts with GPR R1 and works back up the stack.
348 * ====================================================================== */
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200349int do_bedbug_stack (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenk47d1a6e2002-11-03 00:01:44 +0000350{
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200351 unsigned long sp; /* Stack pointer */
352 unsigned long func; /* LR from stack */
353 int depth; /* Stack iteration level */
354 int skip = 1; /* Flag to skip the first entry */
355 unsigned long top; /* Top of memory address */
wdenk47d1a6e2002-11-03 00:01:44 +0000356
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200357 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +0000358
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200359 if (!bug_ctx.stopped) {
360 printf ("Not at a breakpoint\n");
361 return 1;
362 }
wdenk47d1a6e2002-11-03 00:01:44 +0000363
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200364 top = gd->bd->bi_memstart + gd->bd->bi_memsize;
365 depth = 0;
wdenk47d1a6e2002-11-03 00:01:44 +0000366
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200367 printf ("Depth PC\n");
368 printf ("----- --------\n");
369 printf ("%5d %08lx\n", depth++, bug_ctx.regs->nip);
wdenk47d1a6e2002-11-03 00:01:44 +0000370
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200371 sp = bug_ctx.regs->gpr[1];
372 func = *(unsigned long *) (sp + 4);
wdenk47d1a6e2002-11-03 00:01:44 +0000373
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200374 while ((func < top) && (sp < top)) {
375 if (!skip)
376 printf ("%5d %08lx\n", depth++, func);
377 else
378 --skip;
wdenk47d1a6e2002-11-03 00:01:44 +0000379
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200380 sp = *(unsigned long *) sp;
381 func = *(unsigned long *) (sp + 4);
382 }
383 return 0;
384} /* do_bedbug_stack */
385
386U_BOOT_CMD (where, 1, 1, do_bedbug_stack,
387 "where - Print the running stack.\n",
388 " - Print the running stack.\n");
wdenk47d1a6e2002-11-03 00:01:44 +0000389
390/* ======================================================================
391 * Interpreter command to dump the registers. Calls the CPU-specific
392 * show registers routine.
393 * ====================================================================== */
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200394int do_bedbug_rdump (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenk47d1a6e2002-11-03 00:01:44 +0000395{
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200396 /* -------------------------------------------------- */
wdenk47d1a6e2002-11-03 00:01:44 +0000397
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200398 if (!bug_ctx.stopped) {
399 printf ("Not at a breakpoint\n");
400 return 1;
401 }
wdenk47d1a6e2002-11-03 00:01:44 +0000402
Wolfgang Denkd87080b2006-03-31 18:32:53 +0200403 show_regs (bug_ctx.regs);
404 return 0;
405} /* do_bedbug_rdump */
406
407U_BOOT_CMD (rdump, 1, 1, do_bedbug_rdump,
408 "rdump - Show registers.\n", " - Show registers.\n");
wdenk47d1a6e2002-11-03 00:01:44 +0000409/* ====================================================================== */
wdenk47d1a6e2002-11-03 00:01:44 +0000410
411
412/*
413 * Copyright (c) 2001 William L. Pitts
414 * All rights reserved.
415 *
416 * Redistribution and use in source and binary forms are freely
417 * permitted provided that the above copyright notice and this
418 * paragraph and the following disclaimer are duplicated in all
419 * such forms.
420 *
421 * This software is provided "AS IS" and without any express or
422 * implied warranties, including, without limitation, the implied
423 * warranties of merchantability and fitness for a particular
424 * purpose.
425 */