1 /*
2  *  FreeLoader
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  *  Note: much of this code was based on knowledge and/or code developed
19  *  by the Xbox Linux group: http://www.xbox-linux.org
20  */
21 
22 #include <freeldr.h>
23 #include <debug.h>
24 
25 DBG_DEFAULT_CHANNEL(MEMORY);
26 
27 static ULONG InstalledMemoryMb = 0;
28 static ULONG AvailableMemoryMb = 0;
29 extern multiboot_info_t * MultibootInfoPtr;
30 extern ULONG NvBase;
31 extern PVOID FrameBuffer;
32 extern ULONG FrameBufferSize;
33 
34 #define TEST_SIZE     0x200
35 #define TEST_PATTERN1 0xAA
36 #define TEST_PATTERN2 0x55
37 
38 extern VOID
39 SetMemory(
40     PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
41     ULONG_PTR BaseAddress,
42     SIZE_T Size,
43     TYPE_OF_MEMORY MemoryType);
44 
45 extern VOID
46 ReserveMemory(
47     PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
48     ULONG_PTR BaseAddress,
49     SIZE_T Size,
50     TYPE_OF_MEMORY MemoryType,
51     PCHAR Usage);
52 
53 extern ULONG
54 PcMemFinalizeMemoryMap(
55     PFREELDR_MEMORY_DESCRIPTOR MemoryMap);
56 
57 static
58 VOID
XboxInitializePCI(VOID)59 XboxInitializePCI(VOID)
60 {
61     PCI_TYPE1_CFG_BITS PciCfg1;
62     ULONG PciData;
63 
64     /* Select PCI to PCI bridge */
65     PciCfg1.u.bits.Enable = 1;
66     PciCfg1.u.bits.BusNumber = 0;
67     PciCfg1.u.bits.DeviceNumber = 8;
68     PciCfg1.u.bits.FunctionNumber = 0;
69     /* Select register VendorID & DeviceID */
70     PciCfg1.u.bits.RegisterNumber = 0x00;
71     PciCfg1.u.bits.Reserved = 0;
72 
73     WRITE_PORT_ULONG(PCI_TYPE1_ADDRESS_PORT, PciCfg1.u.AsULONG);
74     PciData = READ_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT);
75 
76     if (PciData == 0x01B810DE)
77     {
78         /* Select register PrimaryBus/SecondaryBus/SubordinateBus/SecondaryLatency */
79         PciCfg1.u.bits.RegisterNumber = 0x18;
80         WRITE_PORT_ULONG(PCI_TYPE1_ADDRESS_PORT, PciCfg1.u.AsULONG);
81 
82         /* Link uninitialized PCI bridge to the empty PCI bus 2,
83          * it's not supposed to have any devices attached anyway */
84         PciData = READ_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT);
85         PciData &= 0xFF0000FF;
86         PciData |= 0x00020200;
87         WRITE_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT, PciData);
88     }
89 
90     /* Select AGP to PCI bridge */
91     PciCfg1.u.bits.DeviceNumber = 30;
92     /* Select register VendorID & DeviceID */
93     PciCfg1.u.bits.RegisterNumber = 0x00;
94 
95     WRITE_PORT_ULONG(PCI_TYPE1_ADDRESS_PORT, PciCfg1.u.AsULONG);
96     PciData = READ_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT);
97 
98     if (PciData == 0x01B710DE)
99     {
100         /* Zero out uninitialized AGP Host bridge BARs */
101 
102         /* Select register BAR0 */
103         PciCfg1.u.bits.RegisterNumber = 0x10;
104         WRITE_PORT_ULONG(PCI_TYPE1_ADDRESS_PORT, PciCfg1.u.AsULONG);
105         /* Zero it out */
106         WRITE_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT, 0);
107 
108         /* Select register BAR1 */
109         PciCfg1.u.bits.RegisterNumber = 0x14;
110         WRITE_PORT_ULONG(PCI_TYPE1_ADDRESS_PORT, PciCfg1.u.AsULONG);
111         /* Zero it out */
112         WRITE_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT, 0);
113     }
114 }
115 
116 VOID
XboxMemInit(VOID)117 XboxMemInit(VOID)
118 {
119     PCI_TYPE1_CFG_BITS PciCfg1;
120     UCHAR ControlRegion[TEST_SIZE];
121     PVOID MembaseTop = (PVOID)(64 * 1024 * 1024);
122     PVOID MembaseLow = (PVOID)0;
123 
124     WRITE_REGISTER_ULONG((PULONG)(NvBase + NV2A_FB_CFG0), 0x03070103);
125     WRITE_REGISTER_ULONG((PULONG)(NvBase + NV2A_FB_CFG0 + 4), 0x11448000);
126 
127     /* Select Host to PCI bridge */
128     PciCfg1.u.bits.Enable = 1;
129     PciCfg1.u.bits.BusNumber = 0;
130     PciCfg1.u.bits.DeviceNumber = 0;
131     PciCfg1.u.bits.FunctionNumber = 0;
132     PciCfg1.u.bits.Reserved = 0;
133     /* Prepare hardware for 128 MB */
134     PciCfg1.u.bits.RegisterNumber = 0x84;
135 
136     WRITE_PORT_ULONG(PCI_TYPE1_ADDRESS_PORT, PciCfg1.u.AsULONG);
137     WRITE_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT, 0x7FFFFFF);
138 
139     InstalledMemoryMb = 64;
140     memset(ControlRegion, TEST_PATTERN1, TEST_SIZE);
141     memset(MembaseTop, TEST_PATTERN1, TEST_SIZE);
142     __wbinvd();
143 
144     if (memcmp(MembaseTop, ControlRegion, TEST_SIZE) == 0)
145     {
146         /* Looks like there is memory .. maybe a 128MB box */
147         memset(ControlRegion, TEST_PATTERN2, TEST_SIZE);
148         memset(MembaseTop, TEST_PATTERN2, TEST_SIZE);
149         __wbinvd();
150         if (memcmp(MembaseTop, ControlRegion, TEST_SIZE) == 0)
151         {
152             /* Definitely looks like there is memory */
153             if (memcmp(MembaseLow, ControlRegion, TEST_SIZE) == 0)
154             {
155                 /* Hell, we find the Test-string at 0x0 too! */
156                 InstalledMemoryMb = 64;
157             }
158             else
159             {
160                 InstalledMemoryMb = 128;
161             }
162         }
163     }
164 
165     /* Set hardware for amount of memory detected */
166     WRITE_PORT_ULONG(PCI_TYPE1_ADDRESS_PORT, PciCfg1.u.AsULONG);
167     WRITE_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT, InstalledMemoryMb * 1024 * 1024 - 1);
168 
169     AvailableMemoryMb = InstalledMemoryMb;
170 
171     XboxInitializePCI();
172 }
173 
174 memory_map_t *
XboxGetMultibootMemoryMap(INT * Count)175 XboxGetMultibootMemoryMap(INT * Count)
176 {
177     memory_map_t * MemoryMap;
178 
179     if (!MultibootInfoPtr)
180     {
181         ERR("Multiboot info structure not found!\n");
182         return NULL;
183     }
184 
185     if (!(MultibootInfoPtr->flags & MB_INFO_FLAG_MEMORY_MAP))
186     {
187         ERR("Multiboot memory map is not passed!\n");
188         return NULL;
189     }
190 
191     MemoryMap = (memory_map_t *)MultibootInfoPtr->mmap_addr;
192 
193     if (!MemoryMap ||
194         MultibootInfoPtr->mmap_length == 0 ||
195         MultibootInfoPtr->mmap_length % sizeof(memory_map_t) != 0)
196     {
197         ERR("Multiboot memory map structure is malformed!\n");
198         return NULL;
199     }
200 
201     *Count = MultibootInfoPtr->mmap_length / sizeof(memory_map_t);
202     return MemoryMap;
203 }
204 
205 TYPE_OF_MEMORY
XboxMultibootMemoryType(ULONG Type)206 XboxMultibootMemoryType(ULONG Type)
207 {
208     switch (Type)
209     {
210         case 0: // Video RAM
211             return LoaderFirmwarePermanent;
212         case 1: // Available RAM
213             return LoaderFree;
214         case 3: // ACPI area
215             return LoaderFirmwareTemporary;
216         case 4: // Hibernation area
217             return LoaderSpecialMemory;
218         case 5: // Reserved or invalid memory
219             return LoaderSpecialMemory;
220         default:
221             return LoaderFirmwarePermanent;
222     }
223 }
224 
225 FREELDR_MEMORY_DESCRIPTOR XboxMemoryMap[MAX_BIOS_DESCRIPTORS + 1];
226 
227 PFREELDR_MEMORY_DESCRIPTOR
XboxMemGetMemoryMap(ULONG * MemoryMapSize)228 XboxMemGetMemoryMap(ULONG *MemoryMapSize)
229 {
230     memory_map_t * MbMap;
231     INT Count, i;
232 
233     TRACE("XboxMemGetMemoryMap()\n");
234 
235     MbMap = XboxGetMultibootMemoryMap(&Count);
236     if (MbMap)
237     {
238         /* Obtain memory map via multiboot spec */
239 
240         for (i = 0; i < Count; i++, MbMap++)
241         {
242             TRACE("i = %d, base_addr_low = 0x%p, length_low = 0x%p\n", i, MbMap->base_addr_low, MbMap->length_low);
243 
244             if (MbMap->base_addr_high > 0 || MbMap->length_high > 0)
245             {
246                 ERR("Memory descriptor base or size is greater than 4 GB, should not happen on Xbox!\n");
247                 ASSERT(FALSE);
248             }
249 
250             SetMemory(XboxMemoryMap,
251                       MbMap->base_addr_low,
252                       MbMap->length_low,
253                       XboxMultibootMemoryType(MbMap->type));
254         }
255     }
256     else
257     {
258         /* Synthesize memory map */
259 
260         /* Available RAM block */
261         SetMemory(XboxMemoryMap,
262                   0,
263                   AvailableMemoryMb * 1024 * 1024,
264                   LoaderFree);
265 
266         if (FrameBufferSize != 0)
267         {
268             /* Video memory */
269             ReserveMemory(XboxMemoryMap,
270                           (ULONG_PTR)FrameBuffer,
271                           FrameBufferSize,
272                           LoaderFirmwarePermanent,
273                           "Video memory");
274         }
275     }
276 
277     *MemoryMapSize = PcMemFinalizeMemoryMap(XboxMemoryMap);
278     return XboxMemoryMap;
279 }
280 
281 /* EOF */
282