SetMemMapEntries issue with doubly mapped pages
Jon Abbott (1421) 2651 posts |
When remapping a doubly mapped page, its reported as being -&400 from the address requested. To repro (on a Pi): The code below should remap page 0 to &1FD8000, but its reported as being at &1FD7C00 DIM Z% 16 |
Jeffrey Lee (213) 6048 posts |
Technically it’s correct that the address you read back isn’t &1FD8000. However, there is still some dodgy code in the kernel (I’ll come to that later). Just in case you’re unfamiliar with its behaviour, the “doubly mapped” dynamic area flag causes the OS to automatically map in two copies of the memory: One mapping from da_base to da_base+da_size, and a second mapping from da_base-da_size to da_base. When dealing with doubly mapped regions, for some reason Acorn decided that the CAM map (which maps physical pages to their logical addresses) would, for each doubly mapped page, contain the address of the lowest mapping (i.e. the one at addr-da_size). So when you use OS_SetMemMapEntries to doubly-map in page 0 at &1FD8000, it maps it in at both &1FD8000 and &1FD7C00, and stores &1FD7C00 in the CAM map. Then when OS_ReadMemMapEntries is used to find the address of page 0, it returns the address held in the CAM map – &1FD7C00. Now the real problem you’re facing, in case you haven’t worked it out yet, is that because the ‘doubly mapped’ flag is designed to be used with dynamic areas, there’s no way of indicating to OS_SetMemMapEntries what offset you want the 2nd mapping to be mapped in at (internally the DA remapping code passes down the offset to the page table update functions whenever the DA is being resized, etc.) And, looking at the kernel sources, I can see that R9 (which should contain the offset to the 2nd page on entry to BangCamUpdate) is actually being initialised with the value of the MEMC control register softcopy – which I guess is &400, which isn’t a very good offset to use considering it needs to be a multiple of 4K. Perhaps OS_SetMemMapEntries should be updated to allow the offset to be specified when doubly mapped regions are being moved? Whatever you do, you’ll also need to be aware of the caveat that if you’re moving a doubly mapped page from one doubly mapped region to another (something that BangCamUpdate will currently never be asked to do by the OS), both regions must have the same 2nd mapping offset (BangCamUpdate only allows one offset to be specified, so to avoid the wrong pages being moved the same offset would have to be used in each region). |
Jeffrey Lee (213) 6048 posts |
Although whether we fixed OS_SetMemMapEntries or not, you’d still be (a) stealing pages from a dynamic area without its consent, and (b) messing with the memory mapping of the application space (unless you’ve worked out how to apply a 28MB clamp to the application space?) |
Jon Abbott (1421) 2651 posts |
I should probably have stated a few further points: 1. This behaviour doesn’t happen on RO3.7 – the page map subsequently returns the correct address. I wouldn’t put this as “stealing pages”, as they’re still associated with the DA and are remapped back as required via a virtual memory manager – it’s relocating pages so that legacy games will run on the Pi. At the point I’m doing it, the game has taken over the machine and there’s nothing in the application space as the game wouldn’t be using or expect that much memory. Having said all that, I agree with you and would advice against doing this. |
Jeffrey Lee (213) 6048 posts |
Probably because there wasn’t any reason to stop it from being doubly mapped. Plus at the time the DA is created, the video driver won’t be active yet, so the kernel won’t know whether it supports VIDC style hardware scrolling/address space wraparound yet.
As mentioned above, the kernel is erroneously using the softcopy of the MEMC control register as the offset value. I’m not sure offhand why you get different results on the RiscPC – perhaps that code has changed between 3.7 and now. |
Jon Abbott (1421) 2651 posts |
As mentioned above, the kernel is erroneously using the softcopy of the MEMC control register as the offset value. Sorry, you said that was a guess! Regards the screen DA (on the Pi, can’t vouch for anything else). At boot, it’s zero length, so I have to expand it, so I’m still non-the-wiser why it’s being doubly mapped. Is OS_ChangeDynamicArea hardcoded to doubly map the screen DA? Anyhow, I have a workaround, so I’ll leave it up to you if its worth looking at or not. Its only an issue if you’re using the screen DA, so unlikely to affect anything. It’s probably worth just noting it in the DA related documentation and ignoring. |
Jeffrey Lee (213) 6048 posts |
The double mapping flag is specified when the DA is created, which happens early on in the kernel initialisation (at the same time as the RMA is created and the other builtin DAs)
Yeah, the wiki docs on the page access flags used by OS_SetMemMapEntries and friends are a bit vague. I’ll see if I can find the time to update them. |
Jon Abbott (1421) 2651 posts |
FYI OS_SetMemMapEntries doesn’t support doubly mapped pages on RO3.7, hence why the problem doesn’t occur. It also doesn’t occur on RO4.02, so it must be a later addition. |
Jeffrey Lee (213) 6048 posts |
Here’s a start: I’ve now merged the dynamic area page flags page with the OS_SetMemMapEntries page flags page to produce this. However I still need to go through and flag up which flags are supported where, and will probably add a separate page to describe the function of doubly mapped areas (with suitable disclaimer that they’re for OS use only. I’m not even sure if the kernel allocates logical address space for them properly when you create a DA!) |
Jeffrey Lee (213) 6048 posts |
Yes, it looks like the original version of the call only paid attention to the lower two bits of the flags (i.e. the access privileges – although it looks like that’s been retrofitted to be 4 bits instead of 2?), but then during the development of the Tungsten port that code was removed and it now passes the full unmodified set of flags through. Unfortunately the checkin comment is rather vague (‘Lots of Tungsten work’) so there’s no way of knowing if that was an accidental change or not. It looks like a similar change was also made to OS_ReadMemMapEntries – previously it would have just returned the access privileges, but now, perhaps unintentionally, it returns the full set of flags. It’s a bit hard to tell just from CVS, but it looks like OS_ReadMemMapEntries was changed at the time of RISC OS 3.5, as a side effect of the CAM map format being changed. In fact, prior to RISC OS 3.5, the access level was the only thing that you could specify when mapping in memory, so perhaps it was always Acorn’s intention that OS_ReadMemMapEntries/OS_SetMemMapEntries would deal with the full range of flags – it’s just that (a) OS_SetMemMapEntries was incorrectly throwing away flags prior to RISC OS 5, and (b) OS_SetMemMapEntries under RISC OS 5 doesn’t mask out flags which it can’t deal with properly (reserved flags, internal flags, the doubly-mapped flag, etc.) |
Jon Abbott (1421) 2651 posts |
So I need to mask off more than just the doubly mapped flag, which bits can RO5 handle correctly? The problem I see with OS_SetMemMapEntries not masking off the bits it can’t deal with, is that any code that calls OS_ReadMemMapEntries and then passes the returned values to OS_SetMemMapEntries, is almost certainly going to fail in some cases. |