1 | /* |
---|
2 | * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. |
---|
3 | * |
---|
4 | * This program is free software; you can redistribute it and/or |
---|
5 | * modify it under the terms of the GNU General Public License as |
---|
6 | * published by the Free Software Foundation; either version 2 of the |
---|
7 | * License, or any later version. |
---|
8 | * |
---|
9 | * This program is distributed in the hope that it will be useful, but |
---|
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
12 | * General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU General Public License |
---|
15 | * along with this program; if not, write to the Free Software |
---|
16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
17 | */ |
---|
18 | |
---|
19 | FILE_LICENCE ( GPL2_OR_LATER ); |
---|
20 | |
---|
21 | #include <stdint.h> |
---|
22 | #include <stdlib.h> |
---|
23 | #include <string.h> |
---|
24 | #include <pxe.h> |
---|
25 | #include <realmode.h> |
---|
26 | #include <bios.h> |
---|
27 | #include <pnpbios.h> |
---|
28 | #include <basemem.h> |
---|
29 | #include <gpxe/pci.h> |
---|
30 | #include <undi.h> |
---|
31 | #include <undirom.h> |
---|
32 | #include <undiload.h> |
---|
33 | |
---|
34 | /** @file |
---|
35 | * |
---|
36 | * UNDI load/unload |
---|
37 | * |
---|
38 | */ |
---|
39 | |
---|
40 | /** Parameter block for calling UNDI loader */ |
---|
41 | static struct s_UNDI_LOADER __bss16 ( undi_loader ); |
---|
42 | #define undi_loader __use_data16 ( undi_loader ) |
---|
43 | |
---|
44 | /** UNDI loader entry point */ |
---|
45 | static SEGOFF16_t __bss16 ( undi_loader_entry ); |
---|
46 | #define undi_loader_entry __use_data16 ( undi_loader_entry ) |
---|
47 | |
---|
48 | /** |
---|
49 | * Call UNDI loader to create a pixie |
---|
50 | * |
---|
51 | * @v undi UNDI device |
---|
52 | * @v undirom UNDI ROM |
---|
53 | * @ret rc Return status code |
---|
54 | */ |
---|
55 | int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { |
---|
56 | struct s_PXE ppxe; |
---|
57 | unsigned int fbms_seg; |
---|
58 | uint16_t exit; |
---|
59 | int rc; |
---|
60 | |
---|
61 | /* Set up START_UNDI parameters */ |
---|
62 | memset ( &undi_loader, 0, sizeof ( undi_loader ) ); |
---|
63 | undi_loader.AX = undi->pci_busdevfn; |
---|
64 | undi_loader.BX = undi->isapnp_csn; |
---|
65 | undi_loader.DX = undi->isapnp_read_port; |
---|
66 | undi_loader.ES = BIOS_SEG; |
---|
67 | undi_loader.DI = find_pnp_bios(); |
---|
68 | |
---|
69 | /* Allocate base memory for PXE stack */ |
---|
70 | undi->restore_fbms = get_fbms(); |
---|
71 | fbms_seg = ( undi->restore_fbms << 6 ); |
---|
72 | fbms_seg -= ( ( undirom->code_size + 0x0f ) >> 4 ); |
---|
73 | undi_loader.UNDI_CS = fbms_seg; |
---|
74 | fbms_seg -= ( ( undirom->data_size + 0x0f ) >> 4 ); |
---|
75 | undi_loader.UNDI_DS = fbms_seg; |
---|
76 | |
---|
77 | /* Debug info */ |
---|
78 | DBGC ( undi, "UNDI %p loading UNDI ROM %p to CS %04x DS %04x for ", |
---|
79 | undi, undirom, undi_loader.UNDI_CS, undi_loader.UNDI_DS ); |
---|
80 | if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) { |
---|
81 | unsigned int bus = ( undi->pci_busdevfn >> 8 ); |
---|
82 | unsigned int devfn = ( undi->pci_busdevfn & 0xff ); |
---|
83 | DBGC ( undi, "PCI %02x:%02x.%x\n", |
---|
84 | bus, PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) ); |
---|
85 | } |
---|
86 | if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) { |
---|
87 | DBGC ( undi, "ISAPnP(%04x) CSN %04x\n", |
---|
88 | undi->isapnp_read_port, undi->isapnp_csn ); |
---|
89 | } |
---|
90 | |
---|
91 | /* Call loader */ |
---|
92 | undi_loader_entry = undirom->loader_entry; |
---|
93 | __asm__ __volatile__ ( REAL_CODE ( "pushw %%ds\n\t" |
---|
94 | "pushw %%ax\n\t" |
---|
95 | "lcall *undi_loader_entry\n\t" |
---|
96 | "addw $4, %%sp\n\t" ) |
---|
97 | : "=a" ( exit ) |
---|
98 | : "a" ( __from_data16 ( &undi_loader ) ) |
---|
99 | : "ebx", "ecx", "edx", "esi", "edi", "ebp" ); |
---|
100 | |
---|
101 | /* UNDI API calls may rudely change the status of A20 and not |
---|
102 | * bother to restore it afterwards. Intel is known to be |
---|
103 | * guilty of this. |
---|
104 | * |
---|
105 | * Note that we will return to this point even if A20 gets |
---|
106 | * screwed up by the UNDI driver, because Etherboot always |
---|
107 | * resides in an even megabyte of RAM. |
---|
108 | */ |
---|
109 | gateA20_set(); |
---|
110 | |
---|
111 | if ( exit != PXENV_EXIT_SUCCESS ) { |
---|
112 | rc = -undi_loader.Status; |
---|
113 | if ( rc == 0 ) /* Paranoia */ |
---|
114 | rc = -EIO; |
---|
115 | DBGC ( undi, "UNDI %p loader failed: %s\n", |
---|
116 | undi, strerror ( rc ) ); |
---|
117 | return rc; |
---|
118 | } |
---|
119 | |
---|
120 | /* Populate PXE device structure */ |
---|
121 | undi->pxenv = undi_loader.PXENVptr; |
---|
122 | undi->ppxe = undi_loader.PXEptr; |
---|
123 | copy_from_real ( &ppxe, undi->ppxe.segment, undi->ppxe.offset, |
---|
124 | sizeof ( ppxe ) ); |
---|
125 | undi->entry = ppxe.EntryPointSP; |
---|
126 | DBGC ( undi, "UNDI %p loaded PXENV+ %04x:%04x !PXE %04x:%04x " |
---|
127 | "entry %04x:%04x\n", undi, undi->pxenv.segment, |
---|
128 | undi->pxenv.offset, undi->ppxe.segment, undi->ppxe.offset, |
---|
129 | undi->entry.segment, undi->entry.offset ); |
---|
130 | |
---|
131 | /* Update free base memory counter */ |
---|
132 | undi->fbms = ( fbms_seg >> 6 ); |
---|
133 | set_fbms ( undi->fbms ); |
---|
134 | DBGC ( undi, "UNDI %p using [%d,%d) kB of base memory\n", |
---|
135 | undi, undi->fbms, undi->restore_fbms ); |
---|
136 | |
---|
137 | return 0; |
---|
138 | } |
---|
139 | |
---|
140 | /** |
---|
141 | * Unload a pixie |
---|
142 | * |
---|
143 | * @v undi UNDI device |
---|
144 | * @ret rc Return status code |
---|
145 | * |
---|
146 | * Erases the PXENV+ and !PXE signatures, and frees the used base |
---|
147 | * memory (if possible). |
---|
148 | */ |
---|
149 | int undi_unload ( struct undi_device *undi ) { |
---|
150 | static uint32_t dead = 0xdeaddead; |
---|
151 | |
---|
152 | DBGC ( undi, "UNDI %p unloading\n", undi ); |
---|
153 | |
---|
154 | /* Erase signatures */ |
---|
155 | if ( undi->pxenv.segment ) |
---|
156 | put_real ( dead, undi->pxenv.segment, undi->pxenv.offset ); |
---|
157 | if ( undi->ppxe.segment ) |
---|
158 | put_real ( dead, undi->ppxe.segment, undi->ppxe.offset ); |
---|
159 | |
---|
160 | /* Free base memory, if possible */ |
---|
161 | if ( undi->fbms == get_fbms() ) { |
---|
162 | DBGC ( undi, "UNDI %p freeing [%d,%d) kB of base memory\n", |
---|
163 | undi, undi->fbms, undi->restore_fbms ); |
---|
164 | set_fbms ( undi->restore_fbms ); |
---|
165 | return 0; |
---|
166 | } else { |
---|
167 | DBGC ( undi, "UNDI %p leaking [%d,%d) kB of base memory\n", |
---|
168 | undi, undi->fbms, undi->restore_fbms ); |
---|
169 | return -EBUSY; |
---|
170 | } |
---|
171 | } |
---|